ref: 2dece7c3384b4e3b0dbd1b25879c053209072e92
parent: 9aca69bafe538dfa4f8d37a1088d6e78380cbc0b
author: kvik <kvik@a-b.xyz>
date: Sat Feb 6 19:18:07 EST 2021
os9lib: implement native, mostly compatible 'os' library
--- /dev/null
+++ b/los9lib.c
@@ -1,0 +1,387 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+static void
+dumpstack(lua_State *L)
+{
+ for(int i = 1; i <= lua_gettop(L); i++){
+ print("%d: %s\n", i, luaL_tolstring(L, i, nil));
+ lua_pop(L, 1);
+ }
+}
+
+static int
+os_exit(lua_State *L){
+ char *status;
+
+ status = nil;
+ switch(lua_type(L, 1)){
+ case LUA_TSTRING:
+ status = lua_tostring(L, 1);
+ break;
+ case LUA_TBOOLEAN:
+ status = lua_toboolean(L, 1) ? nil : "failure";
+ break;
+ case LUA_TNUMBER:
+ status = smprint("failure %d", lua_tointeger(L, 1));
+ break;
+ }
+ if(lua_toboolean(L, 2))
+ lua_close(L);
+ exits(status);
+ return 0;
+}
+
+static int
+os_execute(lua_State *L)
+{
+ char *cmd;
+ Waitmsg *w;
+
+ cmd = luaL_optstring(L, 1, nil);
+ if(cmd == nil){
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ switch(rfork(RFPROC|RFFDG|RFREND)){
+ case -1:
+ luaL_pushfail(L);
+ lua_pushstring(L, "exit");
+ lua_pushinteger(L, 999);
+ return 3;
+ case 0:
+ execl("/bin/rc", "rc", "-c", cmd, nil);
+ sysfatal("exec: %r");
+ }
+ w = wait();
+ if(w && w->msg[0] != 0){
+ luaL_pushfail(L);
+ if(strncmp(w->msg, "interrupt", 9) == 0
+ || strncmp(w->msg, "alarm", 5) == 0
+ || strncmp(w->msg, "hangup", 6) == 0)
+ lua_pushstring(L, "signal");
+ else
+ lua_pushstring(L, "exit");
+ lua_pushinteger(L, 999);
+ }else{
+ lua_pushboolean(L, 1);
+ lua_pushstring(L, "exit");
+ lua_pushinteger(L, 0);
+ }
+ return 3;
+}
+
+static int
+os_remove(lua_State *L){
+ char err[ERRMAX];
+ char *file;
+
+ file = luaL_checkstring(L, 1);
+ if(remove(file) == -1){
+ rerrstr(err, sizeof err);
+ luaL_pushfail(L);
+ lua_pushfstring(L, "%s", err);
+ lua_pushinteger(L, 999);
+ return 3;
+ }
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+static int
+os_rename(lua_State *L)
+{
+ char *from = luaL_checkstring(L, 1);
+ char *to = luaL_checkstring(L, 2);
+ char *cmd;
+ int ret;
+
+ /* This will have to do until mv(1) is
+ * basically reimplemented here.
+ * Diagnostic information is particularly
+ * lacking and could be improved by using
+ * the stderr output of mv(1) as an error
+ * message. */
+ cmd = smprint("return os.execute('/bin/mv %q %q >[2]/dev/null')", from, to);
+ ret = luaL_dostring(L, cmd);
+ free(cmd);
+ if(ret != LUA_OK){
+ luaL_pushfail(L);
+ lua_pushstring(L, "should never happen");
+ lua_pushinteger(L, 999);
+ return 3;
+ }
+ if(lua_toboolean(L, -3) == 0)
+ return 3;
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+static int
+os_tmpname(lua_State *L)
+{
+ int i, fd;
+ char buf[32];
+
+ for(i = 0; i < 3; i++){
+ snprint(buf, sizeof buf, "/tmp/lua.%.8lx", lrand());
+ if(access(buf, AEXIST) != 0)
+ goto OK;
+ }
+Error:
+ return luaL_error(L, "unable to generate a unique filename");
+OK:
+ if((fd = create(buf, OWRITE, 0600)) == -1)
+ goto Error;
+ close(fd);
+ lua_pushstring(L, buf);
+ return 1;
+}
+
+static int
+os_getenv(lua_State *L)
+{
+ char *v;
+
+ v = getenv(luaL_checkstring(L, 1));
+ lua_pushstring(L, v);
+ free(v);
+ return 1;
+}
+
+static int
+os_clock(lua_State *L)
+{
+ lua_pushnumber(L, (lua_Number)_tos->pcycles / _tos->cyclefreq);
+ return 1;
+}
+
+static void
+setfield(lua_State *L, const char *key, int value, int delta)
+{
+ lua_pushinteger(L, (lua_Integer)value + delta);
+ lua_setfield(L, -2, key);
+}
+
+static void
+setboolfield(lua_State *L, const char *key, int value)
+{
+ if(value < 0)
+ return;
+ lua_pushboolean(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static void
+setallfields(lua_State *L, Tm *tm)
+{
+ setfield(L, "year", tm->year, 1900);
+ setfield(L, "month", tm->mon, 1);
+ setfield(L, "day", tm->mday, 0);
+ setfield(L, "hour", tm->hour, 0);
+ setfield(L, "min", tm->min, 0);
+ setfield(L, "sec", tm->sec, 0);
+ setfield(L, "yday", tm->yday, 1);
+ setfield(L, "wday", tm->wday, 1);
+ setboolfield(L, "isdst", 0);
+}
+
+static int
+getfield(lua_State *L, const char *key, int d, int delta)
+{
+ int isnum;
+ int t = lua_getfield(L, -1, key);
+ lua_Integer res = lua_tointegerx(L, -1, &isnum);
+
+ if(!isnum){
+ if (t != LUA_TNIL)
+ return luaL_error(L, "field '%s' is not an integer", key);
+ else if (d < 0)
+ return luaL_error(L, "field '%s' missing in date table", key);
+ res = d;
+ }else{
+ /* unsigned avoids overflow when lua_Integer has 32 bits */
+ if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
+ : (lua_Integer)INT_MIN + delta <= res))
+ return luaL_error(L, "field '%s' is out-of-bound", key);
+ res -= delta;
+ }
+ lua_pop(L, 1);
+ return res;
+}
+
+static int
+os_time(lua_State *L)
+{
+ vlong t;
+ Tm tm;
+
+ if (lua_isnoneornil(L, 1))
+ t = time(nil);
+ else {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1);
+ tm.year = getfield(L, "year", -1, 1900);
+ tm.mon = getfield(L, "month", -1, 1);
+ tm.mday = getfield(L, "day", -1, 0);
+ tm.hour = getfield(L, "hour", 12, 0);
+ tm.min = getfield(L, "min", 0, 0);
+ tm.sec = getfield(L, "sec", 0, 0);
+ t = tmnorm(&tm);
+ setallfields(L, &tm);
+ }
+ lua_pushnumber(L, t);
+ return 1;
+}
+
+static vlong
+l_checktime(lua_State *L, int arg)
+{
+ vlong t = luaL_checkinteger(L, arg);
+ luaL_argcheck(L, (vlong)t == t, arg, "time out-of-bounds");
+ return t;
+}
+
+static int
+os_difftime(lua_State *L)
+{
+ vlong t1 = l_checktime(L, 1);
+ vlong t2 = l_checktime(L, 2);
+ lua_pushnumber(L, (lua_Number)(t1 - t2));
+ return 1;
+}
+
+static void
+l_fmttime(lua_State *L, char *fmt, Tm *tm)
+{
+ char *s, mod;
+ Fmt f;
+
+ fmtstrinit(&f);
+ for(s = fmt; *s; s++){
+ if(s[0] != '%'){
+ fmtprint(&f, "%c", s[0]);
+ continue;
+ }
+ s++;
+ if(s[0] == '%'){
+ fmtprint(&f, "%");
+ continue;
+ }
+ if(s[0] == 'E' || s[0] == '0'){
+ mod = s[0];
+ s++;
+ }
+ switch(s[0]){
+ case 'n': fmtprint(&f, "\n"); break;
+ case 't': fmtprint(&f, "\t"); break;
+
+ case 'y': fmtprint(&f, "%τ", tmfmt(tm, "YY")); break;
+ case 'Y': fmtprint(&f, "%τ", tmfmt(tm, "YYYY")); break;
+
+ case 'm': fmtprint(&f, "%τ", tmfmt(tm, "MM")); break;
+ case 'b': fmtprint(&f, "%τ", tmfmt(tm, "MMM")); break;
+ case 'B': fmtprint(&f, "%τ", tmfmt(tm, "MMMM")); break;
+
+ /* TODO: week of the year calculation */
+ case 'U': fmtprint(&f, "00"); break;
+ case 'W': fmtprint(&f, "01"); break;
+
+ case 'j': fmtprint(&f, "%.3d", tm->yday + 1); break;
+ case 'd': fmtprint(&f, "%.2d", tm->mday); break;
+
+ case 'w': fmtprint(&f, "%τ", tmfmt(tm, "W")); break;
+ case 'a': fmtprint(&f, "%τ", tmfmt(tm, "WW")); break;
+ case 'A': fmtprint(&f, "%τ", tmfmt(tm, "WWW")); break;
+
+ case 'H': fmtprint(&f, "%τ", tmfmt(tm, "hh")); break;
+ case 'I': fmtprint(&f, "%d", tm->hour % 12); break;
+
+ case 'M': fmtprint(&f, "%τ", tmfmt(tm, "mm")); break;
+
+ case 'S': fmtprint(&f, "%τ", tmfmt(tm, "ss")); break;
+
+ case 'c': case 'x': fmtprint(&f, "%τ", tmfmt(tm, nil)); break;
+ case 'X': fmtprint(&f, "%τ", tmfmt(tm, "hh[:]mm[:]ss")); break;
+
+ case 'p': fmtprint(&f, "%τ", tmfmt(tm, "a")); break;
+
+ case 'Z': fmtprint(&f, "%τ", tmfmt(tm, "Z")); break;
+
+ default:
+ fmtprint(&f, "%%");
+ if(mod)
+ fmtprint(&f, "%c", mod);
+ fmtprint(&f, "%c", s[0]);
+ break;
+ }
+ mod = 0;
+ }
+ s = fmtstrflush(&f);
+ lua_pushstring(L, s);
+ free(s);
+}
+
+static int
+os_date(lua_State *L)
+{
+ char *s;
+ vlong t;
+ Tm tm, *tp;
+
+ s = luaL_optstring(L, 1, "%c");
+ t = luaL_opt(L, l_checktime, 2, time(nil));
+ if(s[0] == '!'){
+ tp = gmtime(t);
+ s++;
+ }else
+ tp = localtime(t);
+ if(tp == nil)
+ return luaL_error(L, "date result cannot be represented in this installation");
+ if(strcmp(s, "*t") == 0){
+ lua_createtable(L, 0, 9);
+ setallfields(L, tp);
+ }else
+ l_fmttime(L, s, tp);
+ return 1;
+}
+
+static int
+os_setlocale(lua_State *L)
+{
+ if(lua_isnil(L, 1) || strcmp(luaL_checkstring(L, 1), "C") == 0)
+ lua_pushstring(L, "C");
+ else
+ luaL_pushfail(L);
+ return 1;
+}
+
+static const luaL_Reg syslib[] = {
+ {"execute", os_execute},
+ {"exit", os_exit},
+ {"getenv", os_getenv},
+ {"remove", os_remove},
+ {"rename", os_rename},
+ {"tmpname", os_tmpname},
+ {"clock", os_clock},
+ {"date", os_date},
+ {"time", os_time},
+ {"difftime", os_difftime},
+ {"setlocale", os_setlocale},
+ {nil, nil}
+};
+
+LUAMOD_API int luaopen_os (lua_State *L) {
+ quotefmtinstall();
+ tmfmtinstall();
+
+ srand(time(0));
+
+ luaL_newlib(L, syslib);
+ return 1;
+}
--- a/mkfile
+++ b/mkfile
@@ -33,6 +33,7 @@
liolib.$O\
lmathlib.$O\
# loslib.$O\
+ los9lib.$O\
ltablib.$O\
lstrlib.$O\
lutf8lib.$O\