ref: 5168236aa96a83b623a142c0d0e365c7ff0d96e6
parent: 970e4fcbaacb7511d45eaa2890501a0cc1edb45c
author: kvik <kvik@a-b.xyz>
date: Thu Apr 22 18:23:16 EDT 2021
all: introduce nicer project structure This should allow easier maintenance and cherry linking of modules planned in the future.
--- /dev/null
+++ b/base/base.c
@@ -1,0 +1,162 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "../base/common.c"
+
+#include "fs.c"
+#include "walk.c"
+#include "env.c"
+#include "ns.c"
+#include "proc.c"
+#include "misc.c"
+
+typedef struct Data {
+ char *key;
+ lua_Integer val;
+} Data;
+
+static Data p9data[] = {
+ {"OREAD", OREAD},
+ {"OWRITE", OWRITE},
+ {"ORDWR", ORDWR},
+ {"OEXEC", OEXEC},
+ {"OTRUNC", OTRUNC},
+ {"OCEXEC", OCEXEC},
+ {"ORCLOSE", ORCLOSE},
+ {"OEXCL", OEXCL},
+
+ {"DMDIR", DMDIR},
+ {"DMAPPEND", DMAPPEND},
+ {"DMEXCL", DMEXCL},
+ {"DMMOUNT", DMMOUNT},
+ {"DMAUTH", DMAUTH},
+ {"DMTMP", DMTMP},
+ {"DMREAD", DMREAD},
+ {"DMWRITE", DMWRITE},
+ {"DMEXEC", DMEXEC},
+ {"QTDIR", QTDIR},
+ {"QTAPPEND", QTAPPEND},
+ {"QTEXCL", QTEXCL},
+ {"QTMOUNT", QTMOUNT},
+ {"QTAUTH", QTAUTH},
+ {"QTTMP", QTTMP},
+ {"QTFILE", QTFILE},
+
+ {"MREPL", MREPL},
+ {"MBEFORE", MBEFORE},
+ {"MAFTER", MAFTER},
+ {"MCREATE", MCREATE},
+ {"MCACHE", MCACHE},
+
+ {"RFPROC", RFPROC},
+ {"RFNOWAIT", RFNOWAIT},
+ {"RFNAMEG", RFNAMEG},
+ {"RFCNAMEG", RFCNAMEG},
+ {"RFNOMNT", RFNOMNT},
+ {"RFENVG", RFENVG},
+ {"RFCENVG", RFCENVG},
+ {"RFNOTEG", RFNOTEG},
+ {"RFFDG", RFFDG},
+ {"RFCFDG", RFCFDG},
+ {"RFREND", RFREND},
+ {"RFMEM", RFMEM},
+
+ {nil, 0}
+};
+
+static luaL_Reg p9func[] = {
+ {"open", p9_open},
+ {"create", p9_create},
+ {"file", p9_file},
+ {"pipe", p9_pipe},
+ {"remove", p9_remove},
+ {"access", p9_access},
+
+ {"stat", p9_stat},
+ {"wstat", p9_wstat},
+ {"walk", p9_walk},
+
+ {"bind", p9_bind},
+ {"mount", p9_mount},
+ {"unmount", p9_unmount},
+
+ {"getenv", p9_getenv},
+ {"setenv", p9_setenv},
+
+ {"abort", p9_abort},
+ {"exits", p9_exits},
+ {"fatal", p9_fatal},
+ {"sleep", p9_sleep},
+ {"alarm", p9_alarm},
+ {"rfork", p9_rfork},
+ {"wait", p9_wait},
+ {"exec", p9_exec},
+ {"wdir", p9_wdir},
+ {"pid", p9_pid},
+ {"ppid", p9_ppid},
+ {"user", p9_user},
+ {"sysname", p9_sysname},
+
+ {"cleanname", p9_cleanname},
+
+ {nil, nil}
+};
+
+int
+luaopen_p9(lua_State *L)
+{
+ int lib;
+ Buf *buf;
+ Data *d;
+
+ buf = resizebuffer(L, nil, Iosize);
+ lua_pushlightuserdata(L, buf);
+ lua_setfield(L, LUA_REGISTRYINDEX, "p9-buffer");
+
+ static luaL_Reg filemt[] = {
+ {"close", p9_file_close},
+ {"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},
+ {nil, nil},
+ };
+ luaL_newmetatable(L, "p9-File");
+ luaL_setfuncs(L, filemt, 0);
+ lua_pop(L, 1);
+
+ static luaL_Reg walkmt[] = {
+ {"__close", p9_walkclose},
+ {nil, nil},
+ };
+ luaL_newmetatable(L, "p9-Walk");
+ luaL_setfuncs(L, walkmt, 0);
+ lua_pop(L, 1);
+
+ luaL_newlib(L, p9func);
+ lib = lua_gettop(L);
+ for(d = p9data; d->key != nil; d++){
+ lua_pushinteger(L, d->val);
+ lua_setfield(L, -2, d->key);
+ }
+
+ static luaL_Reg envmt[] = {
+ {"__index", p9_getenv_index},
+ {"__newindex", p9_setenv_newindex},
+ {nil, nil},
+ };
+ lua_createtable(L, 0, 2);
+ luaL_setfuncs(L, envmt, 0);
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, -2);
+ lua_setfield(L, lib, "env");
+
+ return 1;
+}
--- /dev/null
+++ b/base/common.c
@@ -1,0 +1,86 @@
+enum {
+ Iosize = 8192,
+ Smallbuf = 512,
+};
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+static int
+error(lua_State *L, char *fmt, ...)
+{
+ va_list varg;
+ int n;
+ char *buf;
+ luaL_Buffer b;
+
+ lua_pushnil(L);
+ buf = luaL_buffinitsize(L, &b, Smallbuf);
+ va_start(varg, fmt);
+ n = vsnprint(buf, Smallbuf, fmt, varg);
+ va_end(varg);
+ luaL_pushresultsize(&b, n);
+ return 2;
+}
+
+/* Memory allocator associated with Lua state */
+static void*
+lalloc(lua_State *L, void *ptr, usize sz)
+{
+ void *ud;
+
+ if((ptr = (lua_getallocf(L, &ud))(ud, ptr, LUA_TUSERDATA, sz)) == nil){
+ lua_pushliteral(L, "out of memory");
+ lua_error(L);
+ }
+ memset(ptr, 0, sz);
+ setmalloctag(ptr, getcallerpc(&L));
+ return ptr;
+}
+
+/*
+ * Various functions in this library require a
+ * variably sized buffer for their operation.
+ * Rather than allocating one for each call
+ * we preallocate a shared buffer of reasonable
+ * size and grow it as needed.
+ * The buffer gets associated with a Lua state
+ * at library load time.
+ * getbuffer(L, sz) returns a pointer to the
+ * memory area of at least sz bytes.
+ *
+ * To avoid stepping on each other's toes the
+ * buffer use must be constrained to a single
+ * call.
+ */
+
+typedef struct Buf Buf;
+
+struct Buf {
+ usize sz;
+ char *b;
+};
+
+static Buf*
+resizebuffer(lua_State *L, Buf *buf, usize sz)
+{
+ if(buf == nil){
+ buf = lalloc(L, nil, sizeof(Buf));
+ buf->b = nil;
+ buf->sz = 0;
+ }
+ if(buf->sz < sz){
+ buf->b = lalloc(L, buf->b, sz);
+ buf->sz = sz;
+ }
+ return buf;
+}
+
+static char*
+getbuffer(lua_State *L, usize sz)
+{
+ Buf *buf;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, "p9-buffer");
+ buf = lua_touserdata(L, -1);
+ return resizebuffer(L, buf, sz)->b;
+}
--- /dev/null
+++ b/base/env.c
@@ -1,0 +1,133 @@
+/* Environment variables
+ *
+ * p9.env object provides a map between the /env device and Lua,
+ * with its dynamic fields representing the environment variables.
+ * Assigning a value to the field writes to the environment:
+ *
+ * p9.env.var = "value"
+ *
+ * while reading a value reads from the environment:
+ *
+ * assert(p9.env.var == "value")
+ *
+ * A value can be a string or a list.
+ * A list is encoded (decoded) to (from) the environment as a
+ * list of strings according to the encoding used by the rc(1)
+ * shell (0-byte separated fields).
+ *
+ * lua> p9.env.list = {"a", "b", "c"}
+ * rc> echo $#list * $list
+ * 3 * a b c
+ *
+ * p9.getenv(name) and p9.setenv(name, val) provide the more
+ * usual API.
+ */
+
+static int
+p9_getenv(lua_State *L)
+{
+ int fd;
+ long len, elems, i;
+ char env[Smallbuf];
+ const char *buf, *s, *e;
+
+ snprint(env, sizeof env, "/env/%s", luaL_checkstring(L, 1));
+ if((fd = open(env, OREAD)) == -1){
+ lua_pushnil(L);
+ return 1;
+ }
+ slurp(L, fd, -1);
+ close(fd);
+ len = luaL_len(L, -1);
+ buf = lua_tostring(L, -1);
+ /* Empty */
+ if(len == 0){
+ lua_pushnil(L);
+ return 1;
+ }
+ /* String (not a list) */
+ if(buf[len-1] != '\0')
+ return 1;
+ /* List */
+ for(elems = i = 0; i < len; i++)
+ if(buf[i] == '\0') elems++;
+ s = buf;
+ e = buf + len;
+ lua_createtable(L, elems, 0);
+ for(i = 1; s < e; i++){
+ lua_pushstring(L, s);
+ lua_rawseti(L, -2, i);
+ s = memchr(s, '\0', e - s);
+ s++;
+ }
+ return 1;
+}
+
+static int
+p9_getenv_index(lua_State *L)
+{
+ lua_remove(L, 1);
+ return p9_getenv(L);
+}
+
+static int
+p9_setenv(lua_State *L)
+{
+ int fd, ntab, n, t, i;
+ char env[Smallbuf];
+ luaL_Buffer b;
+
+ snprint(env, sizeof env, "/env/%s", luaL_checkstring(L, 1));
+ t = lua_type(L, 2);
+ if(t != LUA_TNIL && t != LUA_TSTRING && t != LUA_TTABLE)
+ return luaL_argerror(L, 2, "nil, string, or table expected");
+ if((fd = create(env, OWRITE, 0644)) == -1)
+ return error(L, "open: %r");
+ /*
+ * Writes below are not fully checked for
+ * error (write(n) != n), because env(3)
+ * may truncate the value at its dumb 16k
+ * limit. Encoding a knowledge of this limit
+ * sucks, as does env(3).
+ */
+ if(t == LUA_TNIL){
+ close(fd);
+ return 0;
+ }else if(t == LUA_TSTRING){
+ n = luaL_len(L, 2);
+ if(write(fd, lua_tostring(L, 2), n) == -1){
+ close(fd);
+ return error(L, "write: %r");
+ }
+ }else{
+ ntab = luaL_len(L, 2);
+ luaL_buffinit(L, &b);
+ for(i = 1; i <= ntab; i++){
+ t = lua_geti(L, 2, i);
+ if(t != LUA_TSTRING){
+ if(luaL_callmeta(L, -1, "__tostring")
+ && lua_type(L, -1) == LUA_TSTRING){
+ lua_replace(L, -2);
+ }else{
+ lua_pop(L, 1);
+ continue;
+ }
+ }
+ luaL_addvalue(&b);
+ luaL_addchar(&b, '\0');
+ }
+ if(write(fd, luaL_buffaddr(&b), luaL_bufflen(&b)) == -1){
+ close(fd);
+ return error(L, "write: %r");
+ }
+ }
+ close(fd);
+ return 0;
+}
+
+static int
+p9_setenv_newindex(lua_State *L)
+{
+ lua_remove(L, 1);
+ return p9_setenv(L);
+}
--- /dev/null
+++ b/base/fs.c
@@ -1,0 +1,398 @@
+/*
+ * 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;
+}
--- /dev/null
+++ b/base/misc.c
@@ -1,0 +1,17 @@
+/* Nowhere in particular */
+
+static int
+p9_cleanname(lua_State *L)
+{
+ const char *path, *p;
+ lua_Unsigned len;
+ luaL_Buffer b;
+
+ path = luaL_checklstring(L, 1, &len);
+ luaL_buffinit(L, &b);
+ luaL_addlstring(&b, path, len);
+ luaL_addchar(&b, '\0');
+ p = cleanname(luaL_buffaddr(&b));
+ lua_pushstring(L, p);
+ return 1;
+}
--- /dev/null
+++ b/base/ns.c
@@ -1,0 +1,88 @@
+static int
+parsemntflags(lua_State *L, char *s)
+{
+ int flags, n;
+ char *f[4], buf[128];
+
+ flags = MREPL;
+ n = getfields(s, f, sizeof f, 1, " \t\r\n");
+ if(n > 0) for(int i = 0; i < n; i++){
+ if (strcmp(f[i], "after") == 0)
+ flags |= MAFTER;
+ else if(strcmp(f[i], "before") == 0)
+ flags |= MBEFORE;
+ else if(strcmp(f[i], "create") == 0)
+ flags |= MCREATE;
+ else if(strcmp(f[i], "cache") == 0)
+ flags |= MCACHE;
+ else{
+ snprint(buf, sizeof buf, "skipping unknown mount flag '%s'", f[i]);
+ lua_warning(L, buf, 0);
+ }
+ }
+ return flags;
+}
+
+static int
+p9_bind(lua_State *L)
+{
+ const char *this, *over;
+ int flags, r;
+
+ this = luaL_checkstring(L, 1);
+ over = luaL_checkstring(L, 2);
+ flags = parsemntflags(L, luaL_optstring(L, 3, ""));
+ if((r = bind(this, over, flags)) == -1)
+ return error(L, "bind: %r");
+ lua_pushinteger(L, r);
+ return 1;
+}
+
+static int
+p9_mount(lua_State *L)
+{
+ const char *over, *aname;
+ int fd, afd, closefd, flags, r;
+
+ closefd = -1;
+ switch(lua_type(L, 1)){
+ default:
+ return luaL_typeerror(L, 1, "file descriptor or path");
+ case LUA_TNUMBER:
+ fd = lua_tointeger(L, 1); break;
+ case LUA_TSTRING:
+ if((fd = open(lua_tostring(L, 1), ORDWR)) == -1)
+ return error(L, "open: %r");
+ closefd = fd;
+ break;
+ }
+ over = luaL_checkstring(L, 2);
+ flags = parsemntflags(L, luaL_optstring(L, 3, ""));
+ aname = luaL_optstring(L, 4, "");
+ afd = luaL_optinteger(L, 5, -1);
+ if((r = mount(fd, afd, over, flags, aname)) == -1){
+ close(closefd);
+ return error(L, "mount: %r");
+ }
+ close(closefd);
+ lua_pushinteger(L, r);
+ return 1;
+}
+
+static int
+p9_unmount(lua_State *L)
+{
+ const char *name, *over;
+ int r;
+
+ name = luaL_checkstring(L, 1);
+ over = luaL_optstring(L, 2, nil);
+ if(over == nil){
+ over = name;
+ name = nil;
+ }
+ if((r = unmount(name, over)) == -1)
+ return error(L, "unmount: %r");
+ lua_pushinteger(L, r);
+ return 1;
+}
--- /dev/null
+++ b/base/proc.c
@@ -1,0 +1,193 @@
+static int
+p9_abort(lua_State*)
+{
+ abort();
+ /* never */ return 0;
+}
+
+static int
+p9_exits(lua_State *L)
+{
+ exits(luaL_optstring(L, 1, nil));
+ /* never */ return 0;
+}
+
+static int
+p9_fatal(lua_State *L)
+{
+ if(lua_gettop(L) > 1
+ && lua_getglobal(L, "string") == LUA_TTABLE
+ && lua_getfield(L, -1, "format") == LUA_TFUNCTION){
+ lua_remove(L, -2);
+ lua_insert(L, 1);
+ if(lua_pcall(L, lua_gettop(L) - 1, 1, 0) == LUA_OK)
+ sysfatal(lua_tostring(L, -1));
+ }
+ sysfatal(luaL_optstring(L, 1, "fatal"));
+ /* never */ return 0;
+}
+
+static int
+p9_sleep(lua_State *L)
+{
+ long t;
+
+ t = luaL_checkinteger(L, 1);
+ lua_pushboolean(L,
+ sleep(t) == -1 ? 0 : 1
+ );
+ return 1;
+}
+
+static int
+p9_alarm(lua_State *L)
+{
+ long t, rem;
+
+ t = luaL_checkinteger(L, 1);
+ rem = alarm(t);
+ lua_pushinteger(L, rem);
+ return 1;
+}
+
+static int
+p9_wdir(lua_State *L)
+{
+ const char *path;
+ char *buf;
+ luaL_Buffer b;
+
+ path = luaL_optstring(L, 1, nil);
+ if(path != nil){
+ if(chdir(path) == -1)
+ return error(L, "chdir: %r");
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ luaL_buffinitsize(L, &b, Iosize);
+ buf = luaL_buffaddr(&b);
+ if(getwd(buf, Iosize) == nil)
+ return error(L, "getwd: %r");
+ luaL_pushresultsize(&b, strlen(buf));
+ return 1;
+}
+
+static int
+p9_user(lua_State *L)
+{
+ lua_pushstring(L, getuser());
+ return 1;
+}
+
+static int
+p9_sysname(lua_State *L)
+{
+ lua_pushstring(L, sysname());
+ return 1;
+}
+
+static int
+p9_pid(lua_State *L)
+{
+ lua_pushinteger(L, getpid());
+ return 1;
+}
+
+static int
+p9_ppid(lua_State *L)
+{
+ lua_pushinteger(L, getppid());
+ return 1;
+}
+
+static int
+p9_rfork(lua_State *L)
+{
+ int flags, i, n, r;
+ char *f[12];
+
+ flags = RFENVG|RFNAMEG|RFNOTEG;
+ n = getfields(luaL_optstring(L, 1, ""), f, sizeof f, 0, " \t\n");
+ if(n > 0) for(flags = 0, i = 0; i < n; i++){
+ if (strcmp(f[i], "name") == 0)
+ flags |= RFNAMEG;
+ else if(strcmp(f[i], "cname") == 0)
+ flags |= RFCNAMEG;
+ else if(strcmp(f[i], "env") == 0)
+ flags |= RFENVG;
+ else if(strcmp(f[i], "cenv") == 0)
+ flags |= RFCENVG;
+ else if(strcmp(f[i], "note") == 0)
+ flags |= RFNOTEG;
+ else if(strcmp(f[i], "fd") == 0)
+ flags |= RFFDG;
+ else if(strcmp(f[i], "cfd") == 0)
+ flags |= RFCFDG;
+ else if(strcmp(f[i], "nomnt") == 0)
+ flags |= RFNOMNT;
+ else if(strcmp(f[i], "proc") == 0)
+ flags |= RFPROC;
+ else if(strcmp(f[i], "nowait") == 0)
+ flags |= RFNOWAIT;
+ else if(strcmp(f[i], "rend") == 0)
+ flags |= RFREND;
+ else if(strcmp(f[i], "mem") == 0)
+ flags |= RFMEM;
+ else
+ return luaL_error(L, "unknown rfork flag '%s'", f[i]);
+ }
+ if((r = rfork(flags)) == -1)
+ return error(L, "rfork %r");
+ lua_pushinteger(L, r);
+ return 1;
+}
+
+static int
+p9_exec(lua_State *L)
+{
+ int argc, i;
+ const char **argv, *p;
+ char buf[Smallbuf];
+
+ argc = lua_gettop(L);
+ if(argc < 1)
+ luaL_argerror(L, 1, "string arguments expected");
+ argv = lalloc(L, nil, (argc+1) * sizeof(char*));
+ for(i = 1; i <= argc; i++)
+ argv[i-1] = luaL_checkstring(L, i);
+ argv[argc] = nil;
+ p = argv[0];
+ if(p[0] != '/' && (p[0] != '.' && p[1] != '/')){
+ snprint(buf, sizeof buf, "/bin/%s", argv[0]);
+ argv[0] = buf;
+ }
+ exec(argv[0], argv);
+ free(argv);
+ return error(L, "exec: %r");
+}
+
+static int
+p9_wait(lua_State *L)
+{
+ Waitmsg *w;
+
+ w = wait();
+ if(w == nil)
+ return error(L, "wait: %r");
+ lua_pushboolean(L, w->msg[0] == 0 ? 1 : 0);
+ lua_createtable(L, 0, 3);
+ lua_pushinteger(L, w->pid);
+ lua_setfield(L, -2, "pid");
+ lua_pushstring(L, w->msg);
+ lua_setfield(L, -2, "status");
+ lua_createtable(L, 3, 0);
+ lua_pushinteger(L, w->time[0]);
+ lua_setfield(L, -2, "user");
+ lua_pushinteger(L, w->time[1]);
+ lua_setfield(L, -2, "system");
+ lua_pushinteger(L, w->time[2]);
+ lua_setfield(L, -2, "real");
+ lua_setfield(L, -2, "time");
+ free(w);
+ return 2;
+}
--- /dev/null
+++ b/base/walk.c
@@ -1,0 +1,231 @@
+static char*
+perms(int p, char *buf)
+{
+ buf[0] = p & 04 ? 'r' : '-';
+ buf[1] = p & 02 ? 'w' : '-';
+ buf[2] = p & 01 ? 'x' : '-';
+ return buf;
+}
+
+static void
+createdirtable(lua_State *L, Dir *d)
+{
+ #define set(t, k, v) do { \
+ lua_pushstring(L, (k)); \
+ lua_push##t(L, (v)); \
+ lua_rawset(L, -3); \
+ } while(0)
+
+ lua_createtable(L, 0, 11);
+ set(integer, "type", d->type);
+ set(integer, "dev", d->dev);
+ set(integer, "atime", d->atime);
+ set(integer, "mtime", d->mtime);
+ set(integer, "length", d->length);
+ set(string, "name", d->name);
+ set(string, "uid", d->uid);
+ set(string, "gid", d->gid);
+ set(string, "muid", d->muid);
+
+ lua_pushstring(L, "qid");
+ lua_createtable(L, 0, 3);
+ set(integer, "path", d->qid.path);
+ set(integer, "vers", d->qid.vers);
+ set(integer, "type", d->qid.type);
+ lua_rawset(L, -3);
+
+ lua_pushstring(L, "mode");
+ lua_createtable(L, 0, 7);
+ ulong m = d->mode;
+ set(integer, "raw", m);
+ if(m & DMDIR)
+ set(boolean, "dir", 1);
+ else
+ set(boolean, "file", 1);
+ if(m & DMAPPEND)
+ set(boolean, "append", 1);
+ if(m & DMTMP)
+ set(boolean, "tmp", 1);
+ if(m & DMMOUNT)
+ set(boolean, "mount", 1);
+ if(m & DMAUTH)
+ set(boolean, "auth", 1);
+ char buf[10] = {0};
+ set(string, "user", perms((m & 0700) >> 6, buf));
+ set(string, "group", perms((m & 0070) >> 3, buf+3));
+ set(string, "other", perms((m & 0007) >> 0, buf+6));
+ set(string, "perm", buf);
+ lua_rawset(L, -3);
+
+ #undef set
+}
+
+static int
+p9_stat(lua_State *L)
+{
+ Dir *d;
+
+ d = nil;
+ switch(lua_type(L, 1)){
+ default:
+ USED(d);
+ return luaL_typeerror(L, 1, "string or number");
+ case LUA_TSTRING:
+ d = dirstat(lua_tostring(L, 1)); break;
+ case LUA_TNUMBER:
+ d = dirfstat(lua_tonumber(L, 1)); break;
+ }
+ if(d == nil)
+ return error(L, "stat: %r");
+ createdirtable(L, d);
+ free(d);
+ return 1;
+}
+
+typedef struct Walk {
+ int fd;
+ int nleft;
+ Dir *dirs, *p;
+} Walk;
+
+static int
+p9_walk(lua_State *L)
+{
+ static int p9_walkout(lua_State*);
+ static int p9_walknext(lua_State*);
+ int nargs, wstate;
+ Dir *d;
+ Walk *w;
+
+ nargs = lua_gettop(L);
+ w = lua_newuserdatauv(L, sizeof(Walk), 1);
+ wstate = nargs + 1;
+ w->fd = -1;
+ w->nleft = 0;
+ w->dirs = w->p = nil;
+ luaL_setmetatable(L, "p9-Walk");
+ if(nargs == 2){
+ lua_pushvalue(L, 2);
+ lua_setiuservalue(L, wstate, 1);
+ }
+ if(lua_isnumber(L, 1))
+ w->fd = lua_tointeger(L, 1);
+ else{
+ if((w->fd = open(luaL_checkstring(L, 1), OREAD|OCEXEC)) == -1){
+ error(L, "open: %r");
+ goto Error;
+ }
+ }
+ if((d = dirfstat(w->fd)) == nil){
+ error(L, "stat: %r");
+ goto Error;
+ }
+ int isdir = d->mode & DMDIR;
+ free(d);
+ if(!isdir){
+ error(L, "walk in a non-directory");
+ goto Error;
+ }
+ /* return p9_walknext, p9-Walk, nil, p9-Walk */
+ lua_pushcfunction(L, p9_walknext);
+ lua_pushvalue(L, wstate);
+ lua_pushnil(L);
+ lua_pushvalue(L, wstate);
+ return 4;
+Error:
+ if(nargs == 2){
+ lua_setfield(L, 2, "error");
+ lua_pushcfunction(L, p9_walkout);
+ return 1;
+ }
+ return lua_error(L);
+}
+
+static int
+p9_walkout(lua_State*)
+{
+ return 0;
+}
+
+static int
+p9_walknext(lua_State *L)
+{
+ Walk *w;
+ Dir *d;
+
+ w = luaL_checkudata(L, 1, "p9-Walk");
+ if(w->nleft == 0){
+ if(w->dirs != nil){
+ free(w->dirs);
+ w->dirs = nil;
+ }
+ if((w->nleft = dirread(w->fd, &w->dirs)) == -1){
+ error(L, "dirread: %r");
+ goto Error;
+ }
+ w->p = w->dirs;
+ if(w->nleft == 0)
+ return 0; /* Last Walk state will be closed */
+ }
+ w->nleft--;
+ d = w->p++;
+ createdirtable(L, d);
+ return 1;
+Error:
+ if(lua_getiuservalue(L, 1, 1) == LUA_TTABLE){
+ lua_pushvalue(L, -2);
+ lua_setfield(L, -2, "error");
+ lua_pushnil(L);
+ return 1;
+ }
+ lua_pop(L, 1);
+ return 2;
+}
+
+static int
+p9_walkclose(lua_State *L)
+{
+ Walk *w;
+
+ w = luaL_checkudata(L, 1, "p9-Walk");
+ if(w->dirs != nil){
+ free(w->dirs);
+ w->dirs = nil;
+ }
+ if(w->fd != -1){
+ close(w->fd);
+ w->fd = -1;
+ }
+ return 0;
+}
+
+static int
+p9_wstat(lua_State *L)
+{
+ static int createperm(lua_State*, char*); /* from fs.c */
+ const char *path, *k;
+ Dir new;
+
+ path = luaL_checkstring(L, 1);
+ luaL_argexpected(L, lua_type(L, 2) == LUA_TTABLE, 2, "table");
+ nulldir(&new);
+ lua_pushnil(L);
+ while(lua_next(L, -2)){
+ k = lua_tostring(L, -2);
+ if(strcmp(k, "name") == 0)
+ new.name = (char*)lua_tostring(L, -1); /* dw */
+ else if(strcmp(k, "mode") == 0)
+ new.mode = createperm(L, lua_tostring(L, -1));
+ else if(strcmp(k, "mtime") == 0)
+ new.mtime = lua_tointeger(L, -1);
+ else if(strcmp(k, "gid") == 0)
+ new.gid = (char*)lua_tostring(L, -1);
+ else if(strcmp(k, "length") == 0)
+ new.length = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ }
+ if(dirwstat(path, &new) == -1)
+ return error(L, "wstat: %r");
+ lua_pushboolean(L, 1);
+ return 1;
+}
--- a/env.c
+++ /dev/null
@@ -1,133 +1,0 @@
-/* Environment variables
- *
- * p9.env object provides a map between the /env device and Lua,
- * with its dynamic fields representing the environment variables.
- * Assigning a value to the field writes to the environment:
- *
- * p9.env.var = "value"
- *
- * while reading a value reads from the environment:
- *
- * assert(p9.env.var == "value")
- *
- * A value can be a string or a list.
- * A list is encoded (decoded) to (from) the environment as a
- * list of strings according to the encoding used by the rc(1)
- * shell (0-byte separated fields).
- *
- * lua> p9.env.list = {"a", "b", "c"}
- * rc> echo $#list * $list
- * 3 * a b c
- *
- * p9.getenv(name) and p9.setenv(name, val) provide the more
- * usual API.
- */
-
-static int
-p9_getenv(lua_State *L)
-{
- int fd;
- long len, elems, i;
- char env[Smallbuf];
- const char *buf, *s, *e;
-
- snprint(env, sizeof env, "/env/%s", luaL_checkstring(L, 1));
- if((fd = open(env, OREAD)) == -1){
- lua_pushnil(L);
- return 1;
- }
- slurp(L, fd, -1);
- close(fd);
- len = luaL_len(L, -1);
- buf = lua_tostring(L, -1);
- /* Empty */
- if(len == 0){
- lua_pushnil(L);
- return 1;
- }
- /* String (not a list) */
- if(buf[len-1] != '\0')
- return 1;
- /* List */
- for(elems = i = 0; i < len; i++)
- if(buf[i] == '\0') elems++;
- s = buf;
- e = buf + len;
- lua_createtable(L, elems, 0);
- for(i = 1; s < e; i++){
- lua_pushstring(L, s);
- lua_rawseti(L, -2, i);
- s = memchr(s, '\0', e - s);
- s++;
- }
- return 1;
-}
-
-static int
-p9_getenv_index(lua_State *L)
-{
- lua_remove(L, 1);
- return p9_getenv(L);
-}
-
-static int
-p9_setenv(lua_State *L)
-{
- int fd, ntab, n, t, i;
- char env[Smallbuf];
- luaL_Buffer b;
-
- snprint(env, sizeof env, "/env/%s", luaL_checkstring(L, 1));
- t = lua_type(L, 2);
- if(t != LUA_TNIL && t != LUA_TSTRING && t != LUA_TTABLE)
- return luaL_argerror(L, 2, "nil, string, or table expected");
- if((fd = create(env, OWRITE, 0644)) == -1)
- return error(L, "open: %r");
- /*
- * Writes below are not fully checked for
- * error (write(n) != n), because env(3)
- * may truncate the value at its dumb 16k
- * limit. Encoding a knowledge of this limit
- * sucks, as does env(3).
- */
- if(t == LUA_TNIL){
- close(fd);
- return 0;
- }else if(t == LUA_TSTRING){
- n = luaL_len(L, 2);
- if(write(fd, lua_tostring(L, 2), n) == -1){
- close(fd);
- return error(L, "write: %r");
- }
- }else{
- ntab = luaL_len(L, 2);
- luaL_buffinit(L, &b);
- for(i = 1; i <= ntab; i++){
- t = lua_geti(L, 2, i);
- if(t != LUA_TSTRING){
- if(luaL_callmeta(L, -1, "__tostring")
- && lua_type(L, -1) == LUA_TSTRING){
- lua_replace(L, -2);
- }else{
- lua_pop(L, 1);
- continue;
- }
- }
- luaL_addvalue(&b);
- luaL_addchar(&b, '\0');
- }
- if(write(fd, luaL_buffaddr(&b), luaL_bufflen(&b)) == -1){
- close(fd);
- return error(L, "write: %r");
- }
- }
- close(fd);
- return 0;
-}
-
-static int
-p9_setenv_newindex(lua_State *L)
-{
- lua_remove(L, 1);
- return p9_setenv(L);
-}
--- a/fs.c
+++ /dev/null
@@ -1,398 +1,0 @@
-/*
- * 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;
-}
--- a/mkfile
+++ b/mkfile
@@ -3,18 +3,22 @@
CFLAGS=-FTVw -p -I../lua/shim -I../lua
LIB=libp9.a.$O
+MOD=\
+ base\
+ note
-OBJS=p9.$O
+default:V: all
-all:V: $LIB
+obj/:
+ mkdir -p $target
-clean:QV:
- rm -f *.[$OS] *.a.[$OS]
+obj/%.$O: obj/ %/%.c
+ $CC $CFLAGS -o $target $stem/$stem.c
-$LIB: $OBJS
+$LIB: ${MOD:%=obj/%.$O}
ar cr $target $prereq
-%.$O: %.c
- $CC $CFLAGS $stem.c
+all:V: $LIB
-p9.$O: p9.c fs.c walk.c env.c ns.c proc.c note.c misc.c
+clean:V:
+ rm -rf *.a.[$OS] obj/
--- a/note.c
+++ /dev/null
@@ -1,198 +1,0 @@
-/*
- * The following global state designates the Lua state
- * responsible for registering and running note handler
- * functions -- the one (and only) loading this module.
- * Additionally the note itself is communicated to the
- * postponed handler here due to most of Lua API not
- * being safe to use in a note handler context.
- *
- * This global state and nondeterministic nature of
- * postponed handling of notes means this module should
- * be used with care and will likely need to be heavily
- * adapted for use in any but the simplest of hosts.
- * Lu9 standalone interpreter is an example of a simple
- * program with a single Lua state and Lua code being
- * "the boss", that is, the note is very likely to
- * interrupt a Lua VM rather than host code, and if not
- * the VM will be entered shortly after. This lets
- * postponed note handlers run relatively close to
- * the actual note event.
- * In more involved programs, perhaps running multiple
- * separate Lua states, or spending more time in the
- * host, the postponed handlers may run only as soon
- * as the designated handler state gets a chance to
- * run, if at all.
- *
- * In short, consider alternatives to Lua code doing
- * any direct note handling.
- *
- * TODO: the note state, catcher and exit functions,
- * and notify registration should all be left for the
- * host to set up.
- */
-
-typedef struct Note Note;
-
-struct Note {
- lua_State *L;
- char note[ERRMAX+1];
-};
-
-static Note notestate;
-
-/*
- * Acks the note so it can be handled outside note context
- * but only after the possibly interrupted Lua state
- * stabilizes. This is done by registering an instruction
- * hook and running handler functions inside it.
- * Note that another note may come just as we are doing
- * this. We do nothing about it currently: the newer
- * notes simply interrupt currently executing handlers.
- * One solution is to queue incoming notes and handle
- * all of them in order.
- * Also note that this catcher always acknowledges the
- * note, preventing any other catchers registered after
- * it from ever seeing the note. Therefore you most
- * likely want it to be the last or only note handler.
- */
-static int
-notecatcher(void*, char *note)
-{
- static void noterunner(lua_State*, lua_Debug*);
-
- lua_sethook(notestate.L, noterunner,
- LUA_MASKCALL|LUA_MASKRET|LUA_MASKCOUNT, 1);
- strncpy(notestate.note, note, ERRMAX);
- return 1;
-}
-
-static void
-noterunner(lua_State *L, lua_Debug*)
-{
- int n, i;
-
- lua_sethook(notestate.L, nil, 0, 0);
- if(lua_getfield(L, LUA_REGISTRYINDEX, "p9-note-handlers") != LUA_TTABLE)
- luaL_error(L, "missing note handlers table");
- if((n = lua_rawlen(L, -1)) == 0)
- return;
- for(i = 1; i <= n; i++){
- lua_rawgeti(L, -1, i); /* handler(note) */
- lua_pushstring(L, notestate.note);
- if(lua_pcall(L, 1, 1, 0) != LUA_OK)
- break;
- if(lua_toboolean(L, -1) == 1)
- return; /* to where we got interrupted */
- lua_pop(L, 1);
- }
- /* Emulate kernel handling of unacknowledged note. */
- if(strncmp(notestate.note, "sys:", 4) == 0)
- abort();
- exits(notestate.note); /* threadexitsall when using thread.h */
-}
-
-static int
-noteset(lua_State *L)
-{
- if(lua_getfield(L, LUA_REGISTRYINDEX, "p9-note-handlers") != LUA_TTABLE)
- return luaL_error(L, "missing note handlers table");
- lua_insert(L, -2);
- lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
- lua_pop(L, 1);
- return 0;
-}
-
-static int
-noteunset(lua_State *L)
-{
- int n, pos, fn, t;
-
- fn = lua_gettop(L);
- if(lua_getfield(L, LUA_REGISTRYINDEX, "p9-note-handlers") != LUA_TTABLE)
- return luaL_error(L, "missing note handlers table");
- t = fn + 1;
- n = lua_rawlen(L, t);
- for(pos = 1; pos <= n; pos++){
- lua_rawgeti(L, t, pos);
- if(lua_rawequal(L, fn, -1))
- goto remove;
- lua_pop(L, 1);
- }
- lua_pop(L, 2);
- return 0;
-remove:
- lua_pop(L, 1);
- for ( ; pos < n; pos++) {
- lua_rawgeti(L, t, pos + 1);
- lua_rawseti(L, t, pos);
- }
- lua_pushnil(L);
- lua_rawseti(L, t, pos);
- lua_pop(L, 2);
- return 0;
-}
-
-static int
-p9_note_catch(lua_State *L)
-{
- int fn;
- const char *arg;
-
- fn = lua_gettop(L);
- luaL_argexpected(L, fn > 0, 1, "function expected");
- if(fn == 1)
- arg = "set";
- else
- arg = luaL_checkstring(L, 1);
- luaL_argexpected(L,
- lua_type(L, fn) == LUA_TFUNCTION, fn, "function");
- if(strcmp(arg, "set") == 0)
- return noteset(L);
- else if(strcmp(arg, "unset") == 0)
- return noteunset(L);
- return luaL_error(L, "'set' or 'unset' expected");
-}
-
-static int
-p9_note_post(lua_State *L)
-{
- int pid, w;
- const char *who, *note;
-
- who = luaL_checkstring(L, 1);
- if(strcmp(who, "proc") == 0)
- w = PNPROC;
- else if(strcmp(who, "group") == 0)
- w = PNGROUP;
- else
- return luaL_argerror(L, 1, "expected 'proc' or 'group'");
- pid = luaL_checkinteger(L, 2);
- note = luaL_checkstring(L, 3);
- if(postnote(w, pid, note) == -1)
- return error(L, "postnote: %r");
- lua_pushboolean(L, 1);
- return 1;
-}
-
-static luaL_Reg p9_note_funcs[] = {
- {"post", p9_note_post},
- {"catch", p9_note_catch},
- {nil, nil},
-};
-
-int
-luaopen_p9_note(lua_State *L)
-{
- /* Only one Lua state may work with notes */
- if(notestate.L != nil)
- return 0;
- notestate.L = L;
-
- lua_createtable(L, 1, 0);
- lua_setfield(L, LUA_REGISTRYINDEX, "p9-note-handlers");
-
- luaL_newlib(L, p9_note_funcs);
-
- atnotify(notecatcher, 1);
- return 1;
-}
--- /dev/null
+++ b/note/note.c
@@ -1,0 +1,206 @@
+#include <u.h>
+#include <libc.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "../base/common.c"
+
+/*
+ * The following global state designates the Lua state
+ * responsible for registering and running note handler
+ * functions -- the one (and only) loading this module.
+ * Additionally the note itself is communicated to the
+ * postponed handler here due to most of Lua API not
+ * being safe to use in a note handler context.
+ *
+ * This global state and nondeterministic nature of
+ * postponed handling of notes means this module should
+ * be used with care and will likely need to be heavily
+ * adapted for use in any but the simplest of hosts.
+ * Lu9 standalone interpreter is an example of a simple
+ * program with a single Lua state and Lua code being
+ * "the boss", that is, the note is very likely to
+ * interrupt a Lua VM rather than host code, and if not
+ * the VM will be entered shortly after. This lets
+ * postponed note handlers run relatively close to
+ * the actual note event.
+ * In more involved programs, perhaps running multiple
+ * separate Lua states, or spending more time in the
+ * host, the postponed handlers may run only as soon
+ * as the designated handler state gets a chance to
+ * run, if at all.
+ *
+ * In short, consider alternatives to Lua code doing
+ * any direct note handling.
+ *
+ * TODO: the note state, catcher and exit functions,
+ * and notify registration should all be left for the
+ * host to set up.
+ */
+
+typedef struct Note Note;
+
+struct Note {
+ lua_State *L;
+ char note[ERRMAX+1];
+};
+
+static Note notestate;
+
+/*
+ * Acks the note so it can be handled outside note context
+ * but only after the possibly interrupted Lua state
+ * stabilizes. This is done by registering an instruction
+ * hook and running handler functions inside it.
+ * Note that another note may come just as we are doing
+ * this. We do nothing about it currently: the newer
+ * notes simply interrupt currently executing handlers.
+ * One solution is to queue incoming notes and handle
+ * all of them in order.
+ * Also note that this catcher always acknowledges the
+ * note, preventing any other catchers registered after
+ * it from ever seeing the note. Therefore you most
+ * likely want it to be the last or only note handler.
+ */
+static int
+notecatcher(void*, char *note)
+{
+ static void noterunner(lua_State*, lua_Debug*);
+
+ lua_sethook(notestate.L, noterunner,
+ LUA_MASKCALL|LUA_MASKRET|LUA_MASKCOUNT, 1);
+ strncpy(notestate.note, note, ERRMAX);
+ return 1;
+}
+
+static void
+noterunner(lua_State *L, lua_Debug*)
+{
+ int n, i;
+
+ lua_sethook(notestate.L, nil, 0, 0);
+ if(lua_getfield(L, LUA_REGISTRYINDEX, "p9-note-handlers") != LUA_TTABLE)
+ luaL_error(L, "missing note handlers table");
+ if((n = lua_rawlen(L, -1)) == 0)
+ return;
+ for(i = 1; i <= n; i++){
+ lua_rawgeti(L, -1, i); /* handler(note) */
+ lua_pushstring(L, notestate.note);
+ if(lua_pcall(L, 1, 1, 0) != LUA_OK)
+ break;
+ if(lua_toboolean(L, -1) == 1)
+ return; /* to where we got interrupted */
+ lua_pop(L, 1);
+ }
+ /* Emulate kernel handling of unacknowledged note. */
+ if(strncmp(notestate.note, "sys:", 4) == 0)
+ abort();
+ exits(notestate.note); /* threadexitsall when using thread.h */
+}
+
+static int
+noteset(lua_State *L)
+{
+ if(lua_getfield(L, LUA_REGISTRYINDEX, "p9-note-handlers") != LUA_TTABLE)
+ return luaL_error(L, "missing note handlers table");
+ lua_insert(L, -2);
+ lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
+ lua_pop(L, 1);
+ return 0;
+}
+
+static int
+noteunset(lua_State *L)
+{
+ int n, pos, fn, t;
+
+ fn = lua_gettop(L);
+ if(lua_getfield(L, LUA_REGISTRYINDEX, "p9-note-handlers") != LUA_TTABLE)
+ return luaL_error(L, "missing note handlers table");
+ t = fn + 1;
+ n = lua_rawlen(L, t);
+ for(pos = 1; pos <= n; pos++){
+ lua_rawgeti(L, t, pos);
+ if(lua_rawequal(L, fn, -1))
+ goto remove;
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 2);
+ return 0;
+remove:
+ lua_pop(L, 1);
+ for ( ; pos < n; pos++) {
+ lua_rawgeti(L, t, pos + 1);
+ lua_rawseti(L, t, pos);
+ }
+ lua_pushnil(L);
+ lua_rawseti(L, t, pos);
+ lua_pop(L, 2);
+ return 0;
+}
+
+static int
+p9_note_catch(lua_State *L)
+{
+ int fn;
+ const char *arg;
+
+ fn = lua_gettop(L);
+ luaL_argexpected(L, fn > 0, 1, "function expected");
+ if(fn == 1)
+ arg = "set";
+ else
+ arg = luaL_checkstring(L, 1);
+ luaL_argexpected(L,
+ lua_type(L, fn) == LUA_TFUNCTION, fn, "function");
+ if(strcmp(arg, "set") == 0)
+ return noteset(L);
+ else if(strcmp(arg, "unset") == 0)
+ return noteunset(L);
+ return luaL_error(L, "'set' or 'unset' expected");
+}
+
+static int
+p9_note_post(lua_State *L)
+{
+ int pid, w;
+ const char *who, *note;
+
+ who = luaL_checkstring(L, 1);
+ if(strcmp(who, "proc") == 0)
+ w = PNPROC;
+ else if(strcmp(who, "group") == 0)
+ w = PNGROUP;
+ else
+ return luaL_argerror(L, 1, "expected 'proc' or 'group'");
+ pid = luaL_checkinteger(L, 2);
+ note = luaL_checkstring(L, 3);
+ if(postnote(w, pid, note) == -1)
+ return error(L, "postnote: %r");
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+static luaL_Reg p9_note_funcs[] = {
+ {"post", p9_note_post},
+ {"catch", p9_note_catch},
+ {nil, nil},
+};
+
+int
+luaopen_p9_note(lua_State *L)
+{
+ /* Only one Lua state may work with notes */
+ if(notestate.L != nil)
+ return 0;
+ notestate.L = L;
+
+ lua_createtable(L, 1, 0);
+ lua_setfield(L, LUA_REGISTRYINDEX, "p9-note-handlers");
+
+ luaL_newlib(L, p9_note_funcs);
+
+ atnotify(notecatcher, 1);
+ return 1;
+}
--- a/ns.c
+++ /dev/null
@@ -1,88 +1,0 @@
-int
-parsemntflags(lua_State *L, char *s)
-{
- int flags, n;
- char *f[4], buf[128];
-
- flags = MREPL;
- n = getfields(s, f, sizeof f, 1, " \t\r\n");
- if(n > 0) for(int i = 0; i < n; i++){
- if (strcmp(f[i], "after") == 0)
- flags |= MAFTER;
- else if(strcmp(f[i], "before") == 0)
- flags |= MBEFORE;
- else if(strcmp(f[i], "create") == 0)
- flags |= MCREATE;
- else if(strcmp(f[i], "cache") == 0)
- flags |= MCACHE;
- else{
- snprint(buf, sizeof buf, "skipping unknown mount flag '%s'", f[i]);
- lua_warning(L, buf, 0);
- }
- }
- return flags;
-}
-
-static int
-p9_bind(lua_State *L)
-{
- const char *this, *over;
- int flags, r;
-
- this = luaL_checkstring(L, 1);
- over = luaL_checkstring(L, 2);
- flags = parsemntflags(L, luaL_optstring(L, 3, ""));
- if((r = bind(this, over, flags)) == -1)
- return error(L, "bind: %r");
- lua_pushinteger(L, r);
- return 1;
-}
-
-static int
-p9_mount(lua_State *L)
-{
- const char *over, *aname;
- int fd, afd, closefd, flags, r;
-
- closefd = -1;
- switch(lua_type(L, 1)){
- default:
- return luaL_typeerror(L, 1, "file descriptor or path");
- case LUA_TNUMBER:
- fd = lua_tointeger(L, 1); break;
- case LUA_TSTRING:
- if((fd = open(lua_tostring(L, 1), ORDWR)) == -1)
- return error(L, "open: %r");
- closefd = fd;
- break;
- }
- over = luaL_checkstring(L, 2);
- flags = parsemntflags(L, luaL_optstring(L, 3, ""));
- aname = luaL_optstring(L, 4, "");
- afd = luaL_optinteger(L, 5, -1);
- if((r = mount(fd, afd, over, flags, aname)) == -1){
- close(closefd);
- return error(L, "mount: %r");
- }
- close(closefd);
- lua_pushinteger(L, r);
- return 1;
-}
-
-static int
-p9_unmount(lua_State *L)
-{
- const char *name, *over;
- int r;
-
- name = luaL_checkstring(L, 1);
- over = luaL_optstring(L, 2, nil);
- if(over == nil){
- over = name;
- name = nil;
- }
- if((r = unmount(name, over)) == -1)
- return error(L, "unmount: %r");
- lua_pushinteger(L, r);
- return 1;
-}
--- a/p9.c
+++ /dev/null
@@ -1,246 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <ctype.h>
-
-#include <lua.h>
-#include <lauxlib.h>
-
-enum {
- Iosize = 8192,
- Smallbuf = 512,
-};
-
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
-static int
-error(lua_State *L, char *fmt, ...)
-{
- va_list varg;
- int n;
- char *buf;
- luaL_Buffer b;
-
- lua_pushnil(L);
- buf = luaL_buffinitsize(L, &b, Smallbuf);
- va_start(varg, fmt);
- n = vsnprint(buf, Smallbuf, fmt, varg);
- va_end(varg);
- luaL_pushresultsize(&b, n);
- return 2;
-}
-
-/* Memory allocator associated with Lua state */
-static void*
-lalloc(lua_State *L, void *ptr, usize sz)
-{
- void *ud;
-
- if((ptr = (lua_getallocf(L, &ud))(ud, ptr, LUA_TUSERDATA, sz)) == nil){
- lua_pushliteral(L, "out of memory");
- lua_error(L);
- }
- memset(ptr, 0, sz);
- setmalloctag(ptr, getcallerpc(&L));
- return ptr;
-}
-
-/*
- * Various functions in this library require a
- * variably sized buffer for their operation.
- * Rather than allocating one for each call
- * we preallocate a shared buffer of reasonable
- * size and grow it as needed.
- * The buffer gets associated with a Lua state
- * at library load time.
- * getbuffer(L, sz) returns a pointer to the
- * memory area of at least sz bytes.
- *
- * To avoid stepping on each other's toes the
- * buffer use must be constrained to a single
- * call.
- */
-
-typedef struct Buf {
- usize sz;
- char *b;
-} Buf;
-
-static Buf*
-resizebuffer(lua_State *L, Buf *buf, usize sz)
-{
- if(buf == nil){
- buf = lalloc(L, nil, sizeof(Buf));
- buf->b = nil;
- buf->sz = 0;
- }
- if(buf->sz < sz){
- buf->b = lalloc(L, buf->b, sz);
- buf->sz = sz;
- }
- return buf;
-}
-
-static char*
-getbuffer(lua_State *L, usize sz)
-{
- Buf *buf;
-
- lua_getfield(L, LUA_REGISTRYINDEX, "p9-buffer");
- buf = lua_touserdata(L, -1);
- return resizebuffer(L, buf, sz)->b;
-}
-
-#include "fs.c"
-#include "walk.c"
-#include "env.c"
-#include "ns.c"
-#include "proc.c"
-#include "misc.c"
-#include "note.c"
-
-typedef struct Data {
- char *key;
- lua_Integer val;
-} Data;
-
-static Data p9data[] = {
- {"OREAD", OREAD},
- {"OWRITE", OWRITE},
- {"ORDWR", ORDWR},
- {"OEXEC", OEXEC},
- {"OTRUNC", OTRUNC},
- {"OCEXEC", OCEXEC},
- {"ORCLOSE", ORCLOSE},
- {"OEXCL", OEXCL},
-
- {"DMDIR", DMDIR},
- {"DMAPPEND", DMAPPEND},
- {"DMEXCL", DMEXCL},
- {"DMMOUNT", DMMOUNT},
- {"DMAUTH", DMAUTH},
- {"DMTMP", DMTMP},
- {"DMREAD", DMREAD},
- {"DMWRITE", DMWRITE},
- {"DMEXEC", DMEXEC},
- {"QTDIR", QTDIR},
- {"QTAPPEND", QTAPPEND},
- {"QTEXCL", QTEXCL},
- {"QTMOUNT", QTMOUNT},
- {"QTAUTH", QTAUTH},
- {"QTTMP", QTTMP},
- {"QTFILE", QTFILE},
-
- {"MREPL", MREPL},
- {"MBEFORE", MBEFORE},
- {"MAFTER", MAFTER},
- {"MCREATE", MCREATE},
- {"MCACHE", MCACHE},
-
- {"RFPROC", RFPROC},
- {"RFNOWAIT", RFNOWAIT},
- {"RFNAMEG", RFNAMEG},
- {"RFCNAMEG", RFCNAMEG},
- {"RFNOMNT", RFNOMNT},
- {"RFENVG", RFENVG},
- {"RFCENVG", RFCENVG},
- {"RFNOTEG", RFNOTEG},
- {"RFFDG", RFFDG},
- {"RFCFDG", RFCFDG},
- {"RFREND", RFREND},
- {"RFMEM", RFMEM},
-
- {nil, 0}
-};
-
-static luaL_Reg p9func[] = {
- {"open", p9_open},
- {"create", p9_create},
- {"file", p9_file},
- {"pipe", p9_pipe},
- {"remove", p9_remove},
- {"access", p9_access},
-
- {"stat", p9_stat},
- {"wstat", p9_wstat},
- {"walk", p9_walk},
-
- {"bind", p9_bind},
- {"mount", p9_mount},
- {"unmount", p9_unmount},
-
- {"getenv", p9_getenv},
- {"setenv", p9_setenv},
-
- {"abort", p9_abort},
- {"exits", p9_exits},
- {"fatal", p9_fatal},
- {"sleep", p9_sleep},
- {"alarm", p9_alarm},
- {"rfork", p9_rfork},
- {"wait", p9_wait},
- {"exec", p9_exec},
- {"wdir", p9_wdir},
- {"pid", p9_pid},
- {"ppid", p9_ppid},
- {"user", p9_user},
- {"sysname", p9_sysname},
-
- {"cleanname", p9_cleanname},
-
- {nil, nil}
-};
-
-int
-luaopen_p9(lua_State *L)
-{
- int lib;
- Buf *buf;
- Data *d;
-
- buf = resizebuffer(L, nil, Iosize);
- lua_pushlightuserdata(L, buf);
- lua_setfield(L, LUA_REGISTRYINDEX, "p9-buffer");
-
- static luaL_Reg filemt[] = {
- {"close", p9_file_close},
- {"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},
- {nil, nil},
- };
- luaL_newmetatable(L, "p9-File");
- luaL_setfuncs(L, filemt, 0);
- lua_pop(L, 1);
-
- static luaL_Reg walkmt[] = {
- {"__close", p9_walkclose},
- {nil, nil},
- };
- luaL_newmetatable(L, "p9-Walk");
- luaL_setfuncs(L, walkmt, 0);
- lua_pop(L, 1);
-
- luaL_newlib(L, p9func);
- lib = lua_gettop(L);
- for(d = p9data; d->key != nil; d++){
- lua_pushinteger(L, d->val);
- lua_setfield(L, -2, d->key);
- }
-
- static luaL_Reg envmt[] = {
- {"__index", p9_getenv_index},
- {"__newindex", p9_setenv_newindex},
- {nil, nil},
- };
- lua_createtable(L, 0, 2);
- luaL_setfuncs(L, envmt, 0);
- lua_pushvalue(L, -1);
- lua_setmetatable(L, -2);
- lua_setfield(L, lib, "env");
-
- return 1;
-}
--- a/proc.c
+++ /dev/null
@@ -1,193 +1,0 @@
-static int
-p9_abort(lua_State*)
-{
- abort();
- /* never */ return 0;
-}
-
-static int
-p9_exits(lua_State *L)
-{
- exits(luaL_optstring(L, 1, nil));
- /* never */ return 0;
-}
-
-static int
-p9_fatal(lua_State *L)
-{
- if(lua_gettop(L) > 1
- && lua_getglobal(L, "string") == LUA_TTABLE
- && lua_getfield(L, -1, "format") == LUA_TFUNCTION){
- lua_remove(L, -2);
- lua_insert(L, 1);
- if(lua_pcall(L, lua_gettop(L) - 1, 1, 0) == LUA_OK)
- sysfatal(lua_tostring(L, -1));
- }
- sysfatal(luaL_optstring(L, 1, "fatal"));
- /* never */ return 0;
-}
-
-static int
-p9_sleep(lua_State *L)
-{
- long t;
-
- t = luaL_checkinteger(L, 1);
- lua_pushboolean(L,
- sleep(t) == -1 ? 0 : 1
- );
- return 1;
-}
-
-static int
-p9_alarm(lua_State *L)
-{
- long t, rem;
-
- t = luaL_checkinteger(L, 1);
- rem = alarm(t);
- lua_pushinteger(L, rem);
- return 1;
-}
-
-static int
-p9_wdir(lua_State *L)
-{
- const char *path;
- char *buf;
- luaL_Buffer b;
-
- path = luaL_optstring(L, 1, nil);
- if(path != nil){
- if(chdir(path) == -1)
- return error(L, "chdir: %r");
- lua_pushboolean(L, 1);
- return 1;
- }
- luaL_buffinitsize(L, &b, Iosize);
- buf = luaL_buffaddr(&b);
- if(getwd(buf, Iosize) == nil)
- return error(L, "getwd: %r");
- luaL_pushresultsize(&b, strlen(buf));
- return 1;
-}
-
-static int
-p9_user(lua_State *L)
-{
- lua_pushstring(L, getuser());
- return 1;
-}
-
-static int
-p9_sysname(lua_State *L)
-{
- lua_pushstring(L, sysname());
- return 1;
-}
-
-static int
-p9_pid(lua_State *L)
-{
- lua_pushinteger(L, getpid());
- return 1;
-}
-
-static int
-p9_ppid(lua_State *L)
-{
- lua_pushinteger(L, getppid());
- return 1;
-}
-
-static int
-p9_rfork(lua_State *L)
-{
- int flags, i, n, r;
- char *f[12];
-
- flags = RFENVG|RFNAMEG|RFNOTEG;
- n = getfields(luaL_optstring(L, 1, ""), f, sizeof f, 0, " \t\n");
- if(n > 0) for(flags = 0, i = 0; i < n; i++){
- if (strcmp(f[i], "name") == 0)
- flags |= RFNAMEG;
- else if(strcmp(f[i], "cname") == 0)
- flags |= RFCNAMEG;
- else if(strcmp(f[i], "env") == 0)
- flags |= RFENVG;
- else if(strcmp(f[i], "cenv") == 0)
- flags |= RFCENVG;
- else if(strcmp(f[i], "note") == 0)
- flags |= RFNOTEG;
- else if(strcmp(f[i], "fd") == 0)
- flags |= RFFDG;
- else if(strcmp(f[i], "cfd") == 0)
- flags |= RFCFDG;
- else if(strcmp(f[i], "nomnt") == 0)
- flags |= RFNOMNT;
- else if(strcmp(f[i], "proc") == 0)
- flags |= RFPROC;
- else if(strcmp(f[i], "nowait") == 0)
- flags |= RFNOWAIT;
- else if(strcmp(f[i], "rend") == 0)
- flags |= RFREND;
- else if(strcmp(f[i], "mem") == 0)
- flags |= RFMEM;
- else
- return luaL_error(L, "unknown rfork flag '%s'", f[i]);
- }
- if((r = rfork(flags)) == -1)
- return error(L, "rfork %r");
- lua_pushinteger(L, r);
- return 1;
-}
-
-static int
-p9_exec(lua_State *L)
-{
- int argc, i;
- const char **argv, *p;
- char buf[Smallbuf];
-
- argc = lua_gettop(L);
- if(argc < 1)
- luaL_argerror(L, 1, "string arguments expected");
- argv = lalloc(L, nil, (argc+1) * sizeof(char*));
- for(i = 1; i <= argc; i++)
- argv[i-1] = luaL_checkstring(L, i);
- argv[argc] = nil;
- p = argv[0];
- if(p[0] != '/' && (p[0] != '.' && p[1] != '/')){
- snprint(buf, sizeof buf, "/bin/%s", argv[0]);
- argv[0] = buf;
- }
- exec(argv[0], argv);
- free(argv);
- return error(L, "exec: %r");
-}
-
-static int
-p9_wait(lua_State *L)
-{
- Waitmsg *w;
-
- w = wait();
- if(w == nil)
- return error(L, "wait: %r");
- lua_pushboolean(L, w->msg[0] == 0 ? 1 : 0);
- lua_createtable(L, 0, 3);
- lua_pushinteger(L, w->pid);
- lua_setfield(L, -2, "pid");
- lua_pushstring(L, w->msg);
- lua_setfield(L, -2, "status");
- lua_createtable(L, 3, 0);
- lua_pushinteger(L, w->time[0]);
- lua_setfield(L, -2, "user");
- lua_pushinteger(L, w->time[1]);
- lua_setfield(L, -2, "system");
- lua_pushinteger(L, w->time[2]);
- lua_setfield(L, -2, "real");
- lua_setfield(L, -2, "time");
- free(w);
- return 2;
-}
--- a/test.lua
+++ /dev/null
@@ -1,322 +1,0 @@
-#!/bin/lu9
-
-local p9 = require "p9"
-local dump = (function()
- local ok, inspect = pcall(require, "inspect")
- if ok then return function(v) print(inspect(v)) end end
- return print
-end)()
-
-local function tmp()
- return string.format("/tmp/lua.%x", math.random(1e10))
-end
-
-local function rc()
- os.execute("prompt='p9; ' rc -i")
-end
-
-p9.rfork("env name")
-os.execute("ramfs")
-
-
-
--- File I/O
-do
- local s = string.rep("ABCD", 2048*2) -- 16k > standard 8k buffer
- local f = tmp()
- local fd = p9.create(f, "w")
- fd:write(s)
- fd:close()
-
- fd = p9.open(f)
- assert(fd:slurp() == s)
- fd:close()
-
- fd = p9.open(f, "r")
- assert(fd:slurp(2048) == string.rep("ABCD", 512))
- fd:close()
-
- fd = p9.open(f, "r")
- assert(fd:slurp(16*1024 + 999) == s)
- fd:close()
-
- fd = p9.open(f, "r")
- assert(fd:seek(0, "end") == 16*1024)
- assert(fd:seek(8192, "set") == 8192
- and fd:slurp() == string.rep("ABCD", 2*1024))
- fd:seek(0)
- assert(fd:seek(16*1024 - 4, "cur") == 16*1024 - 4
- and fd:slurp() == "ABCD")
- fd:close()
-end
-
--- File objects
--- Closing
--- Make sure it's closed
-local fd
-do
- local f <close> = p9.create(tmp())
- fd = f.fd
-end
-assert(p9.file(fd):close() == nil)
--- Make sure it's not closed
-local fd
-do
- local f = p9.create(tmp())
- fd = f.fd
-end
-assert(p9.file(fd):seek(0))
-p9.file(fd):close()
-
--- file:path()
-do
- local f = p9.create("/tmp/fd2path")
- assert(f:path() == "/tmp/fd2path")
- f:close()
-end
-
--- file:iounit()
-do
- local f = assert(p9.open("/srv/slashmnt"))
- assert(f:iounit() ~= 0)
- f:close()
-end
-
--- file:dup()
-do
- local a, b = assert(p9.pipe())
- local c = assert(a:dup())
- a:write("hello")
- assert(b:read() == "hello")
- c:write("world")
- assert(b:read() == "world")
- a:close() b:close() c:close()
-
- a = assert(p9.open("/lib/glass"))
- local buf = a:read()
- b = assert(p9.open("/lib/bullshit"))
- b = assert(a:dup(b))
- b:seek(0)
- assert(b:read() == buf)
-end
-
--- access
-do
- assert(p9.access("/dev/null"))
- assert(p9.access("/dev/null", "read write"))
- assert(p9.access("/bin/rc", "exec"))
-end
-
--- pipe
-do
- local p₀, p₁ = assert(p9.pipe())
- p₀:write("ABCD")
- assert(p₁:read() == "ABCD")
- p₀:close(); p₁:close()
-end
-
--- wstat
-do
- local name = tmp()
- assert(p9.create(name, nil, "644")):close()
- assert(p9.wstat(name, {name = "notyourbusiness", mode = "append 777"}))
- local st = assert(p9.stat("/tmp/notyourbusiness"))
- assert(st.mode.file and st.mode.append and st.mode.perm == "rwxrwxrwx")
-end
-
--- Filesystem
-do
- -- Create a test tree
- local function File(data) return {
- type = "file", perm = "644", data = data
- } end
- local function Dir(children) return {
- type = "dir", perm = "dir 755", children = children
- } end
- local function mkfs(path, d)
- assert(p9.create(path, nil, d.perm))
- for name, c in pairs(d.children) do
- local new = path .. "/" .. name
- if c.type == "dir" then
- mkfs(new, c)
- else
- local f <close> = assert(p9.create(new, "w", c.perm))
- f:write(c.data)
- end
- end
- end
- local fs = Dir {
- a = File "a",
- b = Dir {},
- c = Dir {
- ca = File "ca",
- cb = Dir {
- cba = File "cba",
- },
- cc = File "cc",
- },
- d = File "d",
- }
- local function walk(fs)
- dump(fs.perm)
- for _, e in pairs(fs.children) do
- if e.type == "dir" then
- walk(e)
- end
- end
- end
- local ok, err = pcall(mkfs, "/tmp/fs", fs)
- if not ok then print(err) end
-
- -- Stat a file
- assert(p9.stat("/tmp/fs/a").mode.file)
-
- -- Walking
- -- Walking a file (or any other error) must report an error
- local e = {}
- for w in p9.walk("/tmp/fs/a", e) do
- assert(false)
- end
- assert(e.error == "walk in a non-directory")
- -- Without error object an error must be thrown
- assert(false == pcall(function()
- for w in p9.walk("tmp/fs/a") do end
- end))
- -- Same should happen if the iterator function fails inside
- -- the loop because of dirread(2) failure, but this kind of
- -- failure is hard to simulate.
-
- -- Walking a directory
- local function compare(path, fs)
- assert(fs.type == "dir")
- for f in p9.walk(path) do
- local new = path .. "/" .. f.name
- if f.mode.dir then
- if compare(new, fs.children[f.name]) == false then
- return false
- end
- else
- if fs.children[f.name] == nil then
- error("file does not exist in proto")
- end
- end
- end
- return true
- end
- assert(compare("/tmp/fs", fs) == true)
-end
-
-
-
--- Namespaces
--- bind and unmount work
-do
- local f
- assert(p9.bind("#|", "/n/pipe"))
- f = assert(p9.open("/n/pipe/data"))
- assert(p9.unmount("/n/pipe"))
- assert(p9.open("/n/pipe/data") == nil)
-end
--- mount works
-do
- assert(p9.mount("/srv/cwfs", "/n/test"))
- assert(p9.open("/n/test/lib/glass"))
-end
-
--- Process control
--- No idea how to test some of this
-do
- assert(p9.sleep(0) == true)
-end
-
--- wdir
-do
- local cwd = assert(p9.wdir())
- assert(p9.wdir("/dev") and p9.wdir() == "/dev")
- assert(p9.wdir(cwd))
-end
-
--- proc info
-do
- assert(p9.user() and p9.sysname())
- assert(p9.pid() and p9.ppid())
-end
-
--- Fork & Exec
-do
- local us, them = p9.pipe()
- if p9.rfork("proc nowait fd") == 0 then
- them:dup(p9.file(0))
- them:dup(p9.file(1))
- them:close()
- p9.exec("cat")
- else
- them:close()
- us:write("HELLO CAT")
- us:write("")
- assert(us:slurp() == "HELLO CAT")
- us:close()
- end
-
- local ok = p9.exec("/dev/mordor")
- assert(not ok)
-
- -- Wait
- local N = 9
- for i = 1, N do
- if p9.rfork("proc") == 0 then
- p9.exec("sleep", "0")
- end
- end
- for i = 1, N do
- local ok, w = p9.wait()
- end
- local ok = p9.wait()
- assert(not ok)
- -- Wait (status)
- if p9.rfork("proc") == 0 then
- p9.sleep(0)
- p9.exits("i failed you")
- end
- local ok, w = p9.wait()
- assert(not ok and w.status:match("i failed you"))
-end
-
-
-
-
--- Environment variables
-do
- local e
-
- assert(p9.env["sure-is-empty"] == nil)
- -- Set and get a string variable
- p9.env.test = "ABC"; assert(p9.env.test == "ABC")
- -- Delete a variable
- p9.env.test = nil; assert(p9.env.test == nil)
-
- -- Set and get a list variable
- p9.env.test = {"a", "b", "c"}
- e = p9.env.test
- assert(type(e) == "table"
- and #e == 3 and e[1] == "a" and e[2] == "b" and e[3] == "c")
- -- Ensure it's understood by rc
- os.execute("echo -n $#test $test >/env/res")
- assert(p9.env.res == "3 a b c")
- -- Ensure we understand rc
- os.execute("test=(d e f)")
- e = p9.env.test
- assert(type(e) == "table"
- and #e == 3 and e[1] == "d" and e[2] == "e" and e[3] == "f")
- p9.env.test = nil
-end
-
-
-
-
--- Misc.
-
--- cleanname
-do
- assert(p9.cleanname("/usr///./glenda/.") == "/usr/glenda")
-end
--- /dev/null
+++ b/test/test.lua
@@ -1,0 +1,322 @@
+#!/bin/lu9
+
+local p9 = require "p9"
+local dump = (function()
+ local ok, inspect = pcall(require, "inspect")
+ if ok then return function(v) print(inspect(v)) end end
+ return print
+end)()
+
+local function tmp()
+ return string.format("/tmp/lua.%x", math.random(1e10))
+end
+
+local function rc()
+ os.execute("prompt='p9; ' rc -i")
+end
+
+p9.rfork("env name")
+os.execute("ramfs")
+
+
+
+-- File I/O
+do
+ local s = string.rep("ABCD", 2048*2) -- 16k > standard 8k buffer
+ local f = tmp()
+ local fd = p9.create(f, "w")
+ fd:write(s)
+ fd:close()
+
+ fd = p9.open(f)
+ assert(fd:slurp() == s)
+ fd:close()
+
+ fd = p9.open(f, "r")
+ assert(fd:slurp(2048) == string.rep("ABCD", 512))
+ fd:close()
+
+ fd = p9.open(f, "r")
+ assert(fd:slurp(16*1024 + 999) == s)
+ fd:close()
+
+ fd = p9.open(f, "r")
+ assert(fd:seek(0, "end") == 16*1024)
+ assert(fd:seek(8192, "set") == 8192
+ and fd:slurp() == string.rep("ABCD", 2*1024))
+ fd:seek(0)
+ assert(fd:seek(16*1024 - 4, "cur") == 16*1024 - 4
+ and fd:slurp() == "ABCD")
+ fd:close()
+end
+
+-- File objects
+-- Closing
+-- Make sure it's closed
+local fd
+do
+ local f <close> = p9.create(tmp())
+ fd = f.fd
+end
+assert(p9.file(fd):close() == nil)
+-- Make sure it's not closed
+local fd
+do
+ local f = p9.create(tmp())
+ fd = f.fd
+end
+assert(p9.file(fd):seek(0))
+p9.file(fd):close()
+
+-- file:path()
+do
+ local f = p9.create("/tmp/fd2path")
+ assert(f:path() == "/tmp/fd2path")
+ f:close()
+end
+
+-- file:iounit()
+do
+ local f = assert(p9.open("/srv/slashmnt"))
+ assert(f:iounit() ~= 0)
+ f:close()
+end
+
+-- file:dup()
+do
+ local a, b = assert(p9.pipe())
+ local c = assert(a:dup())
+ a:write("hello")
+ assert(b:read() == "hello")
+ c:write("world")
+ assert(b:read() == "world")
+ a:close() b:close() c:close()
+
+ a = assert(p9.open("/lib/glass"))
+ local buf = a:read()
+ b = assert(p9.open("/lib/bullshit"))
+ b = assert(a:dup(b))
+ b:seek(0)
+ assert(b:read() == buf)
+end
+
+-- access
+do
+ assert(p9.access("/dev/null"))
+ assert(p9.access("/dev/null", "read write"))
+ assert(p9.access("/bin/rc", "exec"))
+end
+
+-- pipe
+do
+ local p₀, p₁ = assert(p9.pipe())
+ p₀:write("ABCD")
+ assert(p₁:read() == "ABCD")
+ p₀:close(); p₁:close()
+end
+
+-- wstat
+do
+ local name = tmp()
+ assert(p9.create(name, nil, "644")):close()
+ assert(p9.wstat(name, {name = "notyourbusiness", mode = "append 777"}))
+ local st = assert(p9.stat("/tmp/notyourbusiness"))
+ assert(st.mode.file and st.mode.append and st.mode.perm == "rwxrwxrwx")
+end
+
+-- Filesystem
+do
+ -- Create a test tree
+ local function File(data) return {
+ type = "file", perm = "644", data = data
+ } end
+ local function Dir(children) return {
+ type = "dir", perm = "dir 755", children = children
+ } end
+ local function mkfs(path, d)
+ assert(p9.create(path, nil, d.perm))
+ for name, c in pairs(d.children) do
+ local new = path .. "/" .. name
+ if c.type == "dir" then
+ mkfs(new, c)
+ else
+ local f <close> = assert(p9.create(new, "w", c.perm))
+ f:write(c.data)
+ end
+ end
+ end
+ local fs = Dir {
+ a = File "a",
+ b = Dir {},
+ c = Dir {
+ ca = File "ca",
+ cb = Dir {
+ cba = File "cba",
+ },
+ cc = File "cc",
+ },
+ d = File "d",
+ }
+ local function walk(fs)
+ dump(fs.perm)
+ for _, e in pairs(fs.children) do
+ if e.type == "dir" then
+ walk(e)
+ end
+ end
+ end
+ local ok, err = pcall(mkfs, "/tmp/fs", fs)
+ if not ok then print(err) end
+
+ -- Stat a file
+ assert(p9.stat("/tmp/fs/a").mode.file)
+
+ -- Walking
+ -- Walking a file (or any other error) must report an error
+ local e = {}
+ for w in p9.walk("/tmp/fs/a", e) do
+ assert(false)
+ end
+ assert(e.error == "walk in a non-directory")
+ -- Without error object an error must be thrown
+ assert(false == pcall(function()
+ for w in p9.walk("tmp/fs/a") do end
+ end))
+ -- Same should happen if the iterator function fails inside
+ -- the loop because of dirread(2) failure, but this kind of
+ -- failure is hard to simulate.
+
+ -- Walking a directory
+ local function compare(path, fs)
+ assert(fs.type == "dir")
+ for f in p9.walk(path) do
+ local new = path .. "/" .. f.name
+ if f.mode.dir then
+ if compare(new, fs.children[f.name]) == false then
+ return false
+ end
+ else
+ if fs.children[f.name] == nil then
+ error("file does not exist in proto")
+ end
+ end
+ end
+ return true
+ end
+ assert(compare("/tmp/fs", fs) == true)
+end
+
+
+
+-- Namespaces
+-- bind and unmount work
+do
+ local f
+ assert(p9.bind("#|", "/n/pipe"))
+ f = assert(p9.open("/n/pipe/data"))
+ assert(p9.unmount("/n/pipe"))
+ assert(p9.open("/n/pipe/data") == nil)
+end
+-- mount works
+do
+ assert(p9.mount("/srv/cwfs", "/n/test"))
+ assert(p9.open("/n/test/lib/glass"))
+end
+
+-- Process control
+-- No idea how to test some of this
+do
+ assert(p9.sleep(0) == true)
+end
+
+-- wdir
+do
+ local cwd = assert(p9.wdir())
+ assert(p9.wdir("/dev") and p9.wdir() == "/dev")
+ assert(p9.wdir(cwd))
+end
+
+-- proc info
+do
+ assert(p9.user() and p9.sysname())
+ assert(p9.pid() and p9.ppid())
+end
+
+-- Fork & Exec
+do
+ local us, them = p9.pipe()
+ if p9.rfork("proc nowait fd") == 0 then
+ them:dup(p9.file(0))
+ them:dup(p9.file(1))
+ them:close()
+ p9.exec("cat")
+ else
+ them:close()
+ us:write("HELLO CAT")
+ us:write("")
+ assert(us:slurp() == "HELLO CAT")
+ us:close()
+ end
+
+ local ok = p9.exec("/dev/mordor")
+ assert(not ok)
+
+ -- Wait
+ local N = 9
+ for i = 1, N do
+ if p9.rfork("proc") == 0 then
+ p9.exec("sleep", "0")
+ end
+ end
+ for i = 1, N do
+ local ok, w = p9.wait()
+ end
+ local ok = p9.wait()
+ assert(not ok)
+ -- Wait (status)
+ if p9.rfork("proc") == 0 then
+ p9.sleep(0)
+ p9.exits("i failed you")
+ end
+ local ok, w = p9.wait()
+ assert(not ok and w.status:match("i failed you"))
+end
+
+
+
+
+-- Environment variables
+do
+ local e
+
+ assert(p9.env["sure-is-empty"] == nil)
+ -- Set and get a string variable
+ p9.env.test = "ABC"; assert(p9.env.test == "ABC")
+ -- Delete a variable
+ p9.env.test = nil; assert(p9.env.test == nil)
+
+ -- Set and get a list variable
+ p9.env.test = {"a", "b", "c"}
+ e = p9.env.test
+ assert(type(e) == "table"
+ and #e == 3 and e[1] == "a" and e[2] == "b" and e[3] == "c")
+ -- Ensure it's understood by rc
+ os.execute("echo -n $#test $test >/env/res")
+ assert(p9.env.res == "3 a b c")
+ -- Ensure we understand rc
+ os.execute("test=(d e f)")
+ e = p9.env.test
+ assert(type(e) == "table"
+ and #e == 3 and e[1] == "d" and e[2] == "e" and e[3] == "f")
+ p9.env.test = nil
+end
+
+
+
+
+-- Misc.
+
+-- cleanname
+do
+ assert(p9.cleanname("/usr///./glenda/.") == "/usr/glenda")
+end
--- a/walk.c
+++ /dev/null
@@ -1,231 +1,0 @@
-static char*
-perms(int p, char *buf)
-{
- buf[0] = p & 04 ? 'r' : '-';
- buf[1] = p & 02 ? 'w' : '-';
- buf[2] = p & 01 ? 'x' : '-';
- return buf;
-}
-
-static void
-createdirtable(lua_State *L, Dir *d)
-{
- #define set(t, k, v) do { \
- lua_pushstring(L, (k)); \
- lua_push##t(L, (v)); \
- lua_rawset(L, -3); \
- } while(0)
-
- lua_createtable(L, 0, 11);
- set(integer, "type", d->type);
- set(integer, "dev", d->dev);
- set(integer, "atime", d->atime);
- set(integer, "mtime", d->mtime);
- set(integer, "length", d->length);
- set(string, "name", d->name);
- set(string, "uid", d->uid);
- set(string, "gid", d->gid);
- set(string, "muid", d->muid);
-
- lua_pushstring(L, "qid");
- lua_createtable(L, 0, 3);
- set(integer, "path", d->qid.path);
- set(integer, "vers", d->qid.vers);
- set(integer, "type", d->qid.type);
- lua_rawset(L, -3);
-
- lua_pushstring(L, "mode");
- lua_createtable(L, 0, 7);
- ulong m = d->mode;
- set(integer, "raw", m);
- if(m & DMDIR)
- set(boolean, "dir", 1);
- else
- set(boolean, "file", 1);
- if(m & DMAPPEND)
- set(boolean, "append", 1);
- if(m & DMTMP)
- set(boolean, "tmp", 1);
- if(m & DMMOUNT)
- set(boolean, "mount", 1);
- if(m & DMAUTH)
- set(boolean, "auth", 1);
- char buf[10] = {0};
- set(string, "user", perms((m & 0700) >> 6, buf));
- set(string, "group", perms((m & 0070) >> 3, buf+3));
- set(string, "other", perms((m & 0007) >> 0, buf+6));
- set(string, "perm", buf);
- lua_rawset(L, -3);
-
- #undef set
-}
-
-static int
-p9_stat(lua_State *L)
-{
- Dir *d;
-
- d = nil;
- switch(lua_type(L, 1)){
- default:
- USED(d);
- return luaL_typeerror(L, 1, "string or number");
- case LUA_TSTRING:
- d = dirstat(lua_tostring(L, 1)); break;
- case LUA_TNUMBER:
- d = dirfstat(lua_tonumber(L, 1)); break;
- }
- if(d == nil)
- return error(L, "stat: %r");
- createdirtable(L, d);
- free(d);
- return 1;
-}
-
-typedef struct Walk {
- int fd;
- int nleft;
- Dir *dirs, *p;
-} Walk;
-
-static int
-p9_walk(lua_State *L)
-{
- static int p9_walkout(lua_State*);
- static int p9_walknext(lua_State*);
- int nargs, wstate;
- Dir *d;
- Walk *w;
-
- nargs = lua_gettop(L);
- w = lua_newuserdatauv(L, sizeof(Walk), 1);
- wstate = nargs + 1;
- w->fd = -1;
- w->nleft = 0;
- w->dirs = w->p = nil;
- luaL_setmetatable(L, "p9-Walk");
- if(nargs == 2){
- lua_pushvalue(L, 2);
- lua_setiuservalue(L, wstate, 1);
- }
- if(lua_isnumber(L, 1))
- w->fd = lua_tointeger(L, 1);
- else{
- if((w->fd = open(luaL_checkstring(L, 1), OREAD|OCEXEC)) == -1){
- error(L, "open: %r");
- goto Error;
- }
- }
- if((d = dirfstat(w->fd)) == nil){
- error(L, "stat: %r");
- goto Error;
- }
- int isdir = d->mode & DMDIR;
- free(d);
- if(!isdir){
- error(L, "walk in a non-directory");
- goto Error;
- }
- /* return p9_walknext, p9-Walk, nil, p9-Walk */
- lua_pushcfunction(L, p9_walknext);
- lua_pushvalue(L, wstate);
- lua_pushnil(L);
- lua_pushvalue(L, wstate);
- return 4;
-Error:
- if(nargs == 2){
- lua_setfield(L, 2, "error");
- lua_pushcfunction(L, p9_walkout);
- return 1;
- }
- return lua_error(L);
-}
-
-static int
-p9_walkout(lua_State*)
-{
- return 0;
-}
-
-static int
-p9_walknext(lua_State *L)
-{
- Walk *w;
- Dir *d;
-
- w = luaL_checkudata(L, 1, "p9-Walk");
- if(w->nleft == 0){
- if(w->dirs != nil){
- free(w->dirs);
- w->dirs = nil;
- }
- if((w->nleft = dirread(w->fd, &w->dirs)) == -1){
- error(L, "dirread: %r");
- goto Error;
- }
- w->p = w->dirs;
- if(w->nleft == 0)
- return 0; /* Last Walk state will be closed */
- }
- w->nleft--;
- d = w->p++;
- createdirtable(L, d);
- return 1;
-Error:
- if(lua_getiuservalue(L, 1, 1) == LUA_TTABLE){
- lua_pushvalue(L, -2);
- lua_setfield(L, -2, "error");
- lua_pushnil(L);
- return 1;
- }
- lua_pop(L, 1);
- return 2;
-}
-
-static int
-p9_walkclose(lua_State *L)
-{
- Walk *w;
-
- w = luaL_checkudata(L, 1, "p9-Walk");
- if(w->dirs != nil){
- free(w->dirs);
- w->dirs = nil;
- }
- if(w->fd != -1){
- close(w->fd);
- w->fd = -1;
- }
- return 0;
-}
-
-static int
-p9_wstat(lua_State *L)
-{
- static int createperm(lua_State*, char*); /* from fs.c */
- const char *path, *k;
- Dir new;
-
- path = luaL_checkstring(L, 1);
- luaL_argexpected(L, lua_type(L, 2) == LUA_TTABLE, 2, "table");
- nulldir(&new);
- lua_pushnil(L);
- while(lua_next(L, -2)){
- k = lua_tostring(L, -2);
- if(strcmp(k, "name") == 0)
- new.name = (char*)lua_tostring(L, -1); /* dw */
- else if(strcmp(k, "mode") == 0)
- new.mode = createperm(L, lua_tostring(L, -1));
- else if(strcmp(k, "mtime") == 0)
- new.mtime = lua_tointeger(L, -1);
- else if(strcmp(k, "gid") == 0)
- new.gid = (char*)lua_tostring(L, -1);
- else if(strcmp(k, "length") == 0)
- new.length = lua_tointeger(L, -1);
- lua_pop(L, 1);
- }
- if(dirwstat(path, &new) == -1)
- return error(L, "wstat: %r");
- lua_pushboolean(L, 1);
- return 1;
-}