ref: 2dece7c3384b4e3b0dbd1b25879c053209072e92
dir: /los9lib.c/
#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;
}