ref: 9ddcbf1be2343ebb4d7853fecf8528e862def846
dir: /base/fs.c/
/*
* The File object
* p9.open and p9.create create a file object representing an
* open file descriptor. This object's methods are then used
* for performing I/O and other operations.
*
* The file object holds a reference to a system resource, the
* open file descriptor, which you likely want to release as
* soon as it's not needed anymore.
* To help with that the file implements both __gc and __close
* metamethods, which will close it for you as soon as the file
* falls out of reach.
* file:close() will close the file descriptor immediately.
* file:keep() will prevent automatic closing of the file
* descriptor, which may be needed in some situations.
*
* During module load the standard file descriptors 0 through 2
* are wrapped in File objects and get stashed in p9.fd table:
* p9.fd[0] thus refers to the standard input, and so on.
* Being reachable through the module these aren't subject to
* garbage collection -- at least until Lua state is closed.
* You may use this table for stashing files for similar reasons.
* TODO: use this table to implement access to arbitrary
* already-open file descriptors.
*/
typedef struct File File;
struct File {
int fd;
int keep;
};
static int
filenew(lua_State *L, int fd)
{
File *f;
f = lua_newuserdatauv(L, sizeof(File), 0);
f->fd = fd;
f->keep = 0;
luaL_setmetatable(L, "p9-File");
return 1;
}
static int
fileclose(lua_State *L)
{
File *f;
f = luaL_checkudata(L, 1, "p9-File");
if(f->keep || f->fd == -1)
return 0;
if(close(f->fd) == -1)
return error(L, "close: %r");
f->fd = -1;
return 0;
}
static int
openmode(lua_State *L, char *s)
{
int i, n, mode;
char r, w, x;
char buf[64], *f[10], *p;
snprint(buf, sizeof buf, "%s", s);
mode = r = w = x = 0;
n = getfields(buf, f, sizeof f, 1, " \t\n");
if(n < 1)
return OREAD;
for(i = 0; p = f[i], i < n; i++){
if(strcmp(p, "r") == 0 || strcmp(p, "read") == 0)
r = 1;
else if(strcmp(p, "w") == 0 || strcmp(p, "write") == 0)
w = 1;
else if(strcmp(p, "rw") == 0 || strcmp(p, "rdwr") == 0)
r = w = 1;
else if(strcmp(p, "x") == 0 || strcmp(p, "exec") == 0)
x = 1;
else if(strcmp(p, "trunc") == 0)
mode |= OTRUNC;
else if(strcmp(p, "cexec") == 0)
mode |= OCEXEC;
else if(strcmp(p, "rclose") == 0)
mode |= ORCLOSE;
else if(strcmp(p, "excl") == 0)
mode |= OEXCL;
else
return luaL_error(L, "unknown mode flag '%s'", p);
}
if(x) mode |= OEXEC;
else if(r && w) mode |= ORDWR;
else if(r) mode |= OREAD;
else if(w) mode |= OWRITE;
return mode;
}
static ulong
createperm(lua_State *L, char *s)
{
int i, n;
ulong perm;
char buf[64], *f[10], *p;
snprint(buf, sizeof buf, "%s", s);
perm = 0;
n = getfields(buf, f, sizeof f, 1, " \t\n");
if(n < 1)
return 0644;
for(i = 0; p = f[i], i < n; i++){
if(isdigit(p[0]))
perm |= strtol(p, nil, 8);
else if(strcmp(p, "dir") == 0)
perm |= DMDIR;
else if(strcmp(p, "tmp") == 0)
perm |= DMTMP;
else if(strcmp(p, "excl") == 0)
perm |= DMEXCL;
else if(strcmp(p, "append") == 0)
perm |= DMAPPEND;
else
return luaL_error(L, "unknown permission flag '%s'", p);
}
return perm;
}
static int
p9_open(lua_State *L)
{
const char *file;
int mode;
int fd;
file = luaL_checkstring(L, 1);
mode = openmode(L, luaL_optstring(L, 2, "read"));
if((fd = open(file, mode)) == -1)
return error(L, "open: %r");
return filenew(L, fd);
}
static int
p9_create(lua_State *L)
{
const char *file;
int fd, mode;
ulong perm;
file = luaL_checkstring(L, 1);
mode = openmode(L, luaL_optstring(L, 2, "rdwr"));
perm = createperm(L, luaL_optstring(L, 3, "644"));
if((fd = create(file, mode, perm)) == -1)
return error(L, "create: %r");
return filenew(L, fd);
}
static int
p9_pipe(lua_State *L)
{
int fd[2];
if(pipe(fd) == -1)
return error(L, "pipe: %r");
filenew(L, fd[0]);
filenew(L, fd[1]);
return 2;
}
static int
p9_file_close(lua_State *L)
{
File *f;
f = luaL_checkudata(L, 1, "p9-File");
if(close(f->fd) == -1)
return error(L, "close: %r");
f->fd = -1;
return 0;
}
static int
p9_file_keep(lua_State *L)
{
File *f;
f = luaL_checkudata(L, 1, "p9-File");
f->keep = 1;
return 1;
}
static int
seekmode(lua_State *L, char *s)
{
if(strcmp(s, "set") == 0)
return 0;
if(strcmp(s, "cur") == 0)
return 1;
if(strcmp(s, "end") == 0)
return 2;
return luaL_error(L, "unknown seek mode '%s'", s);
}
static int
p9_file_seek(lua_State *L)
{
File *f;
int type;
vlong n, off;
f = luaL_checkudata(L, 1, "p9-File");
n = luaL_checkinteger(L, 2);
type = seekmode(L, luaL_optstring(L, 3, "set"));
if((off = seek(f->fd, n, type)) == -1)
return error(L, "seek: %r");
lua_pushinteger(L, off);
return 1;
}
static int
p9_file_read(lua_State *L)
{
File *f;
long n, nbytes;
vlong offset;
char *buf;
f = luaL_checkudata(L, 1, "p9-File");
nbytes = luaL_optinteger(L, 2, Iosize);
offset = luaL_optinteger(L, 3, -1);
buf = getbuffer(L, nbytes);
if(offset == -1)
n = read(f->fd, buf, nbytes);
else
n = pread(f->fd, buf, nbytes, offset);
if(n == -1)
return error(L, "read: %r");
lua_pushlstring(L, buf, n);
return 1;
}
static int
slurp(lua_State *L, int fd, long nbytes)
{
int all;
long n, nr, tot;
char *buf;
luaL_Buffer b;
all = (nbytes == -1) ? 1 : 0;
luaL_buffinit(L, &b);
for(tot = 0; all || tot < nbytes; tot += nr){
n = all ? Iosize : min(Iosize, nbytes - tot);
buf = luaL_prepbuffsize(&b, n);
if((nr = read(fd, buf, n)) == -1)
return error(L, "read: %r");
if(nr == 0)
break;
luaL_addsize(&b, nr);
}
luaL_pushresult(&b);
return 1;
}
static int
p9_file_slurp(lua_State *L)
{
File *f;
long nbytes;
f = luaL_checkudata(L, 1, "p9-File");
nbytes = luaL_optinteger(L, 2, -1);
slurp(L, f->fd, nbytes);
return 1;
}
static int
p9_file_write(lua_State *L)
{
File *f;
lua_Integer offset;
size_t nbytes;
const char *buf;
long n;
f = luaL_checkudata(L, 1, "p9-File");
buf = luaL_checklstring(L, 2, &nbytes);
nbytes = luaL_optinteger(L, 3, nbytes);
offset = luaL_optinteger(L, 4, -1);
if(offset == -1)
n = write(f->fd, buf, nbytes);
else
n = pwrite(f->fd, buf, nbytes, offset);
if(n != nbytes)
return error(L, "write: %r");
lua_pushinteger(L, n);
return 1;
}
static int
p9_file_path(lua_State *L)
{
File *f;
char *buf;
f = luaL_checkudata(L, 1, "p9-File");
buf = getbuffer(L, Iosize);
if(fd2path(f->fd, buf, Iosize) != 0)
return error(L, "fd2path: %r");
lua_pushstring(L, buf);
return 1;
}
static int
p9_file_iounit(lua_State *L)
{
File *f;
f = luaL_checkudata(L, 1, "p9-File");
lua_pushinteger(L, iounit(f->fd));
return 1;
}
static int
p9_file_dup(lua_State *L)
{
int fd;
File *f;
f = luaL_checkudata(L, 1, "p9-File");
if((fd = dup(f->fd, -1)) == -1)
return error(L, "dup: %r");
return filenew(L, fd);
}
static int
p9_file_set(lua_State *L)
{
int fd;
File *this, *that;
this = luaL_checkudata(L, 1, "p9-File");
that = luaL_checkudata(L, 2, "p9-File");
if((fd = dup(that->fd, this->fd)) == -1)
return error(L, "dup: %r");
this->fd = fd;
lua_pushvalue(L, 1);
return 1;
}
static luaL_Reg p9_file_proto[] = {
{"close", p9_file_close},
{"keep", p9_file_keep},
{"read", p9_file_read},
{"slurp", p9_file_slurp},
{"write", p9_file_write},
{"seek", p9_file_seek},
{"iounit", p9_file_iounit},
{"path", p9_file_path},
{"dup", p9_file_dup},
{"set", p9_file_set},
{"__gc", fileclose},
{"__close", fileclose},
{"__index", nil}, /* placeholder for ourselves */
{nil, nil},
};
/*
* Assorted functions that don't work with or create Files
*/
static int
p9_remove(lua_State *L)
{
const char *file;
file = luaL_checkstring(L, 1);
if(remove(file) == -1)
return error(L, "remove: %r");
lua_pushboolean(L, 1);
return 1;
}
static int
accessmode(lua_State *L, const char *s)
{
int i, n, mode;
char buf[64], *f[10], *p;
snprint(buf, sizeof buf, "%s", s);
n = getfields(buf, f, sizeof f, 1, " \t\n");
mode = 0;
for(i = 0; p = f[i], i < n; i++){
if(strcmp(p, "exist") == 0 || strcmp(p, "exists") == 0)
mode |= AEXIST;
else if(strcmp(p, "r") == 0 || strcmp(p, "read") == 0)
mode |= AREAD;
else if(strcmp(p, "w") == 0 || strcmp(p, "write") == 0)
mode |= AWRITE;
else if(strcmp(p, "rw") == 0 || strcmp(p, "rdwr") == 0)
mode |= AREAD|AWRITE;
else if(strcmp(p, "x") == 0 || strcmp(p, "exec") == 0)
mode |= AEXEC;
else
return luaL_error(L, "unknown access flag '%s'", p);
}
return mode;
}
static int
p9_access(lua_State *L)
{
const char *path;
int mode;
path = luaL_checkstring(L, 1);
mode = accessmode(L, luaL_optstring(L, 2, "exists"));
lua_pushboolean(L,
access(path, mode) == 0 ? 1 : 0
);
return 1;
}