shithub: lu9-p9

ref: cc43a4edaba3e332a975ed4df82cf5e919cd0c39
dir: /fs.c/

View raw version
/*
 * The File object

 * p9.file(fd) takes an open file descriptor and returns a
 * File object f which provides a convenient method interface
 * to the usual file operations.
 * p9.open and p9.create take a file name to open or create.

 * The file descriptor stored in f.fd is garbage collected,
 * that is, it will be automatically closed once the File
 * object becomes unreachable. Note how this means that f.fd
 * should be used sparringly and with much care. In particular
 * you shouldn't store it outside of f, since the actual file
 * descriptor number might become invalid (closed) or refer
 * to a completely different file after f is collected.
 *
 * Store the File object in some global place to prevent it
 * from being collected.
 */
 
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 filenew(lua_State*, int);
static int fileclose(lua_State*);
static int filefd(lua_State*, int);

static int
filenew(lua_State *L, int fd)
{
	int f;

	lua_createtable(L, 0, 4);
	f = lua_gettop(L);
	lua_pushinteger(L, fd);
		lua_setfield(L, f, "fd");
	luaL_getmetatable(L, "p9-File");
		lua_setfield(L, f, "__index");
	lua_pushcfunction(L, fileclose);
		lua_setfield(L, f, "__close");
	lua_pushcfunction(L, fileclose);
		lua_setfield(L, f, "__gc");
	lua_pushvalue(L, f);
		lua_setmetatable(L, f);
	return 1;
}

static int
fileclose(lua_State *L)
{
	int fd;
	
	fd = filefd(L, 1);
	if(fd == -1)
		return 0;
	lua_pushinteger(L, -1);
		lua_setfield(L, 1, "fd");
	close(fd);
	return 0;
}

static int
filefd(lua_State *L, int idx)
{
	int fd;
	
	if(lua_getfield(L, idx, "fd") != LUA_TNUMBER)
		return luaL_error(L, "fd must be integer");
	fd = lua_tonumber(L, -1);
	lua_pop(L, 1);
	return fd;
}

static int
p9_file(lua_State *L)
{
	int fd;
	
	fd = luaL_checkinteger(L, 1);
	return filenew(L, fd);
}

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_file_close(lua_State *L)
{
	if(close(filefd(L, 1)) == -1)
		return error(L, "close: %r");
	return 0;
}

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)
{
	int fd, type;
	vlong n, off;
	
	fd = filefd(L, 1);
	n = luaL_checkinteger(L, 2);
	type = seekmode(L, luaL_optstring(L, 3, "set"));
	if((off = seek(fd, n, type)) == -1)
		return error(L, "seek: %r");
	lua_pushinteger(L, off);
	return 1;
}

static int
p9_file_read(lua_State *L)
{
	int fd;
	long n, nbytes;
	vlong offset;
	char *buf;
	
	fd = filefd(L, 1);
	nbytes = luaL_optinteger(L, 2, Iosize);
	offset = luaL_optinteger(L, 3, -1);
	buf = getbuffer(L, nbytes);
	if(offset == -1)
		n = read(fd, buf, nbytes);
	else
		n = pread(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)
{
	int fd;
	long nbytes;
	
	fd = filefd(L, 1);
	nbytes = luaL_optinteger(L, 2, -1);
	slurp(L, fd, nbytes);
	return 1;
}

static int
p9_file_write(lua_State *L)
{
	lua_Integer fd, offset;
	size_t nbytes;
	const char *buf;
	long n;

	fd = filefd(L, 1);
	buf = luaL_checklstring(L, 2, &nbytes);
	nbytes = luaL_optinteger(L, 3, nbytes);
	offset = luaL_optinteger(L, 4, -1);
	if(offset == -1)
		n = write(fd, buf, nbytes);
	else
		n = pwrite(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)
{
	int fd;
	char *buf;
	
	fd = filefd(L, 1);
	buf = getbuffer(L, Iosize);
	if(fd2path(fd, buf, Iosize) != 0)
		return error(L, "fd2path: %r");
	lua_pushstring(L, buf);
	return 1;
}

static int
p9_file_iounit(lua_State *L)
{
	int fd;
	
	fd = filefd(L, 1);
	lua_pushinteger(L, iounit(fd));
	return 1;
}

static int
p9_file_dup(lua_State *L)
{
	int fd, new, na;
	
	na = lua_gettop(L);
	fd = filefd(L, 1);
	if(na == 2)
		new = filefd(L, 2);
	else
		new = -1;
	if((new = dup(fd, new)) == -1)
		return error(L, "dup: %r");
	if(na == 2){
		lua_pushinteger(L, new);
		lua_setfield(L, 2, "fd");
		return 1;
	}
	return filenew(L, new);
}

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
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
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;
}