Make improvements

- Add hierarchical auto-completion to redbean's repl
- Fetch latest localtime() and strftime() from Eggert
- Shave a few milliseconds off redbean start latency
- Fix redbean repl with multi-line statements
- Make the Lua unix module code more elegant
- Harden Lua data structure serialization
This commit is contained in:
Justine Tunney 2022-04-27 05:39:39 -07:00
parent d57b81aac7
commit 6a145a9262
44 changed files with 2987 additions and 1941 deletions

View file

@ -97,7 +97,7 @@ o/$(MODE)/tool/build/blinkenlights.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/build/.blinkenlights/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/build/.blinkenlights/.symtab
o/$(MODE)/tool/build/ar.com.dbg: \

View file

@ -585,7 +585,6 @@ FUNCTIONS
ignored if used outside of request handling code.
- numformat: sets numeric format to be used, which can be 'g',
'f', or 'a' [experimental api]
- maxdepth: (number=64) sets the max number of nested tables.
EncodeLua(value[,options:table]) → json:str
Turns passed Lua value into a Lua string. The following options
@ -593,7 +592,6 @@ FUNCTIONS
- useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is
ignored if used outside of request handling code.
- maxdepth: (number=64) sets the max number of nested tables.
EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str
Turns UTF-8 into ISO-8859-1 string.
@ -1421,11 +1419,12 @@ UNIX MODULE
The following values may also be OR'd into `flags`:
- `O_CREAT`: create file if it doesn't exist
- `O_CREAT` create file if it doesn't exist
- `O_TRUNC` automatic ftruncate(fd,0) if exists
- `O_CLOEXEC`: automatic close() upon execve()
- `O_EXCL`: exclusive access (see below)
- `O_APPEND`: open file for append only
- `O_CLOEXEC` automatic close() upon execve()
- `O_EXCL` exclusive access (see below)
- `O_APPEND` open file for append only
- `O_NONBLOCK` asks read/write to fail with EAGAIN rather than block
- `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
- `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
- `O_TMPFILE` try to make temp more secure (Linux and Windows only)
@ -1615,7 +1614,7 @@ UNIX MODULE
- `O_CLOEXEC`: Automatically close file descriptor upon execve()
- `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking.
- `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking
- `O_DIRECT`: Enable packet mode w/ atomic reads and writes, so long
as they're no larger than `PIPE_BUF` (guaranteed to be 512+ bytes)
@ -2634,6 +2633,52 @@ UNIX MODULE
Returns information about resource limit.
unix.gmtime(unixts:int)
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
└─→ nil,unix.Errno
Breaks down UNIX timestamp into Zulu Time numbers.
- `mon` 1 ≤ mon ≤ 12
- `mday` 1 ≤ mday ≤ 31
- `hour` 0 ≤ hour ≤ 23
- `min` 0 ≤ min ≤ 59
- `sec` 0 ≤ sec ≤ 60
- `gmtoff` ±93600 seconds
- `wday` 0 ≤ wday ≤ 6
- `yday` 0 ≤ yday ≤ 365
- `dst` 1 if daylight savings, 0 if not, -1 if not unknown
unix.localtime(unixts:int)
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
└─→ nil,unix.Errno
Breaks down UNIX timestamp into local time numbers.
This follows the same API as gmtime() which has further details.
Your redbean ships with a subset of the time zone database.
- `/zip/usr/share/zoneinfo/Honolulu`
- `/zip/usr/share/zoneinfo/Anchorage`
- `/zip/usr/share/zoneinfo/GST`
- `/zip/usr/share/zoneinfo/Boulder`
- `/zip/usr/share/zoneinfo/Chicago`
- `/zip/usr/share/zoneinfo/New_York`
- `/zip/usr/share/zoneinfo/UTC`
- `/zip/usr/share/zoneinfo/London`
- `/zip/usr/share/zoneinfo/Berlin`
- `/zip/usr/share/zoneinfo/Israel`
- `/zip/usr/share/zoneinfo/Beijing`
- `/zip/usr/share/zoneinfo/Japan`
- `/zip/usr/share/zoneinfo/Sydney`
You can control which timezone is used using the `TZ` environment
variable. If your time zone isn't included in the above list, you
can simply copy it inside your redbean. The same is also the case
for future updates to the database, which can be swapped out when
needed, without having to recompile.
unix.stat(path:str[, flags:int[, dirfd:int]])
├─→ unix.Stat
└─→ nil, unix.Errno
@ -2847,7 +2892,7 @@ UNIX MODULE
actually consumes. For example, for small file systems, your system
might report this number as being 8, which means 4096 bytes.
On Windows NT, if compression is enabled for a file, then this
On Windows NT, if `O_COMPRESSED` is used for a file, then this
number will reflect the size *after* compression. you can use:
st = assert(unix.stat("moby.txt"))

View file

@ -98,15 +98,9 @@ struct UnixDir {
DIR *dir;
};
struct UnixStat {
int refs;
struct stat st;
};
struct UnixErrno {
int refs;
uint16_t errno;
uint16_t winerr;
int errno;
int winerr;
const char *call;
};
@ -137,12 +131,17 @@ static void *LuaUnixAlloc(lua_State *L, size_t n) {
}
static void LuaPushSigset(lua_State *L, struct sigset set) {
struct sigset *sp;
sp = lua_newuserdatauv(L, sizeof(*sp), 1);
struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1);
luaL_setmetatable(L, "unix.Sigset");
*sp = set;
}
static void LuaPushStat(lua_State *L, struct stat *st) {
struct stat *stp = lua_newuserdatauv(L, sizeof(*stp), 1);
luaL_setmetatable(L, "unix.Stat");
*stp = *st;
}
static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, k);
@ -163,19 +162,8 @@ static dontinline int ReturnString(lua_State *L, const char *x) {
return 1;
}
static void LuaUnixPushErrno(lua_State *L, const char *sc, int uerr, int werr) {
struct UnixErrno *ue, **uep;
ue = LuaUnixAlloc(L, sizeof(*ue));
ue->refs = 1;
ue->call = sc;
ue->errno = uerr;
ue->winerr = werr;
uep = lua_newuserdatauv(L, sizeof(*uep), 1);
luaL_setmetatable(L, "unix.Errno");
*uep = ue;
}
static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) {
static int SysretErrno(lua_State *L, const char *call, int olderr) {
struct UnixErrno *ep;
int i, unixerr, winerr;
unixerr = errno;
winerr = GetLastError();
@ -183,7 +171,11 @@ static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) {
WARNF("errno should not be %d", unixerr);
}
lua_pushnil(L);
LuaUnixPushErrno(L, call, unixerr, winerr);
ep = lua_newuserdatauv(L, sizeof(*ep), 1);
luaL_setmetatable(L, "unix.Errno");
ep->errno = unixerr;
ep->winerr = winerr;
ep->call = call;
errno = olderr;
return 2;
}
@ -936,49 +928,33 @@ static int LuaUnixWrite(lua_State *L) {
return SysretInteger(L, "write", olderr, rc);
}
static int ReturnStat(lua_State *L, struct UnixStat *ust) {
struct UnixStat **ustp;
ust->refs = 1;
ustp = lua_newuserdatauv(L, sizeof(*ustp), 1);
luaL_setmetatable(L, "unix.Stat");
*ustp = ust;
return 1;
}
// unix.stat(path:str[, flags:int[, dirfd:int]])
// ├─→ unix.Stat
// └─→ nil, unix.Errno
static int LuaUnixStat(lua_State *L) {
const char *path;
struct UnixStat *ust;
int dirfd, flags, olderr = errno;
path = luaL_checkstring(L, 1);
flags = luaL_optinteger(L, 2, 0);
dirfd = luaL_optinteger(L, 3, AT_FDCWD);
if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) {
if (!fstatat(dirfd, path, &ust->st, flags)) {
return ReturnStat(L, ust);
}
free(ust);
struct stat st;
int olderr = errno;
if (!fstatat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), &st,
luaL_optinteger(L, 2, 0))) {
LuaPushStat(L, &st);
return 1;
} else {
return SysretErrno(L, "stat", olderr);
}
return SysretErrno(L, "stat", olderr);
}
// unix.fstat(fd:int)
// ├─→ unix.Stat
// └─→ nil, unix.Errno
static int LuaUnixFstat(lua_State *L) {
int fd, olderr = errno;
struct UnixStat *ust;
olderr = errno;
fd = luaL_checkinteger(L, 1);
if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) {
if (!fstat(fd, &ust->st)) {
return ReturnStat(L, ust);
}
free(ust);
struct stat st;
int olderr = errno;
if (!fstat(luaL_checkinteger(L, 1), &st)) {
LuaPushStat(L, &st);
return 1;
} else {
return SysretErrno(L, "fstat", olderr);
}
return SysretErrno(L, "fstat", olderr);
}
static bool IsSockoptBool(int l, int x) {
@ -1746,9 +1722,7 @@ static int LuaUnixMinor(lua_State *L) {
// unix.Stat object
static dontinline struct stat *GetUnixStat(lua_State *L) {
struct UnixStat **ust;
ust = luaL_checkudata(L, 1, "unix.Stat");
return &(*ust)->st;
return luaL_checkudata(L, 1, "unix.Stat");
}
// unix.Stat:size()
@ -1853,28 +1827,12 @@ static int LuaUnixStatFlags(lua_State *L) {
return ReturnInteger(L, GetUnixStat(L)->st_flags);
}
static void FreeUnixStat(struct UnixStat *stat) {
if (!--stat->refs) {
free(stat);
}
}
static int LuaUnixStatToString(lua_State *L) {
struct stat *st = GetUnixStat(L);
lua_pushstring(L, "unix.Stat{}");
return 1;
}
static int LuaUnixStatGc(lua_State *L) {
struct UnixStat **ust;
ust = luaL_checkudata(L, 1, "unix.Stat");
if (*ust) {
FreeUnixStat(*ust);
*ust = 0;
}
return 0;
}
static const luaL_Reg kLuaUnixStatMeth[] = {
{"atim", LuaUnixStatAtim}, //
{"birthtim", LuaUnixStatBirthtim}, //
@ -1897,7 +1855,6 @@ static const luaL_Reg kLuaUnixStatMeth[] = {
static const luaL_Reg kLuaUnixStatMeta[] = {
{"__tostring", LuaUnixStatToString}, //
{"__gc", LuaUnixStatGc}, //
{0}, //
};
@ -1913,10 +1870,8 @@ static void LuaUnixStatObj(lua_State *L) {
////////////////////////////////////////////////////////////////////////////////
// unix.Errno object
static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) {
struct UnixErrno **ep;
ep = luaL_checkudata(L, 1, "unix.Errno");
return *ep;
static struct UnixErrno *GetUnixErrno(lua_State *L) {
return luaL_checkudata(L, 1, "unix.Errno");
}
static int LuaUnixErrnoErrno(lua_State *L) {
@ -1948,22 +1903,6 @@ static int LuaUnixErrnoToString(lua_State *L) {
return 1;
}
static void FreeUnixErrno(struct UnixErrno *errno) {
if (!--errno->refs) {
free(errno);
}
}
static int LuaUnixErrnoGc(lua_State *L) {
struct UnixErrno **ue;
ue = luaL_checkudata(L, 1, "unix.Errno");
if (*ue) {
FreeUnixErrno(*ue);
*ue = 0;
}
return 0;
}
static const luaL_Reg kLuaUnixErrnoMeth[] = {
{"strerror", LuaUnixErrnoToString}, //
{"errno", LuaUnixErrnoErrno}, //
@ -1975,7 +1914,6 @@ static const luaL_Reg kLuaUnixErrnoMeth[] = {
static const luaL_Reg kLuaUnixErrnoMeta[] = {
{"__tostring", LuaUnixErrnoToString}, //
{"__gc", LuaUnixErrnoGc}, //
{0}, //
};

View file

@ -117,9 +117,10 @@ o/$(MODE)/tool/net/redbean.com: \
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean/.ape \
o/$(MODE)/tool/net/.redbean/.symtab \
tool/net/help.txt \
tool/net/.init.lua \
tool/net/favicon.ico \
@ -262,9 +263,10 @@ o/$(MODE)/tool/net/redbean-demo.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-demo/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-demo/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-demo/.ape \
o/$(MODE)/tool/net/.redbean-demo/.symtab \
tool/net/help.txt
# REDBEAN-STATIC.COM
@ -284,9 +286,10 @@ o/$(MODE)/tool/net/redbean-static.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-static/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-static/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-static/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-static/.ape \
o/$(MODE)/tool/net/.redbean-static/.symtab \
tool/net/help.txt \
tool/net/favicon.ico \
tool/net/redbean.png
@ -320,9 +323,10 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-unsecure/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-unsecure/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-unsecure/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-unsecure/.ape \
o/$(MODE)/tool/net/.redbean-unsecure/.symtab \
tool/net/help.txt \
tool/net/favicon.ico \
tool/net/redbean.png
@ -363,9 +367,10 @@ o/$(MODE)/tool/net/redbean-original.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-original/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-original/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-original/.ape \
o/$(MODE)/tool/net/.redbean-original/.symtab \
tool/net/help.txt \
tool/net/favicon.ico \
tool/net/redbean.png
@ -436,6 +441,8 @@ o/$(MODE)/tool/net/redbean-assimilate.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-assimilate
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean-assimilate/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-assimilate/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-assimilate/.symtab \
tool/net/help.txt \

View file

@ -4120,7 +4120,7 @@ static int LuaLog(lua_State *L) {
}
static int LuaEncodeSmth(lua_State *L,
int Encoder(lua_State *, char **, int, char *, int)) {
int Encoder(lua_State *, char **, char *, int)) {
int useoutput = false;
int maxdepth = 64;
char *numformat = "%.14g";
@ -4129,15 +4129,14 @@ static int LuaEncodeSmth(lua_State *L,
lua_settop(L, 2); // discard any extra arguments
lua_getfield(L, 2, "useoutput");
// ignore useoutput outside of request handling
if (ishandlingrequest && lua_isboolean(L, -1))
if (ishandlingrequest && lua_isboolean(L, -1)) {
useoutput = lua_toboolean(L, -1);
lua_getfield(L, 2, "maxdepth");
maxdepth = luaL_optinteger(L, -1, maxdepth);
}
lua_getfield(L, 2, "numformat");
numformat = luaL_optstring(L, -1, numformat);
}
lua_settop(L, 1); // keep the passed argument on top
Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1);
Encoder(L, useoutput ? &outbuf : &p, numformat, -1);
if (useoutput) {
lua_pushnil(L);
} else {
@ -5185,7 +5184,7 @@ static void LuaPrint(lua_State *L) {
if (n > 0) {
for (i = 1; i <= n; i++) {
if (i > 1) appendw(&b, '\t');
LuaEncodeLuaData(L, &b, 64, "g", i);
LuaEncodeLuaData(L, &b, "g", i);
}
appendw(&b, '\n');
WRITE(1, b, appendz(b).i);
@ -5222,7 +5221,6 @@ static void LuaInterpreter(lua_State *L) {
}
for (;;) {
status = lua_loadline(L);
write(1, "\n", 1);
if (status == -1) break; // eof
if (status == -2) {
if (errno == EINTR) {
@ -6502,7 +6500,6 @@ static int HandleReadline(void) {
if (status == -1) {
OnTerm(SIGHUP); // eof
INFOF("got repl eof");
write(1, "\n", 1);
return -1;
} else if (errno == EINTR) {
errno = 0;
@ -6516,7 +6513,6 @@ static int HandleReadline(void) {
return -1;
}
}
write(1, "\n", 1);
linenoiseDisableRawMode();
LUA_REPL_LOCK;
if (status == LUA_OK) {

View file

@ -55,7 +55,7 @@ o/$(MODE)/tool/plinko/plinko.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/plinko/.redbean
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/plinko/.plinko/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/plinko/.plinko/.symtab
$(TOOL_PLINKO_OBJS): \

View file

@ -88,7 +88,7 @@ o/$(MODE)/tool/viz/printimage.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/viz/.printimage/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/viz/.printimage/.symtab
o/$(MODE)/tool/viz/printvideo.com: \
@ -98,7 +98,7 @@ o/$(MODE)/tool/viz/printvideo.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/viz/.printvideo/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/viz/.printvideo/.symtab
o/$(MODE)/tool/viz/derasterize.o: \