From 451e3f73d9030e6efc86e5fd785b83866f5012a3 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 25 Apr 2022 08:30:14 -0700 Subject: [PATCH] Improve redbean - Improve serialization - Add Benchmark() API to redbean - Refactor UNIX API to be assert() friendly - Make the redbean Lua REPL print data structures - Fix recent regressions in linenoise reverse search - Add -i flag so redbean can be a language interpreter --- libc/calls/calls.h | 10 +- libc/calls/calls.mk | 1 + libc/calls/describeclockname.c | 38 + libc/calls/describeopenflags.greg.c | 41 + libc/calls/fstatat-sysv.c | 1 + libc/calls/getegid.c | 2 +- libc/calls/geteuid.c | 2 +- libc/calls/getuid.c | 4 +- libc/calls/kclocknames.S | 47 + libc/calls/kopenflags.S | 64 ++ libc/calls/openat.c | 6 +- libc/calls/umask.c | 4 +- libc/fmt/kerrnodocs.S | 3 +- libc/fmt/kerrnonames.S | 5 +- libc/fmt/magnumstrs.internal.h | 31 +- libc/fmt/strerdoc.greg.c | 2 +- libc/fmt/strerrno.greg.c | 2 +- libc/intrin/describemapflags.greg.c | 2 +- .../describentconsolemodeinputflags.greg.c | 2 +- .../describentconsolemodeoutputflags.greg.c | 2 +- libc/intrin/describentfileaccessflags.greg.c | 2 +- .../describentfileflagsandattributes.greg.c | 2 +- libc/intrin/describentfilemapflags.greg.c | 2 +- libc/intrin/describentfileshareflags.greg.c | 2 +- libc/intrin/describentfiletypeflags.greg.c | 2 +- libc/intrin/describentmovefileflags.greg.c | 2 +- libc/intrin/describentpageflags.greg.c | 2 +- libc/intrin/describentpipemodeflags.greg.c | 2 +- libc/intrin/describentpipeopenflags.greg.c | 2 +- .../describentprocessaccessflags.greg.c | 2 +- libc/intrin/describentstartflags.greg.c | 2 +- .../intrin/describentsymboliclinkflags.greg.c | 2 +- libc/intrin/describeprotflags.greg.c | 2 +- libc/intrin/describeremapflags.greg.c | 2 +- libc/intrin/getmagnumstr.greg.c | 8 +- libc/log/leaks.c | 21 +- libc/nt/struct/linger.h | 13 + libc/sock/describesocklevel.greg.c | 2 +- libc/sock/describesockoptname.greg.c | 12 +- libc/sock/getsockopt-nt.c | 8 + libc/sock/kipoptnames.S | 17 +- libc/sock/ksockoptnames.S | 37 +- libc/sock/ktcpoptnames.S | 35 +- libc/sock/setsockopt-nt.c | 6 +- libc/stdio/system.c | 1 + libc/str/ksignalnames.S | 3 +- libc/str/str.h | 4 +- libc/sysv/consts.sh | 16 +- libc/sysv/consts/CLOCK_BOOTTIME.S | 2 +- libc/sysv/consts/SO_DONTLINGER.S | 2 + libc/sysv/consts/SO_REUSEADDR.S | 2 +- libc/testlib/ezbench.h | 292 +++--- libc/testlib/polluteregisters.S | 20 +- test/libc/calls/stat_test.c | 21 +- test/libc/runtime/mmap_test.c | 58 ++ third_party/linenoise/linenoise.c | 22 +- third_party/lua/cosmo.h | 4 +- third_party/lua/escapeluastring.c | 13 +- third_party/lua/lrepl.c | 57 +- third_party/lua/lua.mk | 10 + third_party/lua/luaencodejsondata.c | 158 ++-- third_party/lua/luaencodeluadata.c | 118 ++- third_party/lua/luaformatstack.c | 22 +- tool/build/blinkenlights.c | 3 +- tool/build/build.mk | 1 + tool/net/demo/unix-info.lua | 8 + tool/net/demo/unix-subprocess.lua | 6 +- tool/net/help.txt | 400 ++++---- tool/net/largon2.c | 8 +- tool/net/lfuncs.c | 54 +- tool/net/lfuncs.h | 1 + tool/net/lsqlite3.c | 2 +- tool/net/lunix.c | 877 ++++++++++-------- tool/net/redbean.c | 164 +++- 74 files changed, 1781 insertions(+), 1024 deletions(-) create mode 100644 libc/calls/describeclockname.c create mode 100644 libc/calls/describeopenflags.greg.c create mode 100644 libc/calls/kclocknames.S create mode 100644 libc/calls/kopenflags.S create mode 100644 libc/nt/struct/linger.h create mode 100644 libc/sysv/consts/SO_DONTLINGER.S diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 9de7af625..2d99b4136 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -119,6 +119,9 @@ int fsync(int); int ftruncate(int, int64_t); int getdents(unsigned, void *, unsigned, long *); int getdomainname(char *, size_t); +int getegid(void) nosideeffect; +int geteuid(void) nosideeffect; +int getgid(void) nosideeffect; int gethostname(char *, size_t); int getloadavg(double *, int); int getpgid(int); @@ -130,6 +133,7 @@ int getrlimit(int, struct rlimit *); int getrusage(int, struct rusage *); int getsid(int) nosideeffect; int gettid(void); +int getuid(void) nosideeffect; int kill(int, int); int killpg(int, int); int link(const char *, const char *) dontthrow; @@ -196,6 +200,7 @@ int sysinfo(struct sysinfo *); int touch(const char *, uint32_t); int truncate(const char *, uint64_t); int ttyname_r(int, char *, size_t); +int umask(int); int uname(struct utsname *); int unlink(const char *); int unlink_s(const char **); @@ -226,11 +231,6 @@ ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t); ssize_t write(int, const void *, size_t); struct dirent *readdir(DIR *); -uint32_t getegid(void) nosideeffect; -uint32_t geteuid(void) nosideeffect; -uint32_t getgid(void) nosideeffect; -uint32_t getuid(void) nosideeffect; -uint32_t umask(uint32_t); void rewinddir(DIR *); void sync(void); diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index dfa690101..28e882b67 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -111,6 +111,7 @@ o/$(MODE)/libc/calls/execlp.o \ o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ o/$(MODE)/libc/calls/readlinkat-nt.o \ +o/$(MODE)/libc/calls/describeopenflags.greg.o \ o/$(MODE)/libc/calls/mkntenvblock.o: \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED diff --git a/libc/calls/describeclockname.c b/libc/calls/describeclockname.c new file mode 100644 index 000000000..5a32ff0cc --- /dev/null +++ b/libc/calls/describeclockname.c @@ -0,0 +1,38 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sol.h" + +/** + * Describes clock_gettime() clock argument. + */ +char *DescribeClockName(int x) { + int i; + char *s; + _Alignas(char) static char buf[32]; + if ((s = GetMagnumStr(kClockNames, x))) { + stpcpy(stpcpy(buf, "CLOCK_"), s); + return buf; + } else { + FormatInt32(buf, x); + return buf; + } +} diff --git a/libc/calls/describeopenflags.greg.c b/libc/calls/describeopenflags.greg.c new file mode 100644 index 000000000..57ab7a46a --- /dev/null +++ b/libc/calls/describeopenflags.greg.c @@ -0,0 +1,41 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/mem/alloca.h" +#include "libc/sysv/consts/sol.h" + +/** + * Describes clock_gettime() clock argument. + */ +char *DescribeOpenFlags(int x) { + char *s; + int i, n; + struct DescribeFlags *d; + _Alignas(char) static char openflags[128]; + // TODO(jart): unify DescribeFlags and MagnumStr data structures + for (n = 0; kOpenFlags[n].x != MAGNUM_TERMINATOR;) ++n; + d = alloca(n * sizeof(struct DescribeFlags)); + for (i = 0; i < n; ++i) { + d[i].flag = MAGNUM_NUMBER(kOpenFlags, i); + d[i].name = MAGNUM_STRING(kOpenFlags, i); + } + return DescribeFlags(openflags, sizeof(openflags), d, n, "O_", x); +} diff --git a/libc/calls/fstatat-sysv.c b/libc/calls/fstatat-sysv.c index 594ef7df5..4c64f3986 100644 --- a/libc/calls/fstatat-sysv.c +++ b/libc/calls/fstatat-sysv.c @@ -28,6 +28,7 @@ */ int32_t sys_fstatat(int32_t dirfd, const char *path, struct stat *st, int32_t flags) { + int rc; void *p; union metastat ms; if (IsAsan() && !__asan_is_valid(path, 1)) return efault(); diff --git a/libc/calls/getegid.c b/libc/calls/getegid.c index 929baeb10..e142d34f1 100644 --- a/libc/calls/getegid.c +++ b/libc/calls/getegid.c @@ -25,7 +25,7 @@ * Returns effective group ID of calling process. * @return group id */ -uint32_t getegid(void) { +int getegid(void) { int rc; if (!IsWindows()) { rc = sys_getegid(); diff --git a/libc/calls/geteuid.c b/libc/calls/geteuid.c index 260c4b6ed..ad16d7419 100644 --- a/libc/calls/geteuid.c +++ b/libc/calls/geteuid.c @@ -24,7 +24,7 @@ * Returns effective user ID of calling process. * @return user id */ -uint32_t geteuid(void) { +int geteuid(void) { int rc; if (!IsWindows()) { rc = sys_geteuid(); diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index 34eb2702f..4c0d49be4 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -51,7 +51,7 @@ static textwindows dontinline uint32_t GetUserNameHash(void) { * @asyncsignalsafe * @vforksafe */ -uint32_t getuid(void) { +int getuid(void) { int rc; if (!IsWindows()) { rc = sys_getuid(); @@ -71,7 +71,7 @@ uint32_t getuid(void) { * @asyncsignalsafe * @vforksafe */ -uint32_t getgid(void) { +int getgid(void) { int rc; if (!IsWindows()) { rc = sys_getgid(); diff --git a/libc/calls/kclocknames.S b/libc/calls/kclocknames.S new file mode 100644 index 000000000..d302c87d3 --- /dev/null +++ b/libc/calls/kclocknames.S @@ -0,0 +1,47 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/macros.internal.h" + + .macro .e e s + .long \e - kClockNames + .long 1f - kClockNames + .rodata.str1.1 +1: .string "\s" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kClockNames: + .e CLOCK_REALTIME,"REALTIME" + .e CLOCK_MONOTONIC,"MONOTONIC" + .e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW" + .e CLOCK_REALTIME_COARSE,"REALTIME_COARSE" + .e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE" + .e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID" + .e CLOCK_TAI,"TAI" + .e CLOCK_PROF,"PROF" + .e CLOCK_BOOTTIME,"BOOTTIME" + .e CLOCK_REALTIME_ALARM,"REALTIME_ALARM" + .e CLOCK_BOOTTIME_ALARM,"BOOTTIME_ALARM" + .long MAGNUM_TERMINATOR + .endobj kClockNames,globl,hidden + .overrun diff --git a/libc/calls/kopenflags.S b/libc/calls/kopenflags.S new file mode 100644 index 000000000..9ced691a0 --- /dev/null +++ b/libc/calls/kopenflags.S @@ -0,0 +1,64 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/macros.internal.h" + + .macro .e e s + .long \e - kOpenFlags + .long 1f - kOpenFlags + .rodata.str1.1 +1: .string "\s" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kOpenFlags: + .e O_RDWR,"RDWR" // order matters + .e O_RDONLY,"RDONLY" // + .e O_WRONLY,"WRONLY" // + .e O_ACCMODE,"ACCMODE" // mask of prev three + .e O_CREAT,"CREAT" // + .e O_EXCL,"EXCL" // + .e O_TRUNC,"TRUNC" // + .e O_CLOEXEC,"CLOEXEC" // + .e O_DIRECT,"DIRECT" // no-op on xnu/openbsd + .e O_APPEND,"APPEND" // weird on nt + .e O_TMPFILE,"TMPFILE" // linux, windows + .e O_NOFOLLOW,"NOFOLLOW" // unix + .e O_SYNC,"SYNC" // unix + .e O_ASYNC,"ASYNC" // unix + .e O_NOCTTY,"NOCTTY" // unix + .e O_NOATIME,"NOATIME" // linux + .e O_EXEC,"EXEC" // free/openbsd + .e O_SEARCH,"SEARCH" // free/netbsd + .e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd + .e O_RSYNC,"RSYNC" // linux/open/netbsd + .e O_PATH,"PATH" // linux + .e O_VERIFY,"VERIFY" // freebsd + .e O_SHLOCK,"SHLOCK" // bsd + .e O_EXLOCK,"EXLOCK" // bsd + .e O_RANDOM,"RANDOM" // windows + .e O_SEQUENTIAL,"SEQUENTIAL" // windows + .e O_COMPRESSED,"COMPRESSED" // windows + .e O_INDEXED,"INDEXED" // windows + .long MAGNUM_TERMINATOR + .endobj kOpenFlags,globl,hidden + .overrun diff --git a/libc/calls/openat.c b/libc/calls/openat.c index 349430dea..7f07dd379 100644 --- a/libc/calls/openat.c +++ b/libc/calls/openat.c @@ -21,6 +21,7 @@ #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/asan.internal.h" #include "libc/log/log.h" #include "libc/str/str.h" @@ -74,7 +75,8 @@ int openat(int dirfd, const char *file, int flags, ...) { } else { rc = efault(); } - STRACE("openat(%s, %#s, %#x, %#o) → %d% m", __strace_dirfd(buf, dirfd), file, - flags, (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, rc); + STRACE("openat(%s, %#s, %s, %#o) → %d% m", __strace_dirfd(buf, dirfd), file, + DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, + rc); return rc; } diff --git a/libc/calls/umask.c b/libc/calls/umask.c index e0dec3276..a6a439f8c 100644 --- a/libc/calls/umask.c +++ b/libc/calls/umask.c @@ -27,8 +27,8 @@ * @return previous mask * @note always succeeds */ -unsigned umask(unsigned newmask) { - unsigned oldmask; +int umask(int newmask) { + int oldmask; if (!IsWindows()) { oldmask = sys_umask(newmask); } else { diff --git a/libc/fmt/kerrnodocs.S b/libc/fmt/kerrnodocs.S index cd7e86c9e..d03750d07 100644 --- a/libc/fmt/kerrnodocs.S +++ b/libc/fmt/kerrnodocs.S @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" .macro .e e s @@ -115,6 +116,6 @@ kErrnoDocs: .e ENOTRECOVERABLE,"State not recoverable" .e ENONET,"Machine is not on the network" .e ERESTART,"Interrupted system call should be restarted" - .long -123 + .long MAGNUM_TERMINATOR .endobj kErrnoDocs,globl,hidden .overrun diff --git a/libc/fmt/kerrnonames.S b/libc/fmt/kerrnonames.S index 789339e01..85394e0e2 100644 --- a/libc/fmt/kerrnonames.S +++ b/libc/fmt/kerrnonames.S @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" .macro .e e @@ -27,7 +28,7 @@ .endm .section .rodata - .align 4 + .align 4 .underrun kErrnoNames: .e EINVAL @@ -116,6 +117,6 @@ kErrnoNames: .e ENONET .e ERESTART .e ENODATA - .long -123 + .long MAGNUM_TERMINATOR .endobj kErrnoNames,globl,hidden .overrun diff --git a/libc/fmt/magnumstrs.internal.h b/libc/fmt/magnumstrs.internal.h index e23dfb2ad..af425fc93 100644 --- a/libc/fmt/magnumstrs.internal.h +++ b/libc/fmt/magnumstrs.internal.h @@ -1,22 +1,35 @@ #ifndef COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ #define COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ + +#define MAGNUM_TERMINATOR -123 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#define MAGNUM_NUMBER(TABLE, INDEX) \ + *(const int *)((uintptr_t)TABLE + TABLE[INDEX].x) + +#define MAGNUM_STRING(TABLE, INDEX) \ + (const char *)((uintptr_t)TABLE + TABLE[INDEX].s) + struct MagnumStr { int x, s; }; -extern const struct MagnumStr kErrnoDocs[]; -extern const struct MagnumStr kErrnoNames[]; -extern const struct MagnumStr kIpOptnames[]; -extern const struct MagnumStr kSignalNames[]; -extern const struct MagnumStr kSockOptnames[]; -extern const struct MagnumStr kTcpOptnames[]; +hidden extern const struct MagnumStr kClockNames[]; +hidden extern const struct MagnumStr kErrnoDocs[]; +hidden extern const struct MagnumStr kErrnoNames[]; +hidden extern const struct MagnumStr kIpOptnames[]; +hidden extern const struct MagnumStr kOpenFlags[]; +hidden extern const struct MagnumStr kSignalNames[]; +hidden extern const struct MagnumStr kSockOptnames[]; +hidden extern const struct MagnumStr kTcpOptnames[]; -const char *DescribeSockLevel(int); -const char *DescribeSockOptname(int, int); -const char *GetMagnumStr(const struct MagnumStr *, int); +char *DescribeClockName(int) hidden; +char *DescribeOpenFlags(int) hidden; +char *DescribeSockLevel(int) hidden; +char *DescribeSockOptname(int, int) hidden; +char *GetMagnumStr(const struct MagnumStr *, int) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/fmt/strerdoc.greg.c b/libc/fmt/strerdoc.greg.c index abd68b616..b8e89b384 100644 --- a/libc/fmt/strerdoc.greg.c +++ b/libc/fmt/strerdoc.greg.c @@ -23,7 +23,7 @@ * Converts errno value to descriptive sentence. * @return non-null rodata string or null if not found */ -const char *strerdoc(int x) { +char *strerdoc(int x) { if (x) { return GetMagnumStr(kErrnoDocs, x); } else { diff --git a/libc/fmt/strerrno.greg.c b/libc/fmt/strerrno.greg.c index 042ae1942..145727831 100644 --- a/libc/fmt/strerrno.greg.c +++ b/libc/fmt/strerrno.greg.c @@ -23,7 +23,7 @@ * Converts errno value to symbolic name. * @return non-null rodata string or null if not found */ -const char *strerrno(int x) { +char *strerrno(int x) { if (x) { return GetMagnumStr(kErrnoNames, x); } else { diff --git a/libc/intrin/describemapflags.greg.c b/libc/intrin/describemapflags.greg.c index d913ac66b..8b361b72a 100644 --- a/libc/intrin/describemapflags.greg.c +++ b/libc/intrin/describemapflags.greg.c @@ -23,7 +23,7 @@ #include "libc/sysv/consts/prot.h" const char *DescribeMapFlags(int x) { - static char mapflags[256]; + _Alignas(char) static char mapflags[256]; const struct DescribeFlags kMapFlags[] = { {MAP_ANONYMOUS, "ANONYMOUS"}, // {MAP_PRIVATE, "PRIVATE"}, // diff --git a/libc/intrin/describentconsolemodeinputflags.greg.c b/libc/intrin/describentconsolemodeinputflags.greg.c index 0203679bc..1b3fb94eb 100644 --- a/libc/intrin/describentconsolemodeinputflags.greg.c +++ b/libc/intrin/describentconsolemodeinputflags.greg.c @@ -34,7 +34,7 @@ static const struct DescribeFlags kConsoleModeInputFlags[] = { }; const char *DescribeNtConsoleModeInputFlags(uint32_t x) { - static char consolemodeinputflags[256]; + _Alignas(char) static char consolemodeinputflags[256]; return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags), kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags), "kNtEnable", x); diff --git a/libc/intrin/describentconsolemodeoutputflags.greg.c b/libc/intrin/describentconsolemodeoutputflags.greg.c index b862efae4..139b6e9ed 100644 --- a/libc/intrin/describentconsolemodeoutputflags.greg.c +++ b/libc/intrin/describentconsolemodeoutputflags.greg.c @@ -29,7 +29,7 @@ static const struct DescribeFlags kConsoleModeOutputFlags[] = { }; const char *DescribeNtConsoleModeOutputFlags(uint32_t x) { - static char consolemodeoutputflags[128]; + _Alignas(char) static char consolemodeoutputflags[128]; return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags), kConsoleModeOutputFlags, ARRAYLEN(kConsoleModeOutputFlags), "kNt", x); diff --git a/libc/intrin/describentfileaccessflags.greg.c b/libc/intrin/describentfileaccessflags.greg.c index cd09bf6ab..79924afb7 100644 --- a/libc/intrin/describentfileaccessflags.greg.c +++ b/libc/intrin/describentfileaccessflags.greg.c @@ -64,7 +64,7 @@ static const struct DescribeFlags kFileAccessflags[] = { }; const char *DescribeNtFileAccessFlags(uint32_t x) { - static char ntfileaccessflags[512]; + _Alignas(char) static char ntfileaccessflags[512]; return DescribeFlags(ntfileaccessflags, sizeof(ntfileaccessflags), kFileAccessflags, ARRAYLEN(kFileAccessflags), "kNt", x); } diff --git a/libc/intrin/describentfileflagsandattributes.greg.c b/libc/intrin/describentfileflagsandattributes.greg.c index 9e6174477..53a5a47d7 100644 --- a/libc/intrin/describentfileflagsandattributes.greg.c +++ b/libc/intrin/describentfileflagsandattributes.greg.c @@ -52,7 +52,7 @@ static const struct DescribeFlags kFileFlags[] = { }; const char *DescribeNtFileFlagsAndAttributes(uint32_t x) { - static char ntfileflags[256]; + _Alignas(char) static char ntfileflags[256]; if (x == -1u) return "-1u"; return DescribeFlags(ntfileflags, sizeof(ntfileflags), kFileFlags, ARRAYLEN(kFileFlags), "kNtFile", x); diff --git a/libc/intrin/describentfilemapflags.greg.c b/libc/intrin/describentfilemapflags.greg.c index 5ab201575..f5cdabaf5 100644 --- a/libc/intrin/describentfilemapflags.greg.c +++ b/libc/intrin/describentfilemapflags.greg.c @@ -31,7 +31,7 @@ static const struct DescribeFlags kFileMapFlags[] = { }; const char *DescribeNtFileMapFlags(uint32_t x) { - static char filemapflags[64]; + _Alignas(char) static char filemapflags[64]; return DescribeFlags(filemapflags, sizeof(filemapflags), kFileMapFlags, ARRAYLEN(kFileMapFlags), "kNtFileMap", x); } diff --git a/libc/intrin/describentfileshareflags.greg.c b/libc/intrin/describentfileshareflags.greg.c index a5e6d1b29..c322e3b86 100644 --- a/libc/intrin/describentfileshareflags.greg.c +++ b/libc/intrin/describentfileshareflags.greg.c @@ -27,7 +27,7 @@ static const struct DescribeFlags kFileShareflags[] = { }; const char *DescribeNtFileShareFlags(uint32_t x) { - static char ntfileshareflags[64]; + _Alignas(char) static char ntfileshareflags[64]; return DescribeFlags(ntfileshareflags, sizeof(ntfileshareflags), kFileShareflags, ARRAYLEN(kFileShareflags), "kNtFileShare", x); diff --git a/libc/intrin/describentfiletypeflags.greg.c b/libc/intrin/describentfiletypeflags.greg.c index 764749167..70b48ea1b 100644 --- a/libc/intrin/describentfiletypeflags.greg.c +++ b/libc/intrin/describentfiletypeflags.greg.c @@ -29,7 +29,7 @@ static const struct DescribeFlags kFiletypeFlags[] = { }; const char *DescribeNtFiletypeFlags(uint32_t x) { - static char filetypeflags[64]; + _Alignas(char) static char filetypeflags[64]; return DescribeFlags(filetypeflags, sizeof(filetypeflags), kFiletypeFlags, ARRAYLEN(kFiletypeFlags), "kNtFileType", x); } diff --git a/libc/intrin/describentmovefileflags.greg.c b/libc/intrin/describentmovefileflags.greg.c index 9c5c62026..c2f29b869 100644 --- a/libc/intrin/describentmovefileflags.greg.c +++ b/libc/intrin/describentmovefileflags.greg.c @@ -30,7 +30,7 @@ static const struct DescribeFlags kMoveFileInputFlags[] = { }; const char *DescribeNtMoveFileInputFlags(uint32_t x) { - static char movefileflags[256]; + _Alignas(char) static char movefileflags[256]; return DescribeFlags(movefileflags, sizeof(movefileflags), kMoveFileInputFlags, ARRAYLEN(kMoveFileInputFlags), "kNtMovefile", x); diff --git a/libc/intrin/describentpageflags.greg.c b/libc/intrin/describentpageflags.greg.c index 8c37a622a..02a7451c8 100644 --- a/libc/intrin/describentpageflags.greg.c +++ b/libc/intrin/describentpageflags.greg.c @@ -42,7 +42,7 @@ static const struct DescribeFlags kPageFlags[] = { }; const char *DescribeNtPageFlags(uint32_t x) { - static char pageflags[64]; + _Alignas(char) static char pageflags[64]; return DescribeFlags(pageflags, sizeof(pageflags), kPageFlags, ARRAYLEN(kPageFlags), "kNt", x); } diff --git a/libc/intrin/describentpipemodeflags.greg.c b/libc/intrin/describentpipemodeflags.greg.c index 978935805..f8f1f89ee 100644 --- a/libc/intrin/describentpipemodeflags.greg.c +++ b/libc/intrin/describentpipemodeflags.greg.c @@ -33,7 +33,7 @@ static const struct DescribeFlags kPipeModeFlags[] = { }; const char *DescribeNtPipeModeFlags(uint32_t x) { - static char pipemodeflags[64]; + _Alignas(char) static char pipemodeflags[64]; return DescribeFlags(pipemodeflags, sizeof(pipemodeflags), kPipeModeFlags, ARRAYLEN(kPipeModeFlags), "kNtPipe", x); } diff --git a/libc/intrin/describentpipeopenflags.greg.c b/libc/intrin/describentpipeopenflags.greg.c index 7b6fb5c68..10ab98a81 100644 --- a/libc/intrin/describentpipeopenflags.greg.c +++ b/libc/intrin/describentpipeopenflags.greg.c @@ -28,7 +28,7 @@ static const struct DescribeFlags kPipeOpenFlags[] = { }; const char *DescribeNtPipeOpenFlags(uint32_t x) { - static char pipeopenflags[64]; + _Alignas(char) static char pipeopenflags[64]; return DescribeFlags(pipeopenflags, sizeof(pipeopenflags), kPipeOpenFlags, ARRAYLEN(kPipeOpenFlags), "kNtPipeAccess", x); } diff --git a/libc/intrin/describentprocessaccessflags.greg.c b/libc/intrin/describentprocessaccessflags.greg.c index 3c93b5cc6..5f3d7fcdd 100644 --- a/libc/intrin/describentprocessaccessflags.greg.c +++ b/libc/intrin/describentprocessaccessflags.greg.c @@ -38,7 +38,7 @@ static const struct DescribeFlags kProcessAccessflags[] = { }; const char *DescribeNtProcessAccessFlags(uint32_t x) { - static char ntprocessaccessflags[256]; + _Alignas(char) static char ntprocessaccessflags[256]; return DescribeFlags(ntprocessaccessflags, sizeof(ntprocessaccessflags), kProcessAccessflags, ARRAYLEN(kProcessAccessflags), "kNtProcess", x); diff --git a/libc/intrin/describentstartflags.greg.c b/libc/intrin/describentstartflags.greg.c index e7578a56c..d80e2778b 100644 --- a/libc/intrin/describentstartflags.greg.c +++ b/libc/intrin/describentstartflags.greg.c @@ -39,7 +39,7 @@ static const struct DescribeFlags kNtStartFlags[] = { }; const char *DescribeNtStartFlags(uint32_t x) { - static char startflags[128]; + _Alignas(char) static char startflags[128]; return DescribeFlags(startflags, sizeof(startflags), kNtStartFlags, ARRAYLEN(kNtStartFlags), "kNtStartf", x); } diff --git a/libc/intrin/describentsymboliclinkflags.greg.c b/libc/intrin/describentsymboliclinkflags.greg.c index 3f4afffc3..233945f94 100644 --- a/libc/intrin/describentsymboliclinkflags.greg.c +++ b/libc/intrin/describentsymboliclinkflags.greg.c @@ -26,7 +26,7 @@ static const struct DescribeFlags kSymbolicLinkflags[] = { }; const char *DescribeNtSymbolicLinkFlags(uint32_t x) { - static char ntsymboliclinkflags[64]; + _Alignas(char) static char ntsymboliclinkflags[64]; return DescribeFlags(ntsymboliclinkflags, sizeof(ntsymboliclinkflags), kSymbolicLinkflags, ARRAYLEN(kSymbolicLinkflags), "kNtSymbolicLinkFlag", x); diff --git a/libc/intrin/describeprotflags.greg.c b/libc/intrin/describeprotflags.greg.c index 1bcae2e1e..ac30ab5fc 100644 --- a/libc/intrin/describeprotflags.greg.c +++ b/libc/intrin/describeprotflags.greg.c @@ -27,7 +27,7 @@ static const struct DescribeFlags kProtFlags[] = { }; const char *DescribeProtFlags(int x) { - static char protflags[64]; + _Alignas(char) static char protflags[64]; return DescribeFlags(protflags, sizeof(protflags), kProtFlags, ARRAYLEN(kProtFlags), "PROT_", x); } diff --git a/libc/intrin/describeremapflags.greg.c b/libc/intrin/describeremapflags.greg.c index 98b0f1e7c..40ce7219e 100644 --- a/libc/intrin/describeremapflags.greg.c +++ b/libc/intrin/describeremapflags.greg.c @@ -26,7 +26,7 @@ static const struct DescribeFlags kRemapFlags[] = { }; const char *DescribeRemapFlags(int x) { - static char remapflags[64]; + _Alignas(char) static char remapflags[64]; return DescribeFlags(remapflags, sizeof(remapflags), kRemapFlags, ARRAYLEN(kRemapFlags), "MREMAP_", x); } diff --git a/libc/intrin/getmagnumstr.greg.c b/libc/intrin/getmagnumstr.greg.c index 4be124a29..20834e936 100644 --- a/libc/intrin/getmagnumstr.greg.c +++ b/libc/intrin/getmagnumstr.greg.c @@ -18,11 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/magnumstrs.internal.h" -const char *GetMagnumStr(const struct MagnumStr *ms, int x) { +char *GetMagnumStr(const struct MagnumStr *ms, int x) { int i; - for (i = 0; ms[i].x != -123; ++i) { - if (x == *(const int *)((uintptr_t)ms + ms[i].x)) { - return (const char *)((uintptr_t)ms + ms[i].s); + for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) { + if (x == MAGNUM_NUMBER(ms, i)) { + return MAGNUM_STRING(ms, i); } } return 0; diff --git a/libc/log/leaks.c b/libc/log/leaks.c index e074c8355..0f61dda6a 100644 --- a/libc/log/leaks.c +++ b/libc/log/leaks.c @@ -28,6 +28,8 @@ STATIC_YOINK("__get_symbol_by_addr"); +#define MAXLEAKS 1000 + static bool once; static bool hasleaks; @@ -46,15 +48,18 @@ static noasan void CheckLeak(void *x, void *y, size_t n, void *a) { static noasan void OnMemory(void *x, void *y, size_t n, void *a) { static int i; if (n) { - if (++i < 20) { - kprintf("%p %,lu bytes [dlmalloc]", x, n); - if (IsAsan()) { - __asan_print_trace(x); + if (MAXLEAKS) { + if (i < MAXLEAKS) { + ++i; + kprintf("%p %,lu bytes [dlmalloc]", x, n); + if (IsAsan()) { + __asan_print_trace(x); + } + kprintf("\n"); + } else if (i == MAXLEAKS) { + ++i; + kprintf("etc. etc.\n"); } - kprintf("\n"); - } - if (i == 20) { - kprintf("etc. etc.\n"); } } } diff --git a/libc/nt/struct/linger.h b/libc/nt/struct/linger.h new file mode 100644 index 000000000..0267d205a --- /dev/null +++ b/libc/nt/struct/linger.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct linger_nt { + uint16_t l_onoff; /* on/off */ + uint16_t l_linger; /* seconds */ +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ */ diff --git a/libc/sock/describesocklevel.greg.c b/libc/sock/describesocklevel.greg.c index 337d6e822..7dd91118b 100644 --- a/libc/sock/describesocklevel.greg.c +++ b/libc/sock/describesocklevel.greg.c @@ -22,7 +22,7 @@ /** * Describes setsockopt() level arguments. */ -const char *DescribeSockLevel(int x) { +char *DescribeSockLevel(int x) { static char buf[12]; if (x == SOL_IP) return "SOL_IP"; if (x == SOL_TCP) return "SOL_TCP"; diff --git a/libc/sock/describesockoptname.greg.c b/libc/sock/describesockoptname.greg.c index d3f1388fa..09213b2a0 100644 --- a/libc/sock/describesockoptname.greg.c +++ b/libc/sock/describesockoptname.greg.c @@ -18,26 +18,32 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/itoa.h" #include "libc/fmt/magnumstrs.internal.h" +#include "libc/str/str.h" #include "libc/sysv/consts/sol.h" /** * Describes setsockopt() optname arguments. */ -const char *DescribeSockOptname(int l, int x) { +char *DescribeSockOptname(int l, int x) { int i; - static char buf[12], *s; + char *ps, *s; const struct MagnumStr *ms = 0; + _Alignas(char) static char buf[32]; if (x) { if (l == SOL_SOCKET) { + ps = "SO_"; ms = kSockOptnames; } else if (l == SOL_TCP) { + ps = "TCP_"; ms = kTcpOptnames; } else if (l == SOL_IP) { + ps = "IP_"; ms = kIpOptnames; } } if (ms && (s = GetMagnumStr(ms, x))) { - return s; + stpcpy(stpcpy(buf, ps), s); + return buf; } else { FormatInt32(buf, x); return buf; diff --git a/libc/sock/getsockopt-nt.c b/libc/sock/getsockopt-nt.c index 0ab21dd1f..d64f6c5f6 100644 --- a/libc/sock/getsockopt-nt.c +++ b/libc/sock/getsockopt-nt.c @@ -20,8 +20,10 @@ #include "libc/bits/bits.h" #include "libc/calls/internal.h" #include "libc/calls/struct/timeval.h" +#include "libc/nt/struct/linger.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/sock.h" #include "libc/sock/yoink.inc" #include "libc/str/str.h" #include "libc/sysv/consts/so.h" @@ -33,6 +35,7 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, uint32_t *inout_optlen) { uint64_t ms; uint32_t in_optlen; + struct linger_nt linger; assert(fd->kind == kFdSocket); if (out_opt_optval && inout_optlen) { @@ -55,6 +58,11 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, ((struct timeval *)out_opt_optval)->tv_sec = ms / 1000; ((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000; *inout_optlen = sizeof(struct timeval); + } else if (optname == SO_LINGER && in_optlen == sizeof(struct linger)) { + linger = *(struct linger_nt *)out_opt_optval; + ((struct linger *)out_opt_optval)->l_onoff = !!linger.l_onoff; + ((struct linger *)out_opt_optval)->l_linger = linger.l_linger; + *inout_optlen = sizeof(struct linger); } } diff --git a/libc/sock/kipoptnames.S b/libc/sock/kipoptnames.S index 4d3c8dae4..cee04e63c 100644 --- a/libc/sock/kipoptnames.S +++ b/libc/sock/kipoptnames.S @@ -16,24 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" - .macro .e e + .macro .e e s .long \e - kIpOptnames .long 1f - kIpOptnames .rodata.str1.1 -1: .string "\e" +1: .string "\s" .previous .endm .section .rodata - .align 4 + .align 4 .underrun kIpOptnames: - .e IP_TOS # int - .e IP_MTU # int - .e IP_TTL # int - .e IP_HDRINCL # bool32 - .long -123 + .e IP_TOS,"TOS" # int + .e IP_MTU,"MTU" # int + .e IP_TTL,"TTL" # int + .e IP_HDRINCL,"HDRINCL" # bool32 + .long MAGNUM_TERMINATOR .endobj kIpOptnames,globl,hidden .overrun diff --git a/libc/sock/ksockoptnames.S b/libc/sock/ksockoptnames.S index b56acbffe..8b29764a0 100644 --- a/libc/sock/ksockoptnames.S +++ b/libc/sock/ksockoptnames.S @@ -16,13 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" - .macro .e e + .macro .e e s .long \e - kSockOptnames .long 1f - kSockOptnames .rodata.str1.1 -1: .string "\e" +1: .string "\s" .previous .endm @@ -30,20 +31,22 @@ .align 4 .underrun kSockOptnames: - .e SO_DEBUG # bool32 - .e SO_BROADCAST # bool32 - .e SO_REUSEADDR # bool32 - .e SO_REUSEPORT # bool32 - .e SO_KEEPALIVE # bool32 - .e SO_DONTROUTE # bool32 - .e SO_RCVTIMEO # timeval - .e SO_SNDTIMEO # timeval - .e SO_LINGER # linger - .e SO_SNDBUF # int - .e SO_RCVBUF # int - .e SO_RCVLOWAT # int - .e SO_SNDLOWAT # int - .e SO_ERROR # int - .long -123 + .e SO_DEBUG,"DEBUG" # bool32 + .e SO_ACCEPTCONN,"ACCEPTCONN" # bool32 + .e SO_BROADCAST,"BROADCAST" # bool32 + .e SO_REUSEADDR,"REUSEADDR" # bool32 + .e SO_REUSEPORT,"REUSEPORT" # bool32 + .e SO_KEEPALIVE,"KEEPALIVE" # bool32 + .e SO_DONTROUTE,"DONTROUTE" # bool32 + .e SO_RCVTIMEO,"RCVTIMEO" # timeval + .e SO_SNDTIMEO,"SNDTIMEO" # timeval + .e SO_LINGER,"LINGER" # linger + .e SO_TYPE,"TYPE" # int + .e SO_SNDBUF,"SNDBUF" # int + .e SO_RCVBUF,"RCVBUF" # int + .e SO_RCVLOWAT,"RCVLOWAT" # int + .e SO_SNDLOWAT,"SNDLOWAT" # int + .e SO_ERROR,"ERROR" # int + .long MAGNUM_TERMINATOR .endobj kSockOptnames,globl,hidden .overrun diff --git a/libc/sock/ktcpoptnames.S b/libc/sock/ktcpoptnames.S index f5d837ae8..444b35922 100644 --- a/libc/sock/ktcpoptnames.S +++ b/libc/sock/ktcpoptnames.S @@ -16,33 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" - .macro .e e + .macro .e e s .long \e - kTcpOptnames .long 1f - kTcpOptnames .rodata.str1.1 -1: .string "\e" +1: .string "\s" .previous .endm .section .rodata - .align 4 + .align 4 .underrun kTcpOptnames: - .e TCP_NODELAY # bool32 - .e TCP_CORK # bool32 - .e TCP_QUICKACK # bool32 - .e TCP_FASTOPEN_CONNECT # bool32 - .e TCP_DEFER_ACCEPT # bool32 - .e TCP_KEEPIDLE # int (seconds) - .e TCP_KEEPINTVL # int (seconds) - .e TCP_FASTOPEN # int - .e TCP_KEEPCNT # int - .e TCP_MAXSEG # int - .e TCP_SYNCNT # int - .e TCP_NOTSENT_LOWAT # int - .e TCP_WINDOW_CLAMP # int - .long -123 + .e TCP_NODELAY,"NODELAY" # bool32 + .e TCP_CORK,"CORK" # bool32 + .e TCP_QUICKACK,"QUICKACK" # bool32 + .e TCP_FASTOPEN_CONNECT,"FASTOPEN_CONNECT" # bool32 + .e TCP_DEFER_ACCEPT,"DEFER_ACCEPT" # bool32 + .e TCP_KEEPIDLE,"KEEPIDLE" # int (seconds) + .e TCP_KEEPINTVL,"KEEPINTVL" # int (seconds) + .e TCP_FASTOPEN,"FASTOPEN" # int + .e TCP_KEEPCNT,"KEEPCNT" # int + .e TCP_MAXSEG,"MAXSEG" # int + .e TCP_SYNCNT,"SYNCNT" # int + .e TCP_NOTSENT_LOWAT,"NOTSENT_LOWAT" # int + .e TCP_WINDOW_CLAMP,"WINDOW_CLAMP" # int + .long MAGNUM_TERMINATOR .endobj kTcpOptnames,globl,hidden .overrun diff --git a/libc/sock/setsockopt-nt.c b/libc/sock/setsockopt-nt.c index 24c753517..4f637d8cd 100644 --- a/libc/sock/setsockopt-nt.c +++ b/libc/sock/setsockopt-nt.c @@ -19,16 +19,12 @@ #include "libc/calls/struct/timeval.h" #include "libc/limits.h" #include "libc/macros.internal.h" +#include "libc/nt/struct/linger.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" -struct linger_nt { /* Linux+XNU+BSD ABI */ - uint16_t l_onoff; /* on/off */ - uint16_t l_linger; /* seconds */ -}; - textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname, const void *optval, uint32_t optlen) { int64_t ms; diff --git a/libc/stdio/system.c b/libc/stdio/system.c index dd1fee19b..ee88289a5 100644 --- a/libc/stdio/system.c +++ b/libc/stdio/system.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/log/log.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" diff --git a/libc/str/ksignalnames.S b/libc/str/ksignalnames.S index 5939eb3e9..0e58a1b70 100644 --- a/libc/str/ksignalnames.S +++ b/libc/str/ksignalnames.S @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" .macro .e e s @@ -65,6 +66,6 @@ kSignalNames: .e SIGRTMIN,"RTMIN" .e SIGEMT,"EMT" .e SIGPWR,"PWR" - .long -123 + .long MAGNUM_TERMINATOR .endobj kSignalNames,globl,hidden .overrun diff --git a/libc/str/str.h b/libc/str/str.h index 6ec97a339..fbf461e9a 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -262,8 +262,8 @@ wint_t towctrans(wint_t, wctrans_t); char *strsignal(int) returnsnonnull libcesque; char *strerror(int) returnsnonnull dontthrow nocallback; -const char *strerrno(int) nosideeffect libcesque; -const char *strerdoc(int) nosideeffect libcesque; +char *strerrno(int) nosideeffect libcesque; +char *strerdoc(int) nosideeffect libcesque; int strerror_r(int, char *, size_t) dontthrow nocallback; int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 48fec094f..febb4bb42 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -620,7 +620,7 @@ syscon clock CLOCK_MONOTONIC_RAW 4 4 0x4000 0x4000 0x4000 4 # actu syscon clock CLOCK_REALTIME_COARSE 5 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_MONOTONIC_COARSE 6 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_PROF -1 -1 2 -1 2 -1 # -syscon clock CLOCK_BOOTTIME 7 -1 -1 6 6 -1 # +syscon clock CLOCK_BOOTTIME 7 -1 -1 6 -1 -1 # syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 # syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 # syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 # @@ -669,16 +669,19 @@ syscon epoll EPOLLET 0x80000000 0x80000000 0x80000000 0x80000000 0x80000 # * -1 we define as no-op # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon so SO_DEBUG 1 1 1 1 1 1 # debugging is enabled; consensus +syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus +syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus +syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus syscon so SO_REUSEPORT 15 0x0200 0x0200 0x0200 0x0200 4 # bsd consensus (NT calls it SO_REUSEADDR) -syscon so SO_REUSEADDR 2 4 4 4 4 0 # bsd consensus (default behavior on NT) +syscon so SO_REUSEADDR 2 4 4 4 4 4 # bsd consensus (default behavior on NT) +syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 ~4 # bsd consensus (default behavior on NT) syscon so SO_KEEPALIVE 9 8 8 8 8 8 # bsd consensus syscon so SO_DONTROUTE 5 0x10 0x10 0x10 0x10 0x10 # bsd consensus -syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # bsd consensus +syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # socket is configured for broadcast messages; bsd consensus syscon so SO_USELOOPBACK 0 0x40 0x40 0x40 0x40 0x40 # bsd consensus syscon so SO_LINGER 13 0x80 0x80 0x80 0x80 0x80 # takes struct linger; causes close() return value to actually mean something; bsd consensus -syscon so SO_DEBUG 1 1 1 1 1 1 # consensus -syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus -syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus +syscon so SO_DONTLINGER 0 0 0 0 0 ~0x80 # disables so_linger on windows syscon so SO_OOBINLINE 10 0x0100 0x0100 0x0100 0x0100 0x0100 # bsd consensus syscon so SO_SNDBUF 7 0x1001 0x1001 0x1001 0x1001 0x1001 # bsd consensus syscon so SO_RCVBUF 8 0x1002 0x1002 0x1002 0x1002 0x1002 # bsd consensus @@ -686,7 +689,6 @@ syscon so SO_RCVTIMEO 20 0x1006 0x1006 0x1006 0x100c 0x1006 # rec syscon so SO_SNDTIMEO 21 0x1005 0x1005 0x1005 0x100b 0x1005 # send timeout; takes struct timeval; bsd consensus syscon so SO_RCVLOWAT 18 0x1004 0x1004 0x1004 0x1004 0x1004 # bsd consensus syscon so SO_SNDLOWAT 19 0x1003 0x1003 0x1003 0x1003 0x1003 # bsd consensus -syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus syscon so SO_TIMESTAMP 29 0x0400 0x0400 0x0800 0x2000 0 syscon so SO_SETFIB 0 0 0x1014 0 0 0 syscon so SO_DOMAIN 39 0 0x1019 0x1024 0 0 diff --git a/libc/sysv/consts/CLOCK_BOOTTIME.S b/libc/sysv/consts/CLOCK_BOOTTIME.S index 4a093dab4..05ce54ce7 100644 --- a/libc/sysv/consts/CLOCK_BOOTTIME.S +++ b/libc/sysv/consts/CLOCK_BOOTTIME.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,6,-1 +.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,-1,-1 diff --git a/libc/sysv/consts/SO_DONTLINGER.S b/libc/sysv/consts/SO_DONTLINGER.S new file mode 100644 index 000000000..6d61ff314 --- /dev/null +++ b/libc/sysv/consts/SO_DONTLINGER.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon so,SO_DONTLINGER,0,0,0,0,0,~0x80 diff --git a/libc/sysv/consts/SO_REUSEADDR.S b/libc/sysv/consts/SO_REUSEADDR.S index 7c52c3253..b78e3b8d5 100644 --- a/libc/sysv/consts/SO_REUSEADDR.S +++ b/libc/sysv/consts/SO_REUSEADDR.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon so,SO_REUSEADDR,2,4,4,4,4,0 +.syscon so,SO_REUSEADDR,2,4,4,4,4,4 diff --git a/libc/testlib/ezbench.h b/libc/testlib/ezbench.h index 977321c08..7f8714343 100644 --- a/libc/testlib/ezbench.h +++ b/libc/testlib/ezbench.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_ #define COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_ #include "libc/macros.internal.h" +#include "libc/nexgen32e/bench.h" #include "libc/nexgen32e/x86feature.h" #include "libc/sysv/consts/clock.h" #include "libc/testlib/bench.h" @@ -9,151 +10,162 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#define EZBENCH_COUNT 128 +#define EZBENCH_TRIES 10 + #define EZBENCH(INIT, EXPR) EZBENCH2(#EXPR, INIT, EXPR) -#define EZBENCH2(NAME, INIT, EXPR) \ - do { \ - int Core, Tries, Interrupts; \ - int64_t Speculative, MemoryStrict; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - Speculative = BENCHLOOP(__startbench, __endbench, 128, ({ \ - INIT; \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \ - INIT; \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ - __testlib_ezbenchreport( \ - NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ - MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ +#define EZBENCH2(NAME, INIT, EXPR) \ + do { \ + int Core, Tries, Interrupts; \ + int64_t Speculative, MemoryStrict; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, ({ \ + INIT; \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \ + INIT; \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \ + __testlib_ezbenchreport( \ + NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ + MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ } while (0) -#define EZBENCH3(NAME, NUM, INIT, EXPR) \ - do { \ - int Core, Tries, Interrupts; \ - int64_t Speculative, MemoryStrict; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \ - INIT; \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - INIT; \ - EXPR; \ - MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \ - INIT; \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ - __testlib_ezbenchreport( \ - NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ - MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ +#define EZBENCH3(NAME, NUM, INIT, EXPR) \ + do { \ + int Core, Tries, Interrupts; \ + int64_t Speculative, MemoryStrict; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \ + INIT; \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + INIT; \ + EXPR; \ + MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \ + INIT; \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \ + __testlib_ezbenchreport( \ + NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ + MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ } while (0) -#define EZBENCH_C(NAME, CONTROL, EXPR) \ - do { \ - int Core, Tries, Interrupts; \ - int64_t Control, Speculative, MemoryStrict; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - Control = BENCHLOOP(__startbench_m, __endbench_m, 128, ({ \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (CONTROL)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" control"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - EXPR; \ - Speculative = BENCHLOOP(__startbench, __endbench, 128, \ - polluteregisters(), (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - EXPR; \ - MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \ - thrashcodecache(); \ - polluteregisters(); \ - }), \ - (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ - __testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \ - MAX(0, MemoryStrict - Control)); \ +#define EZBENCH_C(NAME, CONTROL, EXPR) \ + do { \ + int Core, Tries, Interrupts; \ + int64_t Control, Speculative, MemoryStrict; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + Control = BENCHLOOP(__startbench_m, __endbench_m, EZBENCH_COUNT, ({ \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (CONTROL)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" control"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \ + __polluteregisters(), (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + EXPR; \ + MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \ + thrashcodecache(); \ + __polluteregisters(); \ + }), \ + (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \ + __testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \ + MAX(0, MemoryStrict - Control)); \ } while (0) -#define EZBENCH_N(NAME, N, EXPR) \ - do { \ - int64_t Speculative, Toto; \ - int Core, Tries, Interrupts; \ - Tries = 0; \ - do { \ - __testlib_yield(); \ - Core = __testlib_getcore(); \ - Interrupts = __testlib_getinterrupts(); \ - EXPR; \ - Speculative = \ - BENCHLOOP(__startbench, __endbench, 32, polluteregisters(), (EXPR)); \ - } while (++Tries < 10 && (__testlib_getcore() != Core && \ - __testlib_getinterrupts() > Interrupts)); \ - if (Tries == 10) __testlib_ezbenchwarn(""); \ - __testlib_ezbenchreport_n( \ - NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ +#define EZBENCH_N(NAME, N, EXPR) \ + do { \ + int64_t Speculative, Toto; \ + int Core, Tries, Interrupts; \ + Tries = 0; \ + do { \ + __testlib_yield(); \ + Core = __testlib_getcore(); \ + Interrupts = __testlib_getinterrupts(); \ + EXPR; \ + Speculative = BENCHLOOP(__startbench, __endbench, 32, \ + __polluteregisters(), (EXPR)); \ + } while (++Tries < EZBENCH_TRIES && \ + (__testlib_getcore() != Core && \ + __testlib_getinterrupts() > Interrupts)); \ + if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(""); \ + __testlib_ezbenchreport_n( \ + NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ } while (0) #define EZBENCH_K(NAME, K, EXPR) \ @@ -164,14 +176,14 @@ COSMOPOLITAN_C_START_ __testlib_yield(); \ Core = __testlib_getcore(); \ EXPR; \ - Speculative = \ - BENCHLOOP(__startbench, __endbench, 128, donothing, (EXPR)); \ + Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \ + donothing, (EXPR)); \ } while (Core != __testlib_getcore()); \ __testlib_ezbenchreport_n( \ NAME, 'k', K, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ } while (0) -void polluteregisters(void); +void __polluteregisters(void); void __testlib_yield(void); int __testlib_getcore(void); int64_t __testlib_getinterrupts(void); diff --git a/libc/testlib/polluteregisters.S b/libc/testlib/polluteregisters.S index 7b8a1c1be..62971dc97 100644 --- a/libc/testlib/polluteregisters.S +++ b/libc/testlib/polluteregisters.S @@ -19,15 +19,15 @@ #include "libc/nexgen32e/x86feature.h" #include "libc/macros.internal.h" -polluteregisters: +__polluteregisters: .leafprologue xor %eax,%eax - mov %ecx,%ecx - mov %edx,%edx - mov %r8d,%r8d - mov %r9d,%r9d - mov %r10d,%r10d - mov %r11d,%r11d + xor %ecx,%ecx + xor %edx,%edx + xor %r8d,%r8d + xor %r9d,%r9d + xor %r10d,%r10d + xor %r11d,%r11d testb X86_HAVE(AVX)+kCpuids(%rip) jz .Lsse vpxor %xmm0,%xmm0,%xmm0 @@ -48,13 +48,13 @@ polluteregisters: xorps %xmm6,%xmm6 xorps %xmm7,%xmm7 .leafepilogue - .endfn polluteregisters,globl + .endfn __polluteregisters,globl .end // Fill registers with junk data to create false dependencies. // Which shall create the problem that happens w/o vzeroupper. // Or the Core Architecture errata regarding BSR/BSF w/ 64bit. -polluteregisters: +__polluteregisters: .leafprologue mov $-1,%rax mov %rax,%rcx @@ -92,4 +92,4 @@ polluteregisters: punpcklqdq %xmm0,%xmm6 punpcklqdq %xmm0,%xmm7 .leafepilogue - .endfn polluteregisters,globl + .endfn __polluteregisters,globl diff --git a/test/libc/calls/stat_test.c b/test/libc/calls/stat_test.c index b32ed2306..61526d11f 100644 --- a/test/libc/calls/stat_test.c +++ b/test/libc/calls/stat_test.c @@ -17,6 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/metastat.internal.h" #include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" @@ -24,6 +26,7 @@ #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/nr.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -68,18 +71,34 @@ static long Stat(const char *path, struct stat *st) { return ax; } +static long Fstatat(const char *path, struct stat *st) { + long ax, di, si, dx; + register long r10 asm("r10") = 0; + asm volatile("syscall" + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(r10) + : "0"(__NR_fstatat), "1"(AT_FDCWD), "2"(path), "3"(st) + : "rcx", "r8", "r9", "r11", "memory", "cc"); + return ax; +} + BENCH(stat, bench) { struct stat st; + union metastat ms; EXPECT_SYS(0, 0, makedirs(".python/test", 0755)); + EZBENCH2("__stat2cosmo", donothing, __stat2cosmo(&st, &ms)); EXPECT_SYS(0, 0, touch(".python/test/" "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", 0644)); - if (!IsWindows()) { + if (!IsWindows() && !IsFreebsd()) { EZBENCH2("stat syscall", donothing, Stat(".python/test/" "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", &st)); + EZBENCH2("fstatat syscall", donothing, + Fstatat(".python/test/" + "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", + &st)); } EZBENCH2("stat() fs", donothing, stat(".python/test/" diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 1f56f6b38..316517888 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -19,8 +19,11 @@ #include "libc/bits/bits.h" #include "libc/bits/xchg.internal.h" #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/fmt/fmt.h" #include "libc/intrin/kprintf.h" +#include "libc/linux/mmap.h" +#include "libc/linux/munmap.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/rand/rand.h" @@ -33,6 +36,7 @@ #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -281,3 +285,57 @@ TEST(mmap, sharedFileMapFork) { EXPECT_NE(-1, close(fd)); EXPECT_NE(-1, unlink(path)); } + +//////////////////////////////////////////////////////////////////////////////// +// BENCHMARKS + +#define N (EZBENCH_COUNT * EZBENCH_TRIES) + +int count; +void *ptrs[N]; + +void BenchUnmap(void) { + int i; + for (i = 0; i < count; ++i) { + if (ptrs[i]) { + ASSERT_EQ(0, munmap(ptrs[i], FRAMESIZE)); + } + } +} + +void BenchMmapPrivate(void) { + void *p; + p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0); + if (p == MAP_FAILED) abort(); + ptrs[count++] = p; +} + +BENCH(mmap, bench) { + EZBENCH2("mmap", donothing, BenchMmapPrivate()); + BenchUnmap(); +} + +void BenchUnmapLinux(void) { + int i; + for (i = 0; i < count; ++i) { + if (ptrs[i]) { + ASSERT_EQ(0, LinuxMunmap(ptrs[i], FRAMESIZE)); + } + } +} + +void BenchMmapPrivateLinux(void) { + void *p; + p = (void *)LinuxMmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) abort(); + ptrs[count++] = p; +} + +BENCH(mmap, benchLinux) { + void *p; + if (!IsLinux()) return; + EZBENCH2("mmap (linux)", donothing, BenchMmapPrivateLinux()); + BenchUnmapLinux(); +} diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 6c7a727f3..7d04b7c43 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -193,13 +193,14 @@ Copyright 2010-2013 Pieter Noordhuis \""); #define DEBUG(L, ...) (void)0 #endif -#define DUFF_ROUTINE_LOOP 0 -#define DUFF_ROUTINE_START 5 +#define DUFF_ROUTINE_LOOP 0 +#define DUFF_ROUTINE_SEARCH 1 +#define DUFF_ROUTINE_START 5 #define DUFF_ROUTINE_LABEL(STATE) \ case STATE: \ linenoiseRefreshLineForce(l); \ - l->state = DUFF_ROUTINE_LOOP + l->state = STATE #define DUFF_ROUTINE_READ(STATE) \ DUFF_ROUTINE_LABEL(STATE); \ @@ -469,11 +470,11 @@ static const char *FindSubstringReverse(const char *p, size_t n, const char *q, n -= m; do { for (i = 0; i < m; ++i) { - if (p[n + i] != q[i]) { + if (kToLower[p[n + i] & 255] != kToLower[q[i] & 255]) { break; } } - if (i == m) { + if (kToLower[i & 255] == kToLower[m & 255]) { return p + n; } } while (n--); @@ -1852,7 +1853,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, char seq[16]; gotint = 0; - if (prompt && (!l->prompt || strcmp(prompt, l->prompt))) { + if (prompt && l->state != DUFF_ROUTINE_SEARCH && + (!l->prompt || strcmp(prompt, l->prompt))) { free(l->prompt); l->prompt = strdup(prompt); } @@ -1885,7 +1887,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, for (fail = l->matlen = 0;;) { free(l->prompt); l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen); - DUFF_ROUTINE_READ(1); + DUFF_ROUTINE_READ(DUFF_ROUTINE_SEARCH); fail = 1; added = 0; l->j = l->pos; @@ -1906,7 +1908,6 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, } else if (seq[0] == CTRL('G')) { linenoiseEditHistoryGoto(l, l->oldindex); l->pos = l->olderpos; - rc = 0; break; } else if (iswcntrl(seq[0])) { // only sees canonical c0 break; @@ -1953,7 +1954,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, rc = 0; linenoiseFreeCompletions(&l->lc); i = Backwards(l, l->pos, iswname); - j = Forwards(l, l->pos, iswname); + j = l->pos; { char *s = strndup(l->buf + i, j - i); completionCallback(s, &l->lc); @@ -1966,7 +1967,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, if (linenoiseGrow(l, n + 1)) { memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1); memcpy(l->buf + i, l->lc.cvec[0], m); - l->len = l->pos = n; + l->pos = i + m; + l->len = n; } continue; } diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h index 10a0bbc04..6b8980b8b 100644 --- a/third_party/lua/cosmo.h +++ b/third_party/lua/cosmo.h @@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_ char *LuaFormatStack(lua_State *) dontdiscard; int LuaCallWithTrace(lua_State *, int, int, lua_State *); -int LuaEncodeJsonData(lua_State *, char **, int, char *); -int LuaEncodeLuaData(lua_State *, char **, int, char *); +int LuaEncodeJsonData(lua_State *, char **, int, char *, int); +int LuaEncodeLuaData(lua_State *, char **, int, char *, int); int LuaEncodeUrl(lua_State *); int LuaParseUrl(lua_State *); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); diff --git a/third_party/lua/escapeluastring.c b/third_party/lua/escapeluastring.c index 9574b80cc..24d9f730c 100644 --- a/third_party/lua/escapeluastring.c +++ b/third_party/lua/escapeluastring.c @@ -16,20 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/stdio/append.internal.h" +#include "libc/str/str.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lua.h" void EscapeLuaString(char *s, size_t len, char **buf) { appendw(buf, '"'); for (size_t i = 0; i < len; i++) { - if (s[i] == '\\' || s[i] == '\"' || s[i] == '\n' || s[i] == '\0' || - s[i] == '\r') { + if (' ' <= s[i] && s[i] <= 0x7e) { + appendw(buf, s[i]); + } else if (s[i] == '\n') { + appendw(buf, '\\' | 'n' << 8); + } else if (s[i] == '\\' || s[i] == '\'' || s[i] == '\"') { + appendw(buf, '\\' | s[i] << 8); + } else { appendw(buf, '\\' | 'x' << 010 | "0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 | "0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030); - } else { - appendd(buf, s + i, 1); } } appendw(buf, '"'); diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 0fb0d1372..cf3d131e4 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -32,6 +32,7 @@ #include "libc/intrin/kprintf.h" #include "libc/intrin/nomultics.internal.h" #include "libc/log/check.h" +#include "libc/macros.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" @@ -50,6 +51,17 @@ Copyright 1994–2021 Lua.org, PUC-Rio.\""); asm(".include \"libc/disclaimer.inc\""); +static const char *const kKeywordHints[] = { + "else", // + "elseif", // + "function", // + "function", // + "repeat", // + "then", // + "until", // + "while", // +}; + bool lua_repl_blocking; bool lua_repl_isterminal; linenoiseCompletionCallback *lua_repl_completions_callback; @@ -74,46 +86,59 @@ static const char *g_historypath; #define LUA_MAXINPUT 512 #endif -static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) { - char **p = c->cvec; - size_t n = c->len + 1; - if ((p = realloc(p, n * sizeof(*p)))) { - p[n - 1] = s; + +static void lua_readline_addcompletion (linenoiseCompletions *c, const char *s) { + char **p, *t; + if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) { c->cvec = p; - c->len = n; + if ((t = strdup(s))) { + c->cvec[c->len++] = t; + } } } -void lua_readline_completions(const char *p, linenoiseCompletions *c) { + +void lua_readline_completions (const char *p, linenoiseCompletions *c) { + int i; lua_State *L; const char *name; + for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { + if (startswithi(kKeywordHints[i], p)) { + lua_readline_addcompletion(c, kKeywordHints[i]); + } + } L = globalL; lua_pushglobaltable(L); lua_pushnil(L); while (lua_next(L, -2) != 0) { name = lua_tostring(L, -2); if (startswithi(name, p)) { - lua_readline_addcompletion(c, strdup(name)); + lua_readline_addcompletion(c, name); } lua_pop(L, 1); } lua_pop(L, 1); - lua_repl_completions_callback(p, c); + if (lua_repl_completions_callback) { + lua_repl_completions_callback(p, c); + } } -char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) { - char *h = 0; + +char *lua_readline_hint (const char *p, const char **ansi1, const char **ansi2) { + char *h; linenoiseCompletions c = {0}; lua_readline_completions(p, &c); - if (c.len == 1) h = strdup(c.cvec[0] + strlen(p)); + h = c.len == 1 ? strdup(c.cvec[0] + strlen(p)) : 0; linenoiseFreeCompletions(&c); return h; } + static void lua_freeline (lua_State *L, char *b) { free(b); } + /* ** Return the string to be used as a prompt by the interpreter. Leave ** the string (or nil, if using the default value) on the stack, to keep @@ -129,6 +154,7 @@ static const char *get_prompt (lua_State *L, int firstline) { } } + /* mark in error messages for incomplete statements */ #define EOFMARK "" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) @@ -165,7 +191,7 @@ static ssize_t pushline (lua_State *L, int firstline) { prmt = strdup(get_prompt(L, firstline)); lua_pop(L, 1); /* remove prompt */ LUA_REPL_UNLOCK; - rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); + rc = linenoiseEdit(lua_repl_linenoise, 0, &b, !firstline || lua_repl_blocking); free(prmt); if (rc != -1) { if (b && *b) { @@ -187,10 +213,11 @@ static ssize_t pushline (lua_State *L, int firstline) { l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ - if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ + if (firstline && b[0] == '=') { /* for compatibility with 5.2, ... */ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ - else + } else { lua_pushlstring(L, b, l); + } lua_freeline(L, b); return 1; } diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 2fc49188b..4b892d8d4 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -80,6 +80,14 @@ o/$(MODE)/third_party/lua/lua.com: \ @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/third_party/lua/.lua/.symtab +o//third_party/lua/lgc.o: \ + OVERRIDE_CFLAGS += \ + -O2 + +o/$(MODE)/third_party/lua/lvm.o: \ + OVERRIDE_CFLAGS += \ + -fno-gcse + o/$(MODE)/third_party/lua/lauxlib.o: \ OVERRIDE_CFLAGS += \ -DSTACK_FRAME_UNLIMITED @@ -89,6 +97,8 @@ $(THIRD_PARTY_LUA_OBJS): \ -ffunction-sections \ -fdata-sections +$(THIRD_PARTY_LUA_OBJS): third_party/lua/lua.mk + .PHONY: o/$(MODE)/third_party/lua o/$(MODE)/third_party/lua: \ $(THIRD_PARTY_LUA_BINS) \ diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c index 6cc64cd5a..d894ff0e5 100644 --- a/third_party/lua/luaencodejsondata.c +++ b/third_party/lua/luaencodejsondata.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/fmt/itoa.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/append.internal.h" #include "net/http/escape.h" @@ -23,63 +25,115 @@ #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" -int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen; +int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat, + int idx) { + char *s; bool isarray; - int t = lua_type(L, idx); - if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); - if (LUA_TSTRING == t) { - appendw(buf, '"'); - appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, idx), -1, 0))); - appendw(buf, '"'); - } else if (LUA_TNUMBER == t) { - appendf(buf, numformat, lua_tonumber(L, idx)); - } else if (LUA_TBOOLEAN == t) { - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - } else if (LUA_TTABLE == t) { - tbllen = lua_rawlen(L, idx); - // encode tables with numeric indices and empty tables as arrays - isarray = tbllen > 0 || // integer keys present - (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys - (lua_pop(L, 2), false); // pop key/value pushed by lua_next - appendw(buf, isarray ? '[' : '{'); - if (isarray) { - for (int i = 1; i <= tbllen; i++) { - if (i > 1) appendw(buf, ','); - lua_rawgeti(L, -1, i); // table/-2, value/-1 - LuaEncodeJsonData(L, buf, level - 1, numformat); - lua_pop(L, 1); - } - } else { - int i = 1; - lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { - if (!lua_isstring(L, -2)) - return luaL_argerror(L, 1, "expected number or string as key value"); - if (i++ > 1) appendw(buf, ','); + size_t tbllen, z; + char ibuf[21], fmt[] = "%.14g"; + if (level > 0) { + switch (lua_type(L, idx)) { + case LUA_TSTRING: + s = lua_tolstring(L, idx, &z); + s = EscapeJsStringLiteral(s, z, &z); appendw(buf, '"'); - if (lua_type(L, -2) == LUA_TSTRING) { - appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, -2), -1, 0))); + appendd(buf, s, z); + appendw(buf, '"'); + free(s); + return 0; + case LUA_TNIL: + appendw(buf, READ32LE("null")); + return 0; + case LUA_TFUNCTION: + appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TUSERDATA: + appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TLIGHTUSERDATA: + appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TTHREAD: + appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TNUMBER: + if (lua_isinteger(L, idx)) { + appendd(buf, ibuf, + FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf); } else { - // we'd still prefer to use lua_tostring on a numeric index, but can't - // use it in-place, as it breaks lua_next (changes numeric key to a - // string) - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - appends(buf, lua_tostring(L, idx)); // use the copy - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 + // TODO(jart): replace this api + while (*numformat == '%' || *numformat == '.' || + isdigit(*numformat)) { + ++numformat; + } + switch (*numformat) { + case 'a': + case 'g': + case 'f': + fmt[4] = *numformat; + break; + default: + return luaL_error(L, "numformat string not allowed"); + } + appendf(buf, fmt, lua_tonumber(L, idx)); } - appendw(buf, '"' | ':' << 010); - LuaEncodeJsonData(L, buf, level - 1, numformat); - lua_pop(L, 1); // table/-2, key/-1 - } - // stack: table/-1, as the key was popped by lua_next + return 0; + case LUA_TBOOLEAN: + appends(buf, lua_toboolean(L, idx) ? "true" : "false"); + return 0; + case LUA_TTABLE: + tbllen = lua_rawlen(L, idx); + // encode tables with numeric indices and empty tables as arrays + isarray = + tbllen > 0 || // integer keys present + (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys + (lua_pop(L, 2), false); // pop key/value pushed by lua_next + appendw(buf, isarray ? '[' : '{'); + if (isarray) { + for (size_t i = 1; i <= tbllen; i++) { + if (i > 1) appendw(buf, ','); + lua_rawgeti(L, -1, i); // table/-2, value/-1 + LuaEncodeJsonData(L, buf, level - 1, numformat, -1); + lua_pop(L, 1); + } + } else { + int i = 1; + lua_pushnil(L); // push the first key + while (lua_next(L, -2)) { + if (!lua_isstring(L, -2)) { + luaL_error(L, "expected number or string as key value"); + unreachable; + } + if (i++ > 1) appendw(buf, ','); + appendw(buf, '"'); + if (lua_type(L, -2) == LUA_TSTRING) { + s = lua_tolstring(L, -2, &z); + s = EscapeJsStringLiteral(s, z, &z); + appendd(buf, s, z); + free(s); + } else { + // we'd still prefer to use lua_tostring on a numeric index, but + // can't use it in-place, as it breaks lua_next (changes numeric + // key to a string) + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + s = lua_tolstring(L, idx, &z); + appendd(buf, s, z); // use the copy + lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1 + } + appendw(buf, '"' | ':' << 010); + LuaEncodeJsonData(L, buf, level - 1, numformat, -1); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + } + appendw(buf, isarray ? ']' : '}'); + return 0; + default: + luaL_error(L, "can't serialize value of this type"); + unreachable; } - appendw(buf, isarray ? ']' : '}'); - } else if (LUA_TNIL == t) { - appendd(buf, "null", 4); } else { - return luaL_argerror(L, 1, "can't serialize value of this type"); + luaL_error(L, "too many nested tables"); + unreachable; } - return 0; } diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c index fbee64e63..b07bf852c 100644 --- a/third_party/lua/luaencodeluadata.c +++ b/third_party/lua/luaencodeluadata.c @@ -16,51 +16,97 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/fmt/itoa.h" #include "libc/math.h" #include "libc/stdio/append.internal.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" -int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen, slen; +int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat, + int idx) { char *s; int ktype; - int t = lua_type(L, idx); - if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); - if (LUA_TSTRING == t) { - s = lua_tolstring(L, idx, &slen); - EscapeLuaString(s, slen, buf); - } else if (LUA_TNUMBER == t) { - appendf(buf, numformat, lua_tonumber(L, idx)); - } else if (LUA_TBOOLEAN == t) { - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - } else if (LUA_TTABLE == t) { - appendw(buf, '{'); - int i = 0; - lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { - ktype = lua_type(L, -2); - if (ktype == LUA_TTABLE) - return luaL_argerror(L, 1, "can't serialize key of this type"); - if (i++ > 0) appendd(buf, ",", 1); - if (ktype != LUA_TNUMBER || floor(lua_tonumber(L, -2)) != i) { - appendw(buf, '['); - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - LuaEncodeLuaData(L, buf, level, numformat); - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 - appendw(buf, ']' | '=' << 010); - } - LuaEncodeLuaData(L, buf, level - 1, numformat); - lua_pop(L, 1); // table/-2, key/-1 + lua_Integer i; + size_t tbllen, buflen, slen; + char ibuf[21], fmt[] = "%.14g"; + if (level > 0) { + switch (lua_type(L, idx)) { + case LUA_TNIL: + appendw(buf, READ32LE("nil")); + return 0; + case LUA_TSTRING: + s = lua_tolstring(L, idx, &slen); + EscapeLuaString(s, slen, buf); + return 0; + case LUA_TFUNCTION: + appendf(buf, "\"func@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TUSERDATA: + appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TLIGHTUSERDATA: + appendf(buf, "\"light@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TTHREAD: + appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx)); + return 0; + case LUA_TNUMBER: + if (lua_isinteger(L, idx)) { + appendd(buf, ibuf, + FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf); + } else { + // TODO(jart): replace this api + while (*numformat == '%' || *numformat == '.' || + isdigit(*numformat)) { + ++numformat; + } + switch (*numformat) { + case 'a': + case 'g': + case 'f': + fmt[4] = *numformat; + break; + default: + return luaL_error(L, "numformat string not allowed"); + } + appendf(buf, fmt, lua_tonumber(L, idx)); + } + return 0; + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) { + appendw(buf, READ32LE("true")); + } else { + appendw(buf, READ64LE("false\0\0")); + } + return 0; + case LUA_TTABLE: + i = 0; + appendw(buf, '{'); + lua_pushnil(L); // push the first key + while (lua_next(L, -2) != 0) { + ktype = lua_type(L, -2); + if (i++ > 0) appendw(buf, ','); + if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) { + appendw(buf, '['); + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + LuaEncodeLuaData(L, buf, level - 1, numformat, -1); + lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 + appendw(buf, ']' | '=' << 010); + } + LuaEncodeLuaData(L, buf, level - 1, numformat, -1); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + appendw(buf, '}'); + return 0; + default: + luaL_error(L, "can't serialize value of this type"); + unreachable; } - // stack: table/-1, as the key was popped by lua_next - appendw(buf, '}'); - } else if (LUA_TNIL == t) { - appendd(buf, "nil", 3); } else { - return luaL_argerror(L, 1, "can't serialize value of this type"); + luaL_error(L, "too many nested tables"); + unreachable; } - return 0; } diff --git a/third_party/lua/luaformatstack.c b/third_party/lua/luaformatstack.c index 936a658f2..bf155443c 100644 --- a/third_party/lua/luaformatstack.c +++ b/third_party/lua/luaformatstack.c @@ -18,31 +18,17 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/stdio/append.internal.h" #include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" dontdiscard char *LuaFormatStack(lua_State *L) { + size_t l; int i, top; - char *b = 0; + char *p, *b = 0; top = lua_gettop(L); for (i = 1; i <= top; i++) { if (i > 1) appendw(&b, '\n'); appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i)); - switch (lua_type(L, i)) { - case LUA_TNUMBER: - appendf(&b, "%g", lua_tonumber(L, i)); - break; - case LUA_TSTRING: - appends(&b, lua_tostring(L, i)); - break; - case LUA_TBOOLEAN: - appends(&b, lua_toboolean(L, i) ? "true" : "false"); - break; - case LUA_TNIL: - appends(&b, "nil"); - break; - default: - appendf(&b, "%p", lua_topointer(L, i)); - break; - } + LuaEncodeLuaData(L, &b, 64, "g", -1); } return b; } diff --git a/tool/build/blinkenlights.c b/tool/build/blinkenlights.c index 620ddae04..6bdc99e08 100644 --- a/tool/build/blinkenlights.c +++ b/tool/build/blinkenlights.c @@ -2270,8 +2270,7 @@ static void OnVidyaServiceTeletypeOutput(void) { char buf[12]; n = 0 /* FormatCga(m->bx[0], buf) */; w = tpenc(VidyaServiceXlatTeletype(m->ax[0])); - do - buf[n++] = w; + do buf[n++] = w; while ((w >>= 8)); PtyWrite(pty, buf, n); } diff --git a/tool/build/build.mk b/tool/build/build.mk index eb949f080..b2d1b9eb9 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -89,6 +89,7 @@ o/$(MODE)/tool/build/blinkenlights.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +.PRECIOUS: o/$(MODE)/tool/build/blinkenlights.com o/$(MODE)/tool/build/blinkenlights.com: \ o/$(MODE)/tool/build/blinkenlights.com.dbg \ o/$(MODE)/third_party/zip/zip.com \ diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index 611eab9bf..11ca45aa3 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -88,6 +88,14 @@ else Write('
%s\r\n' % {enabled}) end +errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') +if errno then + Write('
%s\r\n' % {EscapeHtml(unix.strerrno(errno))}) +else + Write('
%s\r\n' % {enabled}) +end + errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') if errno then diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index 5e0fbe7b9..f4a8b3ea5 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -13,9 +13,9 @@ function main() syscall = 'pipe' reader, writer, errno = unix.pipe() if reader then - -- oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) - -- oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) - -- oldmask = unix.sigprocmask(unix.SIG_BLOCK, unix.SIGCHLD) + oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) + oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) + oldmask = unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1))) syscall = 'fork' child, errno = unix.fork() if child then diff --git a/tool/net/help.txt b/tool/net/help.txt index 47da46e38..9a1c60ce4 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -42,6 +42,7 @@ FLAGS -u uniprocess -z print port -m log messages + -i interpreter mode -b log message bodies -a log resource usage -g log handler latency @@ -236,9 +237,9 @@ USAGE REPL - Your redbean displays a REPL that lets you modify the state of the - main server process while your server is running. Any changes will - propagate into forked clients. + Your redbean displays a Read-Eval-Print-Loop that lets you modify the + state of the main server process while your server is running. Any + changes will propagate into forked clients. Your REPL is displayed only when redbean is run as a non-daemon in a UNIX terminal or the Windows 10 command prompt or powershell. Since @@ -250,6 +251,20 @@ REPL A history of your commands is saved to `~/.redbean_history`. + If you love the redbean repl and want to use it as your language + interpreter then you can pass the `-i` flag to put redbean into + interpreter mode. + + redbean.com -i binarytrees.lua 15 + + In this mode redbean won't start a web server and instead functions + like the `lua` command. The first command line argument becomes the + script you want to run. If you don't supply a script, then the repl + without a web server is displayed. + + This can be useful for testing, since the redbean extensions and + modules for the Lua language are still made available. + SECURITY @@ -357,12 +372,36 @@ SPECIAL PATHS GLOBALS - argv: array[str] + arg: array[str] + Array of command line arguments, excluding those parsed by getopt() in the C code, which stops parsing at the first non-hyphenated arg. In some cases you can use the magic -- argument to delimit C from Lua arguments. + For example, if you launch your redbean as follows: + + redbean.com -v arg1 arg2 + + Then your `/.init.lua` file will have the `arg` array like: + + arg[-1] = '/usr/bin/redbean.com' + arg[ 0] = '/zip/.init.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + + If you launch redbean in interpreter mode (rather than web + server) mode, then an invocation like this: + + ./redbean.com -i script.lua arg1 arg2 + + Would have an `arg` array like this: + + arg[-1] = './redbean.com' + arg[ 0] = 'script.lua' + arg[ 1] = 'arg1' + arg[ 2] = 'arg2' + HOOKS @@ -522,7 +561,8 @@ 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. - - numformat: (string="%.14g") sets numeric format to be used. + - 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 @@ -531,7 +571,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. - - numformat: (string="%.14g") sets numeric format to be used. - maxdepth: (number=64) sets the max number of nested tables. EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str @@ -1329,52 +1368,25 @@ MAXMIND MODULE UNIX MODULE - This module exposes the low-level UNIX system call interface. The way - these functions work is they'll only throw a Lua exception if there's - some kind of error obtaining the required arguments. Once Lua reads - the arguments and the call is delegated to the system call interface, - all further errors won't be raised, but rather returned as errnos, - which should always be checked. For example, most syscalls follow: + This module exposes the low-level UNIX system call interface. This + module works on all supported platforms, including Windows NT. - errno = unix.foo(...) - if errno then - Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)}) - end - - Any POSIX API that's defined as returning 0 on success or -1 on error - is wrapped here to return nil on success and an integer on error. To - see which errnos are possible for which system calls, please see the - comprehensive index at the bottom of this section. - - In cases where POSIX defines an API as returning codes on success we - wrap the APIs as follows: - - rc, errno = unix.bar(...) - if rc then - Log(kLogWarn, 'foo() succeeded: %d' % {rc}) - else - Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)}) - end - - If the above code succeeds, `rc` will be non-nil and `errno` will be - `nil`. If the above code fails, `rc` will be nil and `errno` will be - an integer greater than zero. - - UNIX FUNCTIONS - - unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int] - - Reads from file descriptor. - - unix.write(fd:int, data[, offset:int]) → wrote:int[, errno:int] - - Writes to file descriptor. - - unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] + unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno Opens file. - `flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`. + Returns a file descriptor integer that needs to be closed, e.g. + + fd = assert(open("/etc/passwd", unix.O_RDONLY)) + print(unix.read(fd)) + unix.close(fd) + + `flags` should have one of: + + - `O_RDONLY`: open for reading + - `O_WRONLY`: open for writing + - `O_RDWR`: open for reading and writing + The following values may also be OR'd into `flags`: - `O_CREAT`: create file if it doesn't exist @@ -1412,10 +1424,18 @@ UNIX MODULE already. If it does exist then `nil` is returned along with `errno` set to `EEXIST`. - unix.close(fd:int) → errno:int + unix.close(fd:int) → ok:bool, unix.Errno Closes file descriptor. + unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, unix.Errno + + Reads from file descriptor. + + unix.write(fd:int, data[, offset:int]) → wrote:int, unix.Errno + + Writes to file descriptor. + unix.exit([exitcode]) → ⊥ Invokes `_Exit(exitcode)` on the process. This will immediately @@ -1443,21 +1463,27 @@ UNIX MODULE command prompt inserts multiple environment variables with empty string as keys, for its internal bookkeeping. - unix.fork() → childpid|0:int[, errno:int] + unix.fork() → childpid|0:int, unix.Errno Creates a new process mitosis style. This returns twice. The parent process gets the nonzero pid. The child gets zero. - unix.commandv(prog:str) → path:str[, errno:int] + unix.commandv(prog:str) → path:str, unix.Errno - Performs `$PATH` lookup of executable. We automatically suffix - `.com` and `.exe` for all platforms when path searching. - By default, the current directory is not on the path. - If `prog` is an absolute path, then it's returned as-is. If - `prog` contains slashes then it's not path searched either and - will be returned if it exists. + Performs `$PATH` lookup of executable. - unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → errno:int + unix = require "unix" + prog = assert(unix.commandv("ls")) + unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"}) + unix.exit(127) + + We automatically suffix `.com` and `.exe` for all platforms when + path searching. By default, the current directory is not on the + path. If `prog` is an absolute path, then it's returned as-is. If + `prog` contains slashes then it's not path searched either and will + be returned if it exists. + + unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → false, unix.Errno Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see @@ -1490,7 +1516,7 @@ UNIX MODULE `EAGAIN` is returned if you've enforced a max number of processes using `setrlimit(RLIMIT_NPROC)`. - unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, errno:int + unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno Duplicates file descriptor. @@ -1501,7 +1527,7 @@ UNIX MODULE `flags` can have `O_CLOEXEC` which means the returned file descriptors will be automatically closed upon execve(). - unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] + unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int Creates fifo which enables communication between processes. Returns two file descriptors: one for reading and one for @@ -1537,7 +1563,7 @@ UNIX MODULE end unix.wait([pid:int, options:int]) - → pid:int, wstatus:int, nil, errno:int + → pid:int, unix.Errno, wstatus:int Waits for subprocess to terminate. @@ -1590,22 +1616,22 @@ UNIX MODULE This function does not fail. - unix.kill(pid, sig) → errno:int + unix.kill(pid, sig) → ok:bool, unix.Errno Returns process id of current process. - unix.raise(sig) → rc:int[, errno:int] + unix.raise(sig) → rc:int, unix.Errno Triggers signal in current process. This is pretty much the same as `kill(getpid(), sig)`. - unix.access(path:str, how) → errno:int + unix.access(path:str, how) → ok:bool, unix.Errno Checks if effective user of current process has permission to access file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for read, write, execute, and existence respectively. - unix.mkdir(path:str, mode) → errno:int + unix.mkdir(path:str, mode) → ok:bool, unix.Errno Makes directory. @@ -1628,7 +1654,7 @@ UNIX MODULE Fails with `ENAMETOOLONG` if the path is too long. - unix.makedirs(path:str, mode) → errno:int + unix.makedirs(path:str, mode) → ok:bool, unix.Errno Makes directories. @@ -1639,48 +1665,48 @@ UNIX MODULE Unlike mkdir() this convenience wrapper will automatically create parent parent directories as needed. - unix.chdir(path:str) → errno:int + unix.chdir(path:str) → ok:bool, unix.Errno Changes current directory to `path`. - unix.unlink(path:str) → errno:int + unix.unlink(path:str) → ok:bool, unix.Errno Removes file at `path`. - unix.rmdir(path:str) → errno:int + unix.rmdir(path:str) → ok:bool, unix.Errno Removes empty directory at `path`. - unix.rename(oldpath:str, newpath:str) → errno:int + unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno Renames file or directory. - unix.link(existingpath:str, newpath:str) → errno:int + unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno Creates hard link, so your underlying inode has two names. - unix.symlink(target:str, linkpath:str) → errno:int + unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno Creates soft link, or a symbolic link. - unix.realpath(filename:str) → abspath:str[, errno:int] + unix.realpath(filename:str) → abspath:str, unix.Errno Returns absolute path of filename, with `.` and `..` components removed, and symlinks will be resolved. - unix.chown(path:str, uid, gid) → errno:int + unix.chown(path:str, uid, gid) → ok:bool, unix.Errno Changes user and gorup on file. - unix.chmod(path:str, mode) → errno:int + unix.chmod(path:str, mode) → ok:bool, unix.Errno Changes mode bits on file. - unix.getcwd() → path:str[, errno:int] + unix.getcwd() → path:str, unix.Errno Returns current working directory. - unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] + unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno Manipulates file descriptor. @@ -1691,27 +1717,27 @@ UNIX MODULE POSIX advisory locks can be controlled by setting `cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getsid(pid:int) → sid:int[, errno:int] + unix.getsid(pid:int) → sid:int, unix.Errno Gets session id. - unix.getpgrp() → pgid:int[, errno:int] + unix.getpgrp() → pgid:int, unix.Errno Gets process group id. - unix.setpgrp() → pgid:int[, errno:int] + unix.setpgrp() → pgid:int, unix.Errno Sets process group id. This is the same as `setpgid(0,0)`. - unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] + unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno Sets process group id the modern way. - unix.getpgid(pid) → pgid:int[, errno:int] + unix.getpgid(pid) → pgid:int, unix.Errno Gets process group id the modern wayp. - unix.setsid() → sid:int[, errno:int] + unix.setsid() → sid:int, unix.Errno Sets session id. @@ -1756,13 +1782,13 @@ UNIX MODULE This function does not fail. - unix.chroot(path:str) → errno:int + unix.chroot(path:str) → ok:bool, unix.Errno Changes root directory. Returns `ENOSYS` on Windows NT. - unix.setuid(uid:int) → errno:int + unix.setuid(uid:int) → ok:bool, unix.Errno Sets user id. @@ -1798,13 +1824,13 @@ UNIX MODULE Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. - unix.setgid(gid:int) → errno:int + unix.setgid(gid:int) → ok:bool, unix.Errno Sets group id. Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. - unix.setresuid(real:int, effective:int, saved:int) → errno:int + unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno Sets real, effective, and saved user ids. @@ -1813,7 +1839,7 @@ UNIX MODULE Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. - unix.setresgid(real:int, effective:int, saved:int) → errno:int + unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno Sets real, effective, and saved group ids. @@ -1854,30 +1880,40 @@ UNIX MODULE This function currently works on Linux, Windows, and NetBSD. On WIN32 it uses the ReportEvent() facility. - unix.clock_gettime([clock]) → seconds, nanos, errno:int + unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int Returns nanosecond precision timestamp from the system. - `clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or - `CLOCK_MONOTONIC_RAW` since they work across platforms. - You may also try your luck with `CLOCK_REALTIME_COARSE`, - `CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`, - `CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`, - `CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`, + `clock` can be any one of of: + + - `CLOCK_REALTIME`: universally supported + - `CLOCK_MONOTONIC`: universally supported + - `CLOCK_MONOTONIC_RAW`: nearly universally supported + - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd + - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd + - `CLOCK_REALTIME_COARSE`: : linux and openbsd + - `CLOCK_MONOTONIC_COARSE`: linux + - `CLOCK_PROF`: linux and netbsd + - `CLOCK_BOOTTIME`: linux and openbsd + - `CLOCK_REALTIME_ALARM`: linux-only + - `CLOCK_BOOTTIME_ALARM`: linux-only + - `CLOCK_TAI`: ilnux-only + + Returns `EINVAL` if clock isn't supported on platform. unix.nanosleep(seconds:int[, nanos:int]) - → remseconds:int, remnanos:int[, errno:int] + → remseconds:int, unix.Errno, remnanos:int Sleeps with nanosecond precision. unix.sync() - unix.fsync(fd:int) → errno:int - unix.fdatasync(fd:int) → errno:int + unix.fsync(fd:int) → ok:bool, unix.Errno + unix.fdatasync(fd:int) → ok:bool, unix.Errno These functions are used to make programs slower by asking the operating system to flush data to the physical medium. - unix.lseek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] + unix.lseek(fd:int, offset:int, whence:int) → newpos:int, unix.Errno Seeks to file position. @@ -1889,21 +1925,21 @@ UNIX MODULE Returns the new position relative to the start of the file. - unix.truncate(path:str[, length:int]) → errno:int + unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno Reduces or extends underlying physical medium of file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.ftruncate(fd:int[, length:int]) → errno:int + unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno Reduces or extends underlying physical medium of open file. If file was originally larger, content >length is lost. `length` defaults to zero. - unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] + unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int, unix.Errno `family` defaults to `AF_INET` and can be: @@ -1929,14 +1965,14 @@ UNIX MODULE `SOCK_CLOEXEC` may be bitwise or'd into `type`. unix.socketpair([family:int[, type:int[, protocol:int]]]) - → fd1:int, fd2:int[, errno:int] + → fd1:int, unix.Errno, fd2:int `SOCK_CLOEXEC` may be or'd into type `family` defaults to `AF_INET` `type` defaults to `SOCK_STREAM` `protocol` defaults to `IPPROTO_TCP` - unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int + unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno Binds socket. @@ -1970,19 +2006,21 @@ UNIX MODULE Further note that calling `unix.bind(sock)` is equivalent to not calling bind() at all, since the above behavior is the default. - unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, errno:int] + unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno Returns list of network adapter addresses. - unix.getsockopt(fd:int, level:int, optname:int) → errno:int, ... - unix.setsockopt(fd:int, level:int, optname:int, ...) → errno:int + unix.getsockopt(fd:int, level:int, optname:int) → ok:bool, unix.Errno, ... + unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno Tunes networking parameters. - `level` and `optname` may be one of the following. Please note the - type signature for getsockopt() changes depending on these values: + `level` and `optname` may be one of the following. The ellipses type + signature above changes depending on which options are used. + - `SOL_SOCKET` + `SO_TYPE`: bool - `SOL_SOCKET` + `SO_DEBUG`: bool + - `SOL_SOCKET` + `SO_ACCEPTCONN`: bool - `SOL_SOCKET` + `SO_BROADCAST`: bool - `SOL_SOCKET` + `SO_REUSEADDR`: bool - `SOL_SOCKET` + `SO_REUSEPORT`: bool @@ -2016,15 +2054,8 @@ UNIX MODULE Returns `ENOSYS` if setting isn't supported by the host o/s. - NOTE: The API for this function diverges from the the norm. `errno` - needs to come first in the results, because otherwise we - wouldn't know the arity of items to push before it. It's - because Cosmopolitan Libc polyfills the magic numbers above as - zero if the host operating system doesn't support them. If - there's no error, then `errno` will be set to nil. - unix.poll({fd:int=events:int, ...}[, timeoutms:int]) - → {fd:int=revents:int, ...}[, errno:int] + → {fd:int=revents:int, ...}, unix.Errno Checks for events on a set of file descriptors. @@ -2037,19 +2068,25 @@ UNIX MODULE then that means block as long as it takes until there's an event or an interrupt. If the timeout expires, an empty table is returned. - unix.gethostname() → host:str[, errno:int] + unix.gethostname() → host:str, unix.Errno Returns hostname of system. - unix.listen(fd:int[, backlog]) → errno:int + unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno Begins listening for incoming connections on a socket. - unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int] + unix.accept(serverfd:int[, flags:int]) + → clientfd:int, unix.Errno, ip:uint32, port:uint16 Accepts new client socket descriptor for a listening tcp socket. - unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int] + `flags` can have any of: + + - `SOCK_CLOEXEC` + - `SOCK_NONBLOCK` + + unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno Connects a TCP socket to a remote host. @@ -2057,24 +2094,34 @@ UNIX MODULE remembers the intended address so that send() or write() may be used rather than sendto(). - unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] + unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 Retrieves the local address of a socket. - unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] + unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 Retrieves the remote address of a socket. - unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] + unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + `flags` can have: + + - `MSG_WAITALL` + - `MSG_DONTROUTE` + - `MSG_PEEK` + - `MSG_OOB` unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) - → data:str, ip:uint32, port:uint16, errno:int + → data:str, unix.Errno, ip:uint32, port:uint16 - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + `flags` can have: - unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] + - `MSG_WAITALL` + - `MSG_DONTROUTE` + - `MSG_PEEK` + - `MSG_OOB` + + unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno This is the same as `write` except it has a `flags` argument that's intended for sockets. @@ -2082,24 +2129,36 @@ UNIX MODULE `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int]) - → sent:int, errno:int + → sent:int, unix.Errno This is useful for sending messages over UDP sockets to specific addresses. `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. - unix.shutdown(fd:int, how:int) → errno:int + unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or `SHUT_RDWR`. - unix.sigprocmask(how[, mask]) → oldmask, errno:int + unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno - `how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` + Manipulates bitset of signals blocked by process. + + `how` can be one of: + + - `SIG_BLOCK`: bitwise ors `mask` into set of blocked signals + - `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals + - `SIG_SETMASK`: replaces process signal mask with `mask` + + `mask` is a word encoded bitset of signals. Valid signal numbers + start at 1 and vary between platforms. The most famous `SIGKILL` + can't be masked, but if it could, it's assigned the number `9` + across all platforms, so if you wanted to add it to a bitset, you + would say, `1 << 8` or in general terms `1 << (sig - 1)`. unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) - → oldhandler:func|int, flags:int, mask:int, errno:int + → oldhandler:func|int, unix.Errno, flags:int, mask:int `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. @@ -2115,7 +2174,7 @@ UNIX MODULE It's a good idea to not do too much work in a signal handler. - unix.sigsuspend([mask:int]) → errno:int + unix.sigsuspend([mask]) → false, unix.Errno Waits for signal to be delivered. @@ -2123,7 +2182,7 @@ UNIX MODULE system call. `mask` specifies which signals should be blocked. unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) - → intsec, intns, valsec, valns, errno:int + → intsec, unix.Errno, intns, valsec, valns Causes `SIGALRM` signals to be generated at some point(s) in the future. The `which` parameter should be `ITIMER_REAL`. @@ -2145,17 +2204,17 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) - unix.strerrno(errno:int) → str + unix.strerrno(unix.Errno) → str Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If `errno` isn't known, this function returns nil. - unix.strerdoc(errno:int) → str + unix.strerdoc(unix.Errno) → str Turns `errno` code into a descriptive string. If `errno` isn't known, this function returns nil. - unix.strerror(errno:int) → str + unix.strerror(unix.Errno) → str Turns `errno` code into longest string describing the error. This includes the output of both strerrno() and strerror() as well as the @@ -2167,7 +2226,7 @@ UNIX MODULE Turns platform-specific `sig` code into its name, e.g. `strsignal(9)` always returns `"SIGKILL"`. - unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int + unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno Changes resource limit. @@ -2198,72 +2257,79 @@ UNIX MODULE 127. On most platforms these limits are enforced by the kernel and as such are inherited by subprocesses. - unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] + unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int Returns information about resource limit. - unix.stat(x) → UnixStat*[, errno:int] + unix.stat(x) → unix.Stat, Errno* Gets information about file or directory. `x` may be a file or directory path string, or it may be a file descriptor int that was made by open(). - unix.opendir(path:str) → UnixDir*[, errno:int] + unix.opendir(path:str) → unix.Dir, Errno* Opens directory for listing its contents. - unix.fdopendir(fd:int) → UnixDir*[, errno:int] + unix.fdopendir(fd:int) → unix.Dir, Errno* Opens directory for listing its contents, via an fd. `fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The - returned UnixDir* ownership takes ownership of the file descriptor + returned unix.Dir ownership takes ownership of the file descriptor and will close it automatically when garbage collected. UNIX DIR OBJECT - UnixDir* objects are created by opendir() or fdopendir(). The + unix.Dir objects are created by opendir() or fdopendir(). The following methods are available: - UnixDir:close() → errno:int + unix.Dir:close() → ok:bool, unix.Errno may be called multiple times called by the garbage collector too - UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] + unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int Returns `nil` if there are no more entries. Or error, `nil` will be returned and `errno` will be non-nil. - `kind` can be `DT_UNKNOWN`, `DT_REG`, `DT_DIR`, `DT_BLK`, - `DT_LNK`, `DT_CHR`, `DT_FIFO`, or `DT_SOCK`. + `kind` can be any of: - UnixDir:fd() → fd:int[, errno:int] + - `DT_UNKNOWN` + - `DT_REG` + - `DT_DIR` + - `DT_BLK` + - `DT_LNK` + - `DT_CHR` + - `DT_FIFO` + - `DT_SOCK` + + unix.Dir:fd() → fd:int, unix.Errno Returns file descriptor of open directory object. Returns `EOPNOTSUPP` if using a `/zip/...` path. - Returns `EOPNOTSUPP` if using Windows NT. - UnixDir:tell() → offset:int + unix.Dir:tell() → offset:int Returns current arbitrary offset into stream. - UnixDir:rewind() + unix.Dir:rewind() Resets stream back to beginning. UNIX STAT OBJECT - UnixStat* objects are created by stat() or fstat(). The following + unix.Stat objects are created by stat() or fstat(). The following methods are available: - UnixStat:size() → bytes:int + unix.Stat:size() → bytes:int Size of file in bytes. - UnixStat:mode() → mode:int + unix.Stat:mode() → mode:int Contains file type and permissions. @@ -2280,49 +2346,49 @@ UNIX MODULE - `(st:mode() & 0170000) == 0120000` means symbolic link - `(st:mode() & 0170000) == 0140000` means socket - UnixStat:atim() → secs:int, nanos:int + unix.Stat:atim() → secs:int, nanos:int Size of file in bytes. - UnixStat:uid() → int + unix.Stat:uid() → int User ID of file owner. - UnixStat:gid() → int + unix.Stat:gid() → int Group ID of file owner. - UnixStat:mtim() → secs:int, nanos:int + unix.Stat:mtim() → secs:int, nanos:int Last modified time. - UnixStat:birthtim() → secs:int, nanos:int + unix.Stat:birthtim() → secs:int, nanos:int Creation time. Note that on Linux this is the mimimum of atom/mtim/ctim. - UnixStat:ctim() → secs:int, nanos:int + unix.Stat:ctim() → secs:int, nanos:int Complicated time. Means time file status was last changed on UNIX. Means creation time on Windows. - UnixStat:blocks() → int + unix.Stat:blocks() → int Number of blocks used by storage medium. - UnixStat:blksize() → int + unix.Stat:blksize() → int Block size is usually 4096 for file system files. - UnixStat:dev() → int + unix.Stat:dev() → int ID of device containing file. - UnixStat:ino() → int + unix.Stat:ino() → int Inode number. - UnixStat:rdev() → int + unix.Stat:rdev() → int Device ID (if special file) diff --git a/tool/net/largon2.c b/tool/net/largon2.c index 157ae854d..cb0f4e208 100644 --- a/tool/net/largon2.c +++ b/tool/net/largon2.c @@ -531,16 +531,16 @@ luaopen_argon2(lua_State *L) largon2_push_argon2_variants_table(L); lua_setfield(L, -2, "variants"); - lua_pushstring(L, "3.0.1"); + lua_pushliteral(L, "3.0.1"); lua_setfield(L, -2, "_VERSION"); - lua_pushstring(L, "Thibault Charbonnier"); + lua_pushliteral(L, "Thibault Charbonnier"); lua_setfield(L, -2, "_AUTHOR"); - lua_pushstring(L, "MIT"); + lua_pushliteral(L, "MIT"); lua_setfield(L, -2, "_LICENSE"); - lua_pushstring(L, "https://github.com/thibaultcha/lua-argon2"); + lua_pushliteral(L, "https://github.com/thibaultcha/lua-argon2"); lua_setfield(L, -2, "_URL"); return 1; diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index a58760bd5..d1be7df68 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -18,10 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/scale/cdecimate2xuint8x8.h" #include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/rusage.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/bench.h" #include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" @@ -31,6 +35,7 @@ #include "libc/runtime/gc.internal.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/rusage.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "net/http/escape.h" @@ -48,6 +53,10 @@ #include "third_party/mbedtls/sha512.h" #include "tool/net/lfuncs.h" +static int Rdpid(void) { + return rdpid(); +} + int LuaGetTime(lua_State *L) { lua_pushnumber(L, nowl()); return 1; @@ -64,12 +73,12 @@ int LuaRdtsc(lua_State *L) { } int LuaGetCpuNode(lua_State *L) { - lua_pushinteger(L, TSC_AUX_NODE(rdpid())); + lua_pushinteger(L, TSC_AUX_NODE(Rdpid())); return 1; } int LuaGetCpuCore(lua_State *L) { - lua_pushinteger(L, TSC_AUX_CORE(rdpid())); + lua_pushinteger(L, TSC_AUX_CORE(Rdpid())); return 1; } @@ -560,3 +569,44 @@ void LuaPushUrlView(lua_State *L, struct UrlView *v) { lua_pushnil(L); } } + +static int64_t GetInterrupts(void) { + struct rusage ru; + if (!getrusage(RUSAGE_SELF, &ru)) { + return ru.ru_nivcsw; + } else { + return 0; + } +} + +int LuaBenchmark(lua_State *L) { + double avgticks; + uint64_t t1, t2; + int64_t interrupts; + int core, iter, count, tries, attempts, maxattempts; + luaL_checktype(L, 1, LUA_TFUNCTION); + count = luaL_optinteger(L, 2, 100); + maxattempts = luaL_optinteger(L, 3, 10); + for (attempts = 0;;) { + sched_yield(); + core = TSC_AUX_CORE(Rdpid()); + interrupts = GetInterrupts(); + for (avgticks = iter = 1; iter < count; ++iter) { + t1 = __startbench(); + lua_pushvalue(L, 1); + lua_call(L, 0, 0); + t2 = __endbench(); + avgticks += 1. / iter * ((int)(t2 - t1) - avgticks); + } + ++attempts; + if (TSC_AUX_CORE(Rdpid()) == core && GetInterrupts() == interrupts) { + break; + } else if (attempts >= maxattempts) { + return luaL_error(L, "system is under too much load to run benchmark"); + } + } + lua_pushnumber(L, ConvertTicksToNanos(avgticks)); + lua_pushinteger(L, avgticks); + lua_pushinteger(L, attempts); + return 3; +} diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index d776272de..b731d8a03 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -11,6 +11,7 @@ int LuaUnix(lua_State *); int luaopen_argon2(lua_State *); int luaopen_lsqlite3(lua_State *); +int LuaBenchmark(lua_State *); int LuaBsf(lua_State *); int LuaBsr(lua_State *); int LuaCategorizeIp(lua_State *); diff --git a/tool/net/lsqlite3.c b/tool/net/lsqlite3.c index ee749d61b..3757f4811 100644 --- a/tool/net/lsqlite3.c +++ b/tool/net/lsqlite3.c @@ -1936,7 +1936,7 @@ static const luaL_Reg sqlitelib[] = { static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { luaL_newmetatable(L, name); - lua_pushstring(L, "__index"); + lua_pushliteral(L, "__index"); lua_pushvalue(L, -2); /* push metatable */ lua_rawset(L, -3); /* metatable.__index = metatable */ diff --git a/tool/net/lunix.c b/tool/net/lunix.c index 677d98aa1..683c54ce5 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -72,6 +72,7 @@ #include "libc/sysv/errfuns.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" #include "third_party/lua/luaconf.h" @@ -82,14 +83,19 @@ * @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD */ +struct UnixDir { + int refs; + DIR *dir; +}; + struct UnixStat { int refs; struct stat st; }; -struct UnixDir { +struct UnixErrno { int refs; - DIR *dir; + int errno; }; static lua_State *GL; @@ -109,37 +115,56 @@ static dontinline int ReturnString(lua_State *L, const char *x) { return 1; } -static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { - lua_pushinteger(L, ts->tv_sec); - lua_pushinteger(L, ts->tv_nsec); - return 2; +static void LuaUnixPushErrno(lua_State *L, int err) { + // TODO: How do we solve "error object is a userdata value"? + lua_pushinteger(L, err); + return; + //////////////////////////////////////////////////////////// + struct UnixErrno *ue, **uep; + ue = xcalloc(1, sizeof(struct UnixErrno)); + ue->refs = 1; + ue->errno = err; + uep = lua_newuserdatauv(L, sizeof(*uep), 1); + luaL_setmetatable(L, "unix.Errno"); + *uep = ue; } -static int ReturnErrno(lua_State *L, int nils, int olderr) { +static dontinline int SysretErrnoImpl(lua_State *L, int olderr, bool usebool) { int i, newerr = errno; if (!IsTiny() && !(0 < newerr && newerr < (!IsWindows() ? 4096 : 65536))) { WARNF("errno should not be %d", newerr); } - for (i = 0; i < nils; ++i) { + if (usebool) { + lua_pushboolean(L, false); + } else { lua_pushnil(L); } - lua_pushinteger(L, newerr); + LuaUnixPushErrno(L, newerr); errno = olderr; - return nils + 1; + return 2; } -static int Return01(lua_State *L, int rc, int olderr) { +static dontinline int SysretErrnoNil(lua_State *L, int olderr) { + return SysretErrnoImpl(L, olderr, false); +} + +static dontinline int SysretErrnoBool(lua_State *L, int olderr) { + return SysretErrnoImpl(L, olderr, true); +} + +static int SysretBool(lua_State *L, int rc, int olderr) { if (!IsTiny() && (rc != 0 && rc != -1)) { WARNF("syscall supposed to return 0 / -1 but got %d", rc); } if (rc != -1) { + lua_pushboolean(L, true); return 0; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoBool(L, olderr); } } -static int ReturnRc(lua_State *L, int64_t rc, int olderr) { +static int SysretInteger(lua_State *L, int64_t rc, int olderr) { if (rc != -1) { if (!IsTiny() && olderr != errno) { WARNF("errno unexpectedly changed %d → %d", olderr, errno); @@ -147,7 +172,7 @@ static int ReturnRc(lua_State *L, int64_t rc, int olderr) { lua_pushinteger(L, rc); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } @@ -195,34 +220,38 @@ static char **ConvertLuaArrayToStringList(lua_State *L, int i) { //////////////////////////////////////////////////////////////////////////////// // System Calls +static dontinline int LuaUnixGetid(lua_State *L, int f(void)) { + return ReturnInteger(L, f()); +} + // unix.getpid() → pid:int static int LuaUnixGetpid(lua_State *L) { - return ReturnInteger(L, getpid()); + return LuaUnixGetid(L, getpid); } // unix.getppid() → pid:int static int LuaUnixGetppid(lua_State *L) { - return ReturnInteger(L, getppid()); + return LuaUnixGetid(L, getppid); } // unix.getuid() → uid:int static int LuaUnixGetuid(lua_State *L) { - return ReturnInteger(L, getuid()); + return LuaUnixGetid(L, getuid); } // unix.getgid() → gid:int static int LuaUnixGetgid(lua_State *L) { - return ReturnInteger(L, getgid()); + return LuaUnixGetid(L, getgid); } // unix.geteuid() → uid:int static int LuaUnixGeteuid(lua_State *L) { - return ReturnInteger(L, geteuid()); + return LuaUnixGetid(L, geteuid); } // unix.getegid() → gid:int static int LuaUnixGetegid(lua_State *L) { - return ReturnInteger(L, getegid()); + return LuaUnixGetid(L, getegid); } // unix.umask(mask:int) → oldmask:int @@ -235,86 +264,86 @@ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); } -// unix.access(path:str, how:int) → errno:int +// unix.access(path:str, how:int) → ok:bool, unix.Errno // how can be: R_OK, W_OK, X_OK, F_OK static int LuaUnixAccess(lua_State *L) { int olderr = errno; - return Return01(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, access(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.mkdir(path:str[, mode:int]) → errno:int +// unix.mkdir(path:str[, mode:int]) → ok:bool, unix.Errno // mode should be octal static int LuaUnixMkdir(lua_State *L) { int olderr = errno; - return Return01(L, mkdir(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), - olderr); + return SysretBool( + L, mkdir(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); } -// unix.makedirs(path:str[, mode:int]) → errno:int +// unix.makedirs(path:str[, mode:int]) → ok:bool, unix.Errno // mode should be octal static int LuaUnixMakedirs(lua_State *L) { int olderr = errno; - return Return01( + return SysretBool( L, makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)), olderr); } -// unix.chdir(path:str) → errno:int +// unix.chdir(path:str) → ok:bool, unix.Errno static int LuaUnixChdir(lua_State *L) { int olderr = errno; - return Return01(L, chdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, chdir(luaL_checkstring(L, 1)), olderr); } -// unix.unlink(path:str) → errno:int +// unix.unlink(path:str) → ok:bool, unix.Errno static int LuaUnixUnlink(lua_State *L) { int olderr = errno; - return Return01(L, unlink(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, unlink(luaL_checkstring(L, 1)), olderr); } -// unix.rmdir(path:str) → errno:int +// unix.rmdir(path:str) → ok:bool, unix.Errno static int LuaUnixRmdir(lua_State *L) { int olderr = errno; - return Return01(L, rmdir(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, rmdir(luaL_checkstring(L, 1)), olderr); } -// unix.rename(oldpath:str, newpath:str) → errno:int +// unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno static int LuaUnixRename(lua_State *L) { int olderr = errno; - return Return01(L, rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool(L, rename(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.link(existingpath:str, newpath:str) → errno:int +// unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno static int LuaUnixLink(lua_State *L) { int olderr = errno; - return Return01(L, link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool(L, link(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.symlink(target:str, linkpath:str) → errno:int +// unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno static int LuaUnixSymlink(lua_State *L) { int olderr = errno; - return Return01(L, symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), - olderr); + return SysretBool(L, symlink(luaL_checkstring(L, 1), luaL_checkstring(L, 2)), + olderr); } -// unix.chown(path:str, uid:int, gid:int) → errno:int +// unix.chown(path:str, uid:int, gid:int) → ok:bool, unix.Errno static int LuaUnixChown(lua_State *L) { int olderr = errno; - return Return01(L, - chown(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return SysretBool(L, + chown(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); } -// unix.chmod(path:str, mode:int) → errno:int +// unix.chmod(path:str, mode:int) → ok:bool, unix.Errno static int LuaUnixChmod(lua_State *L) { int olderr = errno; - return Return01(L, chmod(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, chmod(luaL_checkstring(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.getcwd() → path:str[, errno:int] +// unix.getcwd() → path:str, unix.Errno static int LuaUnixGetcwd(lua_State *L) { int olderr; char *path; @@ -324,14 +353,14 @@ static int LuaUnixGetcwd(lua_State *L) { free(path); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.fork() → childpid|0:int[, errno:int] +// unix.fork() → childpid|0:int, unix.Errno static int LuaUnixFork(lua_State *L) { int olderr = errno; - return ReturnRc(L, fork(), olderr); + return SysretInteger(L, fork(), olderr); } // unix.environ() → {str,...} @@ -346,15 +375,7 @@ static int LuaUnixEnviron(lua_State *L) { return 1; } -// unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int -// -// unix = require "unix" -// prog = unix.commandv("ls") -// unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"}) -// unix.exit(127) -// -// prog needs to be absolute, see commandv() -// envp defaults to environ +// unix.execve(prog:str[, args:List<*>, env:List<*>]) → false, unix.Errno static int LuaUnixExecve(lua_State *L) { int olderr; const char *prog; @@ -369,14 +390,14 @@ static int LuaUnixExecve(lua_State *L) { freeme2 = envp; } else { FreeStringList(argv); - return ReturnErrno(L, 1, olderr); + return SysretErrnoBool(L, olderr); } } else { envp = environ; freeme2 = 0; } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoBool(L, olderr); } } else { ezargs[0] = prog; @@ -389,10 +410,10 @@ static int LuaUnixExecve(lua_State *L) { execve(prog, argv, envp); FreeStringList(freeme1); FreeStringList(freeme2); - return ReturnErrno(L, 1, olderr); + return SysretErrnoBool(L, olderr); } -// unix.commandv(prog:str) → path:str[, errno:int] +// unix.commandv(prog:str) → path:str, unix.Errno static int LuaUnixCommandv(lua_State *L) { int olderr; const char *prog; @@ -406,14 +427,14 @@ static int LuaUnixCommandv(lua_State *L) { return 1; } else { free(pathbuf); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.realpath(path:str) → path:str[, errno:int] +// unix.realpath(path:str) → path:str, unix.Errno static int LuaUnixRealpath(lua_State *L) { char *resolved; int olderr; @@ -425,7 +446,7 @@ static int LuaUnixRealpath(lua_State *L) { free(resolved); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } @@ -435,24 +456,24 @@ static int LuaUnixSyslog(lua_State *L) { return 0; } -// unix.chroot(path:str) → errno:int +// unix.chroot(path:str) → ok:bool, unix.Errno static int LuaUnixChroot(lua_State *L) { int olderr = errno; - return Return01(L, chroot(luaL_checkstring(L, 1)), olderr); + return SysretBool(L, chroot(luaL_checkstring(L, 1)), olderr); } -// unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int +// unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno static int LuaUnixSetrlimit(lua_State *L) { int olderr = errno; int64_t soft = luaL_checkinteger(L, 2); - return Return01( + return SysretBool( L, setrlimit(luaL_checkinteger(L, 1), &(struct rlimit){soft, luaL_optinteger(L, 3, soft)}), olderr); } -// unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] +// unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int static int LuaUnixGetrlimit(lua_State *L) { struct rlimit rlim; int rc, olderr, resource; @@ -460,49 +481,51 @@ static int LuaUnixGetrlimit(lua_State *L) { resource = luaL_checkinteger(L, 1); if (!getrlimit(resource, &rlim)) { lua_pushinteger(L, rlim.rlim_cur < RLIM_INFINITY ? rlim.rlim_cur : -1); + lua_pushnil(L); lua_pushinteger(L, rlim.rlim_max < RLIM_INFINITY ? rlim.rlim_max : -1); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.kill(pid:int, sig:int) → errno:int +// unix.kill(pid:int, sig:int) → ok:bool, unix.Errno static int LuaUnixKill(lua_State *L) { int olderr = errno; - return Return01(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool(L, kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), + olderr); } -// unix.raise(sig:int) → rc:int[, errno:int] +// unix.raise(sig:int) → rc:int, unix.Errno static int LuaUnixRaise(lua_State *L) { int olderr = errno; - return ReturnRc(L, raise(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, raise(luaL_checkinteger(L, 1)), olderr); } -// unix.wait([pid:int, options:int]) → pid:int, wstatus:int, nil[, errno:int] +// unix.wait([pid:int, options:int]) → pid:int, unix.Errno, wstatus:int static int LuaUnixWait(lua_State *L) { int pid, wstatus, olderr = errno; if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, luaL_optinteger(L, 2, 0), 0)) != -1) { lua_pushinteger(L, pid); + lua_pushnil(L); lua_pushinteger(L, wstatus); - return 2; + return 3; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] +// unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno static int LuaUnixFcntl(lua_State *L) { int olderr = errno; - return ReturnRc(L, - fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretInteger(L, + fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0)), + olderr); } -// unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int[, errno:int] +// unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno static int LuaUnixDup(lua_State *L) { int rc, oldfd, newfd, flags, olderr; olderr = errno; @@ -514,103 +537,111 @@ static int LuaUnixDup(lua_State *L) { } else { rc = dup3(oldfd, newfd, flags); } - return ReturnRc(L, rc, olderr); + return SysretInteger(L, rc, olderr); } -// unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] +// unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int static int LuaUnixPipe(lua_State *L) { int pipefd[2], olderr = errno; if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) { lua_pushinteger(L, pipefd[0]); + lua_pushnil(L); lua_pushinteger(L, pipefd[1]); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.getsid(pid) → sid:int[, errno:int] +// unix.getsid(pid) → sid:int, unix.Errno static int LuaUnixGetsid(lua_State *L) { int olderr = errno; - return ReturnRc(L, getsid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, getsid(luaL_checkinteger(L, 1)), olderr); } -// unix.getpgrp() → pgid:int[, errno:int] -static int LuaUnixGetpgrp(lua_State *L) { +static dontinline int LuaUnixRc0(lua_State *L, int f(void)) { int olderr = errno; - return ReturnRc(L, getpgrp(), olderr); + return SysretInteger(L, f(), olderr); } -// unix.getpgid(pid:int) → pgid:int[, errno:int] +// unix.getpgrp() → pgid:int, unix.Errno +static int LuaUnixGetpgrp(lua_State *L) { + return LuaUnixRc0(L, getpgrp); +} + +// unix.setpgrp() → pgid:int, unix.Errno +static int LuaUnixSetpgrp(lua_State *L) { + return LuaUnixRc0(L, setpgrp); +} + +// unix.setsid() → sid:int, unix.Errno +static int LuaUnixSetsid(lua_State *L) { + return LuaUnixRc0(L, setsid); +} + +// unix.getpgid(pid:int) → pgid:int, unix.Errno static int LuaUnixGetpgid(lua_State *L) { int olderr = errno; - return ReturnRc(L, getpgid(luaL_checkinteger(L, 1)), olderr); + return SysretInteger(L, getpgid(luaL_checkinteger(L, 1)), olderr); } -// unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] +// unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno static int LuaUnixSetpgid(lua_State *L) { int olderr = errno; - return ReturnRc(L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretInteger( + L, setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); } -// unix.setpgrp() → pgid:int[, errno:int] -static int LuaUnixSetpgrp(lua_State *L) { +static dontinline int LuaUnixSetid(lua_State *L, int f(int)) { int olderr = errno; - return ReturnRc(L, setpgrp(), olderr); + return SysretBool(L, f(luaL_checkinteger(L, 1)), olderr); } -// unix.setsid() → sid:int[, errno:int] -static int LuaUnixSetsid(lua_State *L) { - int olderr = errno; - return ReturnRc(L, setsid(), olderr); -} - -// unix.setuid(uid:int) → errno:int +// unix.setuid(uid:int) → ok:bool, unix.Errno static int LuaUnixSetuid(lua_State *L) { - int olderr = errno; - return Return01(L, setuid(luaL_checkinteger(L, 1)), olderr); + return LuaUnixSetid(L, setuid); } -// unix.setgid(gid:int) → errno:int +// unix.setgid(gid:int) → ok:bool, unix.Errno static int LuaUnixSetgid(lua_State *L) { - int olderr = errno; - return Return01(L, setgid(luaL_checkinteger(L, 1)), olderr); + return LuaUnixSetid(L, setgid); } -// unix.setresuid(real:int, effective:int, saved:int) → errno:int +static dontinline int LuaUnixSetresid(lua_State *L, + int f(uint32_t, uint32_t, uint32_t)) { + int olderr = errno; + return SysretBool(L, + f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3)), + olderr); +} + +// unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno static int LuaUnixSetresuid(lua_State *L) { - int olderr = errno; - return Return01(L, - setresuid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return LuaUnixSetresid(L, setresuid); } -// unix.setresgid(real:int, effective:int, saved:int) → errno:int +// unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno static int LuaUnixSetresgid(lua_State *L) { - int olderr = errno; - return Return01(L, - setresgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_checkinteger(L, 3)), - olderr); + return LuaUnixSetresid(L, setresgid); } -// unix.clock_gettime([clock:int]) → seconds:int, nanos:int[, errno:int] +// unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int static int LuaUnixGettime(lua_State *L) { struct timespec ts; int rc, olderr = errno; if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) { lua_pushinteger(L, ts.tv_sec); + lua_pushnil(L); lua_pushinteger(L, ts.tv_nsec); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } // unix.nanosleep(seconds:int, nanos:int) -// → remseconds:int, remnanos:int[, errno:int] +// → remseconds:int, unix.Errno, remnanos:int static int LuaUnixNanosleep(lua_State *L) { int olderr = errno; struct timespec req, rem; @@ -618,10 +649,11 @@ static int LuaUnixNanosleep(lua_State *L) { req.tv_nsec = luaL_optinteger(L, 2, 0); if (!nanosleep(&req, &rem)) { lua_pushinteger(L, rem.tv_sec); + lua_pushnil(L); lua_pushinteger(L, rem.tv_nsec); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } @@ -631,59 +663,57 @@ static int LuaUnixSync(lua_State *L) { return 0; } -// unix.fsync(fd:int) → errno:int +// unix.fsync(fd:int) → ok:bool, unix.Errno static int LuaUnixFsync(lua_State *L) { int olderr = errno; - return Return01(L, fsync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, fsync(luaL_checkinteger(L, 1)), olderr); } -// unix.fdatasync(fd:int) → errno:int +// unix.fdatasync(fd:int) → ok:bool, unix.Errno static int LuaUnixFdatasync(lua_State *L) { int olderr = errno; - return Return01(L, fdatasync(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, fdatasync(luaL_checkinteger(L, 1)), olderr); } -// unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int] +// unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno static int LuaUnixOpen(lua_State *L) { int olderr = errno; - return ReturnRc(L, - open(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, 0)), - olderr); + return SysretInteger(L, + open(luaL_checkstring(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0)), + olderr); } -// unix.close(fd:int) → errno:int +// unix.close(fd:int) → ok:bool, unix.Errno static int LuaUnixClose(lua_State *L) { int olderr = errno; - return Return01(L, close(luaL_checkinteger(L, 1)), olderr); + return SysretBool(L, close(luaL_checkinteger(L, 1)), olderr); } -// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int[, errno:int] -// where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END} -// whence defaults to SEEK_SET +// unix.lseek(fd:int, offset:int[, whence:int]) → newpos:int, unix.Errno static int LuaUnixLseek(lua_State *L) { int olderr = errno; - return ReturnRc(L, - lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), - luaL_optinteger(L, 3, SEEK_SET)), - olderr); + return SysretInteger(L, + lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, SEEK_SET)), + olderr); } -// unix.truncate(path:str[, length:int]) → errno:int +// unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno static int LuaUnixTruncate(lua_State *L) { int olderr = errno; - return Return01(L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), - olderr); + return SysretBool( + L, truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0)), olderr); } -// unix.ftruncate(fd:int[, length:int]) → errno:int +// unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno static int LuaUnixFtruncate(lua_State *L) { int olderr = errno; - return Return01( + return SysretBool( L, ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0)), olderr); } -// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str[, errno:int] +// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, unix.Errno static int LuaUnixRead(lua_State *L) { char *buf; size_t got; @@ -707,14 +737,14 @@ static int LuaUnixRead(lua_State *L) { return 1; } else { free(buf); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.write(fd:int, data:str[, offset:int]) → rc:int[, errno:int] +// unix.write(fd:int, data:str[, offset:int]) → rc:int, unix.Errno static int LuaUnixWrite(lua_State *L) { size_t size; int fd, olderr; @@ -730,19 +760,19 @@ static int LuaUnixWrite(lua_State *L) { } else { rc = pwrite(fd, data, size, offset); } - return ReturnRc(L, rc, olderr); + return SysretInteger(L, rc, olderr); } 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, "UnixStat*"); + luaL_setmetatable(L, "unix.Stat"); *ustp = ust; return 1; } -// unix.stat(path:str) → UnixStat*[, errno:int] +// unix.stat(path:str) → unix.Stat, unix.Errno static int LuaUnixStat(lua_State *L) { const char *path; int olderr = errno; @@ -754,10 +784,10 @@ static int LuaUnixStat(lua_State *L) { } free(ust); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } -// unix.fstat(fd:int) → UnixFstat*[, errno:int] +// unix.fstat(fd:int) → unix.Stat, unix.Errno static int LuaUnixFstat(lua_State *L) { int fd, olderr = errno; struct UnixStat *ust; @@ -769,19 +799,19 @@ static int LuaUnixFstat(lua_State *L) { } free(ust); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } static int ReturnDir(lua_State *L, struct UnixDir *udir) { struct UnixDir **udirp; udir->refs = 1; udirp = lua_newuserdatauv(L, sizeof(*udirp), 1); - luaL_setmetatable(L, "UnixDir*"); + luaL_setmetatable(L, "unix.UnixDir"); *udirp = udir; return 1; } -// unix.opendir(path:str) → UnixDir*[, errno:int] +// unix.opendir(path:str) → unix.Dir, unix.Errno static int LuaUnixOpendir(lua_State *L) { int olderr = errno; const char *path; @@ -793,10 +823,10 @@ static int LuaUnixOpendir(lua_State *L) { } free(udir); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } -// unix.fdopendir(fd:int) → UnixDir*[, errno:int] +// unix.fdopendir(fd:int) → unix.Dir, unix.Errno static int LuaUnixFdopendir(lua_State *L) { int fd, olderr = errno; struct UnixDir *udir; @@ -807,17 +837,20 @@ static int LuaUnixFdopendir(lua_State *L) { } free(udir); } - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } static bool IsSockoptBool(int l, int x) { if (l == SOL_SOCKET) { - return x == SO_DEBUG || // - x == SO_BROADCAST || // - x == SO_REUSEADDR || // - x == SO_REUSEPORT || // - x == SO_KEEPALIVE || // - x == SO_DONTROUTE; // + return x == SO_TYPE || // + x == SO_DEBUG || // + x == SO_ERROR || // + x == SO_BROADCAST || // + x == SO_REUSEADDR || // + x == SO_REUSEPORT || // + x == SO_KEEPALIVE || // + x == SO_ACCEPTCONN || // + x == SO_DONTROUTE; // } else if (l = SOL_TCP) { return x == TCP_NODELAY || // x == TCP_CORK || // @@ -865,8 +898,9 @@ static bool IsSockoptTimeval(int l, int x) { } // unix.setsockopt(fd:int, level:int, optname:int, ...) -// → errno:int +// → ok:bool, unix.Errno static int LuaUnixSetsockopt(lua_State *L) { + struct linger l; struct timeval tv; int rc, fd, level, optname, optval, olderr = errno; fd = luaL_checkinteger(L, 1); @@ -874,32 +908,38 @@ static int LuaUnixSetsockopt(lua_State *L) { optname = luaL_checkinteger(L, 3); if (IsSockoptBool(level, optname)) { optval = lua_toboolean(L, 4); - return Return01(L, setsockopt(fd, level, optname, &optval, sizeof(optval)), - olderr); + return SysretBool( + L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); } else if (IsSockoptInt(level, optname)) { optval = luaL_checkinteger(L, 4); - return Return01(L, setsockopt(fd, level, optname, &optval, sizeof(optval)), - olderr); + return SysretBool( + L, setsockopt(fd, level, optname, &optval, sizeof(optval)), olderr); } else if (IsSockoptTimeval(level, optname)) { tv.tv_sec = luaL_checkinteger(L, 4); tv.tv_usec = luaL_optinteger(L, 5, 0); - return Return01(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), olderr); + return SysretBool(L, setsockopt(fd, level, optname, &tv, sizeof(tv)), + olderr); + } else if (level == SOL_SOCKET && optname == SO_LINGER) { + l.l_onoff = lua_toboolean(L, 4); + l.l_linger = luaL_checkinteger(L, 5); + return SysretBool(L, setsockopt(fd, level, optname, &l, sizeof(l)), olderr); } else { - lua_pushinteger(L, EINVAL); - return 1; + einval(); + return SysretErrnoBool(L, olderr); } } -// unix.getsockopt(fd:int, level:int, optname:int) -// → errno:int, ... static int LuaUnixGetsockopt(lua_State *L) { + struct linger l; struct timeval tv; - uint32_t tvsize, optvalsize; + uint32_t lsize, tvsize, optvalsize; int rc, fd, level, optname, optval, olderr = errno; fd = luaL_checkinteger(L, 1); level = luaL_checkinteger(L, 2); optname = luaL_checkinteger(L, 3); if (IsSockoptBool(level, optname)) { + // unix.getsockopt(fd:int, level:int, optname:int) + // → bool, unix.Errno optvalsize = sizeof(optval); if (getsockopt(fd, level, optname, &optval, &optvalsize) != -1) { CheckOptvalsize(L, sizeof(optval), optvalsize); @@ -907,7 +947,7 @@ static int LuaUnixGetsockopt(lua_State *L) { lua_pushboolean(L, optval); return 2; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoNil(L, olderr); } } else if (IsSockoptInt(level, optname)) { optvalsize = sizeof(optval); @@ -917,7 +957,7 @@ static int LuaUnixGetsockopt(lua_State *L) { lua_pushinteger(L, optval); return 2; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoNil(L, olderr); } } else if (IsSockoptTimeval(level, optname)) { tvsize = sizeof(tv); @@ -928,18 +968,30 @@ static int LuaUnixGetsockopt(lua_State *L) { lua_pushinteger(L, tv.tv_usec); return 3; } else { - return ReturnErrno(L, 0, olderr); + return SysretErrnoNil(L, olderr); + } + } else if (level == SOL_SOCKET && optname == SO_LINGER) { + lsize = sizeof(l); + if (getsockopt(fd, level, optname, &l, &lsize) != -1) { + CheckOptvalsize(L, sizeof(l), lsize); + lua_pushnil(L); + lua_pushinteger(L, l.l_onoff); + lua_pushinteger(L, l.l_linger); + return 3; + } else { + return SysretErrnoNil(L, olderr); } } else { - lua_pushinteger(L, EINVAL); - return 1; + einval(); + return SysretErrnoNil(L, olderr); } } -// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] +// unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, +// unix.Errno] static int LuaUnixSocket(lua_State *L) { int olderr = errno; - return ReturnRc( + return SysretInteger( L, socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, IPPROTO_TCP)), @@ -947,55 +999,57 @@ static int LuaUnixSocket(lua_State *L) { } // unix.socketpair([family:int[, type:int[, protocol:int]]]) -// → fd1:int, fd2:int[, errno:int] +// → fd1:int, unix.Errno, fd2:int static int LuaUnixSocketpair(lua_State *L) { int sv[2], olderr = errno; if (!socketpair(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), luaL_optinteger(L, 3, IPPROTO_TCP), sv)) { lua_pushinteger(L, sv[0]); + lua_pushnil(L); lua_pushinteger(L, sv[1]); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int +// unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno static int LuaUnixBind(lua_State *L) { int olderr = errno; - return Return01(L, - bind(luaL_checkinteger(L, 1), - &(struct sockaddr_in){ - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), - .sin_port = htons(luaL_optinteger(L, 3, 0)), - }, - sizeof(struct sockaddr_in)), - olderr); + return SysretBool(L, + bind(luaL_checkinteger(L, 1), + &(struct sockaddr_in){ + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)), + .sin_port = htons(luaL_optinteger(L, 3, 0)), + }, + sizeof(struct sockaddr_in)), + olderr); } -// unix.connect(fd:int, ip:uint32, port:uint16) → errno:int +// unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno static int LuaUnixConnect(lua_State *L) { int olderr = errno; - return Return01(L, - connect(luaL_checkinteger(L, 1), - &(struct sockaddr_in){ - .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), - .sin_port = htons(luaL_checkinteger(L, 3)), - }, - sizeof(struct sockaddr_in)), - olderr); + return SysretBool( + L, + connect(luaL_checkinteger(L, 1), + &(struct sockaddr_in){ + .sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)), + .sin_port = htons(luaL_checkinteger(L, 3)), + }, + sizeof(struct sockaddr_in)), + olderr); } -// unix.listen(fd:int[, backlog:int]) → errno:int +// unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno static int LuaUnixListen(lua_State *L) { int olderr = errno; - return Return01(L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), - olderr); + return SysretBool( + L, listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)), olderr); } -// unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] +// unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16 static int LuaUnixGetsockname(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1005,14 +1059,15 @@ static int LuaUnixGetsockname(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getsockname(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] +// unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16 static int LuaUnixGetpeername(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -1022,15 +1077,16 @@ static int LuaUnixGetpeername(lua_State *L) { fd = luaL_checkinteger(L, 1); if (!getpeername(fd, &sa, &addrsize)) { lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); + lua_pushnil(L); lua_pushinteger(L, ntohs(sa.sin_port)); - return 2; + return 3; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, -// errno:int] +// unix.siocgifconf() +// → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno static int LuaUnixSiocgifconf(lua_State *L) { size_t n; char *data; @@ -1039,33 +1095,33 @@ static int LuaUnixSiocgifconf(lua_State *L) { struct ifconf conf; olderr = errno; if (!(data = malloc((n = 4096)))) { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) { free(data); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } conf.ifc_buf = data; conf.ifc_len = n; if (ioctl(fd, SIOCGIFCONF, &conf) == -1) { close(fd); free(data); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } lua_newtable(L); i = 0; for (ifr = (struct ifreq *)data; (char *)ifr < data + conf.ifc_len; ++ifr) { if (ifr->ifr_addr.sa_family != AF_INET) continue; lua_createtable(L, 0, 3); - lua_pushstring(L, "name"); + lua_pushliteral(L, "name"); lua_pushstring(L, ifr->ifr_name); lua_settable(L, -3); - lua_pushstring(L, "ip"); + lua_pushliteral(L, "ip"); lua_pushinteger( L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr)); lua_settable(L, -3); if (ioctl(fd, SIOCGIFNETMASK, ifr) != -1) { - lua_pushstring(L, "netmask"); + lua_pushliteral(L, "netmask"); lua_pushinteger( L, ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr)); lua_settable(L, -3); @@ -1077,7 +1133,7 @@ static int LuaUnixSiocgifconf(lua_State *L) { return 1; } -// unix.gethostname() → host:str[, errno:int] +// unix.gethostname() → host:str, unix.Errno static int LuaUnixGethostname(lua_State *L) { int rc, olderr; char buf[DNS_NAME_MAX + 1]; @@ -1088,35 +1144,37 @@ static int LuaUnixGethostname(lua_State *L) { return 1; } else { enomem(); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.accept(serverfd:int) -// → clientfd:int, ip:uint32, port:uint16[, errno:int] +// unix.accept(serverfd:int[, flags:int]) +// → clientfd:int, unix.Errno, ip:uint32, port:uint16 static int LuaUnixAccept(lua_State *L) { uint32_t addrsize; struct sockaddr_in sa; - int clientfd, serverfd, olderr; + int clientfd, serverfd, olderr, flags; olderr = errno; addrsize = sizeof(sa); serverfd = luaL_checkinteger(L, 1); - clientfd = accept(serverfd, &sa, &addrsize); + flags = luaL_optinteger(L, 2, 0); + clientfd = accept4(serverfd, &sa, &addrsize, flags); if (clientfd != -1) { lua_pushinteger(L, clientfd); + lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); - return 3; + return 4; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } // unix.poll({fd:int=events:int, ...}[, timeoutms:int]) -// → {fd:int=revents:int, ...}[, errno:int] +// → {fd:int=revents:int, ...}, unix.Errno static int LuaUnixPoll(lua_State *L) { size_t nfds; struct pollfd *fds; @@ -1145,12 +1203,12 @@ static int LuaUnixPoll(lua_State *L) { return 1; } else { free(fds); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.recvfrom(fd[, bufsiz[, flags]]) → data, ip, port[, errno] -// flags can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. +// unix.recvfrom(fd:int[, bufsiz:str[, flags:int]]) +// → data:str, unix.Errno, ip:uint32, port:uint16 static int LuaUnixRecvfrom(lua_State *L) { char *buf; size_t got; @@ -1169,20 +1227,21 @@ static int LuaUnixRecvfrom(lua_State *L) { if (rc != -1) { got = rc; lua_pushlstring(L, buf, got); + lua_pushnil(L); lua_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); free(buf); - return 3; + return 4; } else { free(buf); - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] +// unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno static int LuaUnixRecv(lua_State *L) { char *buf; size_t got; @@ -1202,14 +1261,14 @@ static int LuaUnixRecv(lua_State *L) { return 1; } else { free(buf); - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] +// unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno static int LuaUnixSend(lua_State *L) { char *data; ssize_t rc; @@ -1220,15 +1279,13 @@ static int LuaUnixSend(lua_State *L) { data = luaL_checklstring(L, 2, &size); size = MIN(size, 0x7ffff000); flags = luaL_optinteger(L, 5, 0); - rc = send(fd, data, size, flags); - return ReturnRc(L, rc, olderr); + return SysretInteger(L, send(fd, data, size, flags), olderr); } // unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) -// → sent:int[, errno:int] +// → sent:int, unix.Errno static int LuaUnixSendto(lua_State *L) { char *data; - ssize_t rc; size_t sent, size; struct sockaddr_in sa; int fd, flags, bufsiz, olderr; @@ -1240,20 +1297,18 @@ static int LuaUnixSendto(lua_State *L) { sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3)); sa.sin_port = htons(luaL_checkinteger(L, 4)); flags = luaL_optinteger(L, 5, 0); - rc = sendto(fd, data, size, flags, &sa, sizeof(sa)); - return ReturnRc(L, rc, olderr); + return SysretInteger(L, sendto(fd, data, size, flags, &sa, sizeof(sa)), + olderr); } -// unix.shutdown(fd, how) → rc:int[, errno:int] -// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR +// unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno static int LuaUnixShutdown(lua_State *L) { int olderr = errno; - return Return01(L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), - olderr); + return SysretBool( + L, shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)), olderr); } -// unix.sigprocmask(how[, mask]) → oldmask[, errno] -// how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK +// unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno static int LuaUnixSigprocmask(lua_State *L) { uint64_t imask; int how, olderr; @@ -1276,10 +1331,7 @@ static int LuaUnixSigprocmask(lua_State *L) { lua_pushinteger(L, oldmask.__bits[0]); return 1; } else { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - return 2; + return SysretErrnoNil(L, olderr); } } @@ -1295,7 +1347,8 @@ static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) { } } -// unix.sigaction(sig[,handler[,flags[,mask]]]) → handler,flags,mask[,errno] +// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) +// → oldhandler:func|int, unix.Errno, flags:int, mask:int // // unix = require "unix" // unix.sigaction(unix.SIGUSR1, function(sig) @@ -1369,42 +1422,25 @@ static int LuaUnixSigaction(lua_State *L) { } // remove the signal handler table from stack lua_remove(L, -2); - // finish pushing the last 2/3 results + // finish pushing the last 3/4 results + lua_pushnil(L); lua_pushinteger(L, oldsa.sa_flags); lua_pushinteger(L, oldsa.sa_mask.__bits[0]); - return 3; + return 4; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.sigsuspend([mask]) → errno +// unix.sigsuspend([mask]) → false, unix.Errno static int LuaUnixSigsuspend(lua_State *L) { - int olderr; - sigset_t mask; - olderr = errno; - mask.__bits[0] = luaL_optinteger(L, 1, 0); - mask.__bits[1] = 0; - sigsuspend(&mask); - lua_pushinteger(L, errno); - errno = olderr; - return 1; + int olderr = errno; + sigsuspend(&(struct sigset){{luaL_optinteger(L, 1, 0)}}); + return SysretErrnoBool(L, olderr); } // unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) -// → intsec, intns, valsec, valns[, errno] -// -// ticks = 0 -// unix.sigaction(unix.SIGALRM, function(sig) -// print(string.format("tick no. %d", ticks)) -// ticks = ticks + 1 -// end) -// unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) -// while true do -// unix.sigsuspend() -// end -// -// which should be ITIMER_REAL +// → intsec:int, unix.Errno, intns:int, valsec:int, valns:int static int LuaUnixSetitimer(lua_State *L) { int which, olderr; struct itimerval it, oldit, *itptr; @@ -1421,33 +1457,38 @@ static int LuaUnixSetitimer(lua_State *L) { } if (!setitimer(which, itptr, &oldit)) { lua_pushinteger(L, oldit.it_interval.tv_sec); + lua_pushnil(L); lua_pushinteger(L, oldit.it_interval.tv_usec); lua_pushinteger(L, oldit.it_value.tv_sec); lua_pushinteger(L, oldit.it_value.tv_usec); - return 4; + return 5; } else { - return ReturnErrno(L, 4, olderr); + return SysretErrnoNil(L, olderr); } } -// unix.strerror(errno) → str -static int LuaUnixStrerror(lua_State *L) { - return ReturnString(L, strerror(luaL_checkinteger(L, 1))); -} - -// unix.strerrno(errno) → str -static int LuaUnixStrerrno(lua_State *L) { - return ReturnString(L, strerrno(luaL_checkinteger(L, 1))); +static dontinline int LuaUnixStr(lua_State *L, char *f(int)) { + return ReturnString(L, f(luaL_checkinteger(L, 1))); } // unix.strerdoc(errno) → str static int LuaUnixStrerdoc(lua_State *L) { - return ReturnString(L, strerdoc(luaL_checkinteger(L, 1))); + return LuaUnixStr(L, strerdoc); } // unix.strsignal(sig) → str static int LuaUnixStrsignal(lua_State *L) { - return ReturnString(L, strsignal(luaL_checkinteger(L, 1))); + return LuaUnixStr(L, strsignal); +} + +// unix.strerror(errno) → str +static int LuaUnixStrerror(lua_State *L) { + return LuaUnixStr(L, strerror); +} + +// unix.strerrno(errno) → str|nil +static int LuaUnixStrerrno(lua_State *L) { + return LuaUnixStr(L, strerrno); } // unix.WIFEXITED(wstatus) → int @@ -1471,11 +1512,11 @@ static int LuaUnixWtermsig(lua_State *L) { } //////////////////////////////////////////////////////////////////////////////// -// UnixStat* object +// unix.Stat object static dontinline struct stat *GetUnixStat(lua_State *L) { struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "UnixStat*"); + ust = luaL_checkudata(L, 1, "unix.Stat"); return &(*ust)->st; } @@ -1519,20 +1560,26 @@ static int LuaUnixStatBlksize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blksize); } +static dontinline int UnixStatTim(lua_State *L, struct timespec *ts) { + lua_pushinteger(L, ts->tv_sec); + lua_pushinteger(L, ts->tv_nsec); + return 2; +} + static int LuaUnixStatAtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_atim); + return UnixStatTim(L, &GetUnixStat(L)->st_atim); } static int LuaUnixStatMtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_mtim); + return UnixStatTim(L, &GetUnixStat(L)->st_mtim); } static int LuaUnixStatCtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_ctim); + return UnixStatTim(L, &GetUnixStat(L)->st_ctim); } static int LuaUnixStatBirthtim(lua_State *L) { - return ReturnTimespec(L, &GetUnixStat(L)->st_birthtim); + return UnixStatTim(L, &GetUnixStat(L)->st_birthtim); } static void FreeUnixStat(struct UnixStat *stat) { @@ -1541,9 +1588,15 @@ static void FreeUnixStat(struct UnixStat *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, "UnixStat*"); + ust = luaL_checkudata(L, 1, "unix.Stat"); if (*ust) { FreeUnixStat(*ust); *ust = 0; @@ -1552,30 +1605,31 @@ static int LuaUnixStatGc(lua_State *L) { } static const luaL_Reg kLuaUnixStatMeth[] = { - {"size", LuaUnixStatSize}, // - {"mode", LuaUnixStatMode}, // + {"atim", LuaUnixStatAtim}, // + {"birthtim", LuaUnixStatBirthtim}, // + {"blksize", LuaUnixStatBlksize}, // + {"blocks", LuaUnixStatBlocks}, // + {"ctim", LuaUnixStatCtim}, // {"dev", LuaUnixStatDev}, // + {"gid", LuaUnixStatGid}, // {"ino", LuaUnixStatIno}, // + {"mode", LuaUnixStatMode}, // + {"mtim", LuaUnixStatMtim}, // {"nlink", LuaUnixStatNlink}, // {"rdev", LuaUnixStatRdev}, // + {"size", LuaUnixStatSize}, // {"uid", LuaUnixStatUid}, // - {"gid", LuaUnixStatGid}, // - {"atim", LuaUnixStatAtim}, // - {"mtim", LuaUnixStatMtim}, // - {"ctim", LuaUnixStatCtim}, // - {"birthtim", LuaUnixStatBirthtim}, // - {"blocks", LuaUnixStatBlocks}, // - {"blksize", LuaUnixStatBlksize}, // {0}, // }; static const luaL_Reg kLuaUnixStatMeta[] = { - {"__gc", LuaUnixStatGc}, // - {0}, // + {"__tostring", LuaUnixStatToString}, // + {"__gc", LuaUnixStatGc}, // + {0}, // }; static void LuaUnixStatObj(lua_State *L) { - luaL_newmetatable(L, "UnixStat*"); + luaL_newmetatable(L, "unix.Stat"); luaL_setfuncs(L, kLuaUnixStatMeta, 0); luaL_newlibtable(L, kLuaUnixStatMeth); luaL_setfuncs(L, kLuaUnixStatMeth, 0); @@ -1584,10 +1638,81 @@ static void LuaUnixStatObj(lua_State *L) { } //////////////////////////////////////////////////////////////////////////////// -// UnixDir* object +// unix.Errno object + +static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) { + struct UnixErrno **ue; + ue = luaL_checkudata(L, 1, "unix.Errno"); + return *ue; +} + +static int LuaUnixErrnoName(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushstring(L, strerrno(e->errno)); + return 1; +} + +static int LuaUnixErrnoDoc(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushstring(L, strerdoc(e->errno)); + return 1; +} + +static int LuaUnixErrnoError(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushstring(L, strerror(e->errno)); + return 1; +} + +static int LuaUnixErrnoToString(lua_State *L) { + struct UnixErrno *e = GetUnixErrno(L); + lua_pushfstring(L, "error: system call failed: %s", strerror(e->errno)); + 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[] = { + {"error", LuaUnixErrnoError}, // + {"name", LuaUnixErrnoName}, // + {"doc", LuaUnixErrnoDoc}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixErrnoMeta[] = { + {"__tostring", LuaUnixErrnoToString}, // + {"__gc", LuaUnixErrnoGc}, // + {0}, // +}; + +static void LuaUnixErrnoObj(lua_State *L) { + luaL_newmetatable(L, "unix.Errno"); + luaL_setfuncs(L, kLuaUnixErrnoMeta, 0); + luaL_newlibtable(L, kLuaUnixErrnoMeth); + luaL_setfuncs(L, kLuaUnixErrnoMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//////////////////////////////////////////////////////////////////////////////// +// unix.Dir object static struct UnixDir **GetUnixDirSelf(lua_State *L) { - return luaL_checkudata(L, 1, "UnixDir*"); + return luaL_checkudata(L, 1, "unix.Dir"); } static DIR *GetDirOrDie(lua_State *L) { @@ -1595,7 +1720,7 @@ static DIR *GetDirOrDie(lua_State *L) { udir = GetUnixDirSelf(L); assert((*udir)->dir); if (*udir) return (*udir)->dir; - luaL_argerror(L, 1, "UnixDir* is closed"); + luaL_argerror(L, 1, "unix.UnixDir is closed"); unreachable; } @@ -1604,7 +1729,7 @@ static int FreeUnixDir(struct UnixDir *dir) { return closedir(dir->dir); } -// UnixDir:close() → errno:int +// unix.Dir:close() → ok:bool, unix.Errno // may be called multiple times // called by the garbage collector too static int LuaUnixDirClose(lua_State *L) { @@ -1615,12 +1740,10 @@ static int LuaUnixDirClose(lua_State *L) { olderr = 0; rc = FreeUnixDir(*udir); *udir = 0; - return Return01(L, rc, olderr); + return SysretBool(L, rc, olderr); } -// UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] -// returns nil if no more entries -// kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK +// unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int static int LuaUnixDirRead(lua_State *L) { int olderr; struct dirent *ent; @@ -1628,6 +1751,7 @@ static int LuaUnixDirRead(lua_State *L) { errno = 0; if ((ent = readdir(GetDirOrDie(L)))) { lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); + lua_pushnil(L); lua_pushinteger(L, ent->d_type); lua_pushinteger(L, ent->d_ino); lua_pushinteger(L, ent->d_off); @@ -1635,13 +1759,11 @@ static int LuaUnixDirRead(lua_State *L) { } else if (!ent && !errno) { return 0; // end of listing } else { - return ReturnErrno(L, 4, olderr); + return SysretErrnoNil(L, olderr); } } -// UnixDir:fd() → fd:int[, errno:int] -// EOPNOTSUPP if using /zip/ -// EOPNOTSUPP if IsWindows() +// unix.Dir:fd() → fd:int, unix.Errno static int LuaUnixDirFd(lua_State *L) { int fd, olderr; olderr = errno; @@ -1650,11 +1772,11 @@ static int LuaUnixDirFd(lua_State *L) { lua_pushinteger(L, fd); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrnoNil(L, olderr); } } -// UnixDir:tell() → off:int +// unix.Dir:tell() → off:int static int LuaUnixDirTell(lua_State *L) { long off; off = telldir(GetDirOrDie(L)); @@ -1662,7 +1784,7 @@ static int LuaUnixDirTell(lua_State *L) { return 1; } -// UnixDir:rewind() +// unix.Dir:rewind() static int LuaUnixDirRewind(lua_State *L) { rewinddir(GetDirOrDie(L)); return 0; @@ -1683,7 +1805,7 @@ static const luaL_Reg kLuaUnixDirMeta[] = { }; static void LuaUnixDirObj(lua_State *L) { - luaL_newmetatable(L, "UnixDir*"); + luaL_newmetatable(L, "unix.Dir"); luaL_setfuncs(L, kLuaUnixDirMeta, 0); luaL_newlibtable(L, kLuaUnixDirMeth); luaL_setfuncs(L, kLuaUnixDirMeth, 0); @@ -1793,9 +1915,9 @@ static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) { int i; char b[64], *p; p = stpcpy(b, pfx); - for (i = 0; ms[i].x != -123; ++i) { - stpcpy(p, (const char *)((uintptr_t)ms + ms[i].s)); - LuaSetIntField(L, b, *(const int *)((uintptr_t)ms + ms[i].x)); + for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) { + stpcpy(p, MAGNUM_STRING(ms, i)); + LuaSetIntField(L, b, MAGNUM_NUMBER(ms, i)); } } @@ -1808,40 +1930,12 @@ int LuaUnix(lua_State *L) { lua_setglobal(L, "__signal_handlers"); LoadMagnums(L, kErrnoNames, ""); + LoadMagnums(L, kOpenFlags, "O_"); LoadMagnums(L, kSignalNames, "SIG"); - LoadMagnums(L, kIpOptnames, ""); - LoadMagnums(L, kTcpOptnames, ""); - LoadMagnums(L, kSockOptnames, ""); - - // open() flags - LuaSetIntField(L, "O_RDONLY", O_RDONLY); // - LuaSetIntField(L, "O_WRONLY", O_WRONLY); // - LuaSetIntField(L, "O_RDWR", O_RDWR); // - LuaSetIntField(L, "O_ACCMODE", O_ACCMODE); // mask of prev three - LuaSetIntField(L, "O_CREAT", O_CREAT); // - LuaSetIntField(L, "O_EXCL", O_EXCL); // - LuaSetIntField(L, "O_TRUNC", O_TRUNC); // - LuaSetIntField(L, "O_CLOEXEC", O_CLOEXEC); // - LuaSetIntField(L, "O_DIRECT", O_DIRECT); // no-op on xnu/openbsd - LuaSetIntField(L, "O_APPEND", O_APPEND); // weird on nt - LuaSetIntField(L, "O_TMPFILE", O_TMPFILE); // linux, windows - LuaSetIntField(L, "O_NOFOLLOW", O_NOFOLLOW); // unix - LuaSetIntField(L, "O_SYNC", O_SYNC); // unix - LuaSetIntField(L, "O_ASYNC", O_ASYNC); // unix - LuaSetIntField(L, "O_NOCTTY", O_NOCTTY); // unix - LuaSetIntField(L, "O_NOATIME", O_NOATIME); // linux - LuaSetIntField(L, "O_EXEC", O_EXEC); // free/openbsd - LuaSetIntField(L, "O_SEARCH", O_SEARCH); // free/netbsd - LuaSetIntField(L, "O_DSYNC", O_DSYNC); // linux/xnu/open/netbsd - LuaSetIntField(L, "O_RSYNC", O_RSYNC); // linux/open/netbsd - LuaSetIntField(L, "O_PATH", O_PATH); // linux - LuaSetIntField(L, "O_VERIFY", O_VERIFY); // freebsd - LuaSetIntField(L, "O_SHLOCK", O_SHLOCK); // bsd - LuaSetIntField(L, "O_EXLOCK", O_EXLOCK); // bsd - LuaSetIntField(L, "O_RANDOM", O_RANDOM); // windows - LuaSetIntField(L, "O_SEQUENTIAL", O_SEQUENTIAL); // windows - LuaSetIntField(L, "O_COMPRESSED", O_COMPRESSED); // windows - LuaSetIntField(L, "O_INDEXED", O_INDEXED); // windows + LoadMagnums(L, kIpOptnames, "IP_"); + LoadMagnums(L, kTcpOptnames, "TCP_"); + LoadMagnums(L, kSockOptnames, "SO_"); + LoadMagnums(L, kClockNames, "CLOCK_"); // seek() whence LuaSetIntField(L, "SEEK_SET", SEEK_SET); @@ -1878,19 +1972,6 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "WNOHANG", WNOHANG); LuaSetIntField(L, "WNOHANG", WNOHANG); - // gettime() clocks - LuaSetIntField(L, "CLOCK_REALTIME", CLOCK_REALTIME); // portable - LuaSetIntField(L, "CLOCK_MONOTONIC", CLOCK_MONOTONIC); // portable - LuaSetIntField(L, "CLOCK_MONOTONIC_RAW", CLOCK_MONOTONIC_RAW); // portable - LuaSetIntField(L, "CLOCK_REALTIME_COARSE", CLOCK_REALTIME_COARSE); - LuaSetIntField(L, "CLOCK_MONOTONIC_COARSE", CLOCK_MONOTONIC_COARSE); - LuaSetIntField(L, "CLOCK_PROCESS_CPUTIME_ID", CLOCK_PROCESS_CPUTIME_ID); - LuaSetIntField(L, "CLOCK_TAI", CLOCK_TAI); - LuaSetIntField(L, "CLOCK_PROF", CLOCK_PROF); - LuaSetIntField(L, "CLOCK_BOOTTIME", CLOCK_BOOTTIME); - LuaSetIntField(L, "CLOCK_REALTIME_ALARM", CLOCK_REALTIME_ALARM); - LuaSetIntField(L, "CLOCK_BOOTTIME_ALARM", CLOCK_BOOTTIME_ALARM); - // socket() family LuaSetIntField(L, "AF_UNSPEC", AF_UNSPEC); LuaSetIntField(L, "AF_UNIX", AF_UNIX); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 33bfa4ca0..b6cb5e42c 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -191,10 +191,10 @@ STATIC_YOINK("zip_uri_support"); #define HeaderEqualCase(H, S) \ SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) -// letters not used: EIJNOQWXYinoqwxy +// letters not used: EIJNOQWXYnoqwxy // digits not used: 0123456789 // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ -#define GETOPTS "BSVZabdfghjkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" +#define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" static const uint8_t kGzipHeader[] = { 0x1F, // MAGNUM @@ -379,6 +379,7 @@ static bool checkedmethod; static bool sslinitialized; static bool sslfetchverify; static bool hascontenttype; +static bool interpretermode; static bool sslclientverify; static bool connectionclose; static bool hasonworkerstop; @@ -1185,10 +1186,10 @@ static void ReportWorkerExit(int pid, int ws) { static void ReportWorkerResources(int pid, struct rusage *ru) { char *s, *b = 0; if (logrusage || LOGGABLE(kLogDebug)) { - AppendResourceReport(&b, ru, "\r\n"); + AppendResourceReport(&b, ru, "\n"); if (b) { if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) { - LOGF(kLogDebug, "(stat) resource report for pid %d\r\n%s", pid, s); + LOGF(kLogDebug, "(stat) resource report for pid %d\n%s", pid, s); free(s); } free(b); @@ -4116,7 +4117,7 @@ static int LuaLog(lua_State *L) { } static int LuaEncodeSmth(lua_State *L, - int Encoder(lua_State *, char **, int, char *)) { + int Encoder(lua_State *, char **, int, char *, int)) { int useoutput = false; int maxdepth = 64; char *numformat = "%.14g"; @@ -4133,7 +4134,7 @@ static int LuaEncodeSmth(lua_State *L, numformat = luaL_optstring(L, -1, numformat); } lua_settop(L, 1); // keep the passed argument on top - Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat); + Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1); if (useoutput) { lua_pushnil(L); } else { @@ -4920,6 +4921,7 @@ static const char *const kDontAutoComplete[] = { // static const luaL_Reg kLuaFuncs[] = { + {"Benchmark", LuaBenchmark}, // {"Bsf", LuaBsf}, // {"Bsr", LuaBsr}, // {"CategorizeIp", LuaCategorizeIp}, // @@ -5085,13 +5087,21 @@ static const luaL_Reg kLuaLibs[] = { }; static void LuaSetArgv(lua_State *L) { - size_t i; + int i, j = -1; lua_newtable(L); + lua_pushstring(L, __argv[0]); + lua_seti(L, -2, j++); + if (!interpretermode) { + lua_pushstring(L, "/zip/.init.lua"); + lua_seti(L, -2, j++); + } for (i = optind; i < __argc; ++i) { lua_pushstring(L, __argv[i]); - lua_seti(L, -2, i - optind + 1); + lua_seti(L, -2, j++); } - lua_setglobal(L, "argv"); + lua_pushvalue(L, -1); + lua_setglobal(L, "argv"); // deprecated + lua_setglobal(L, "arg"); } static void LuaSetConstant(lua_State *L, const char *s, long x) { @@ -5133,10 +5143,111 @@ static void LuaStart(void) { #endif } +static bool ShouldAutocomplete(const char *s) { + int c, m, l, r; + l = 0; + r = ARRAYLEN(kDontAutoComplete) - 1; + while (l <= r) { + m = (l + r) >> 1; + c = strcmp(kDontAutoComplete[m], s); + if (c < 0) { + l = m + 1; + } else if (c > 0) { + r = m - 1; + } else { + return false; + } + } + return true; +} + +static void HandleCompletions(const char *p, linenoiseCompletions *c) { + size_t i, j; + for (j = i = 0; i < c->len; ++i) { + if (ShouldAutocomplete(c->cvec[i])) { + c->cvec[j++] = c->cvec[i]; + } else { + free(c->cvec[i]); + } + } + c->len = j; +} + +static void LuaPrint(lua_State *L) { + int i, n; + char *b = 0; + const char *s; + n = lua_gettop(L); + for (i = 1; i <= n; i++) { + if (i > 1) appendw(&b, '\t'); + LuaEncodeLuaData(L, &b, 64, "g", i); + } + appendw(&b, '\n'); + WRITE(1, b, appendz(b).i); + free(b); +} + +static void LuaInterpreter(lua_State *L) { + int i, n, sig, status; + const char *script; + if (optind < __argc) { + script = __argv[optind]; + if (!strcmp(script, "-")) script = 0; + if ((status = luaL_loadfile(L, script)) == LUA_OK) { + lua_getglobal(L, "arg"); + n = luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many script args"); + for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); + lua_remove(L, -i); // remove arg table from stack + status = lua_runchunk(L, n, LUA_MULTRET); + } + lua_report(L, status); + } else { + lua_repl_blocking = true; + lua_repl_completions_callback = HandleCompletions; + lua_initrepl(GL, "redbean"); + if (lua_repl_isterminal) { + linenoiseEnableRawMode(0); + } + for (;;) { + status = lua_loadline(L); + write(1, "\n", 1); + if (status == -1) break; // eof + if (status == -2) { + if (errno == EINTR) { + if ((sig = linenoiseGetInterrupt())) { + raise(sig); + } + } + fprintf(stderr, "i/o error: %m\n"); + exit(1); + } + if (status == LUA_OK) { + status = lua_runchunk(GL, 0, LUA_MULTRET); + } + if (status == LUA_OK) { + LuaPrint(GL); + } else { + lua_report(GL, status); + } + } + linenoiseDisableRawMode(); + lua_freerepl(); + lua_settop(GL, 0); // clear stack + if ((sig = linenoiseGetInterrupt())) { + raise(sig); + } + } +} + static void LuaInit(void) { #ifndef STATIC lua_State *L = GL; LuaSetArgv(L); + if (interpretermode) { + LuaInterpreter(L); + exit(0); + } if (LuaRunAsset("/.init.lua", true)) { hasonhttprequest = IsHookDefined("OnHttpRequest"); hasonclientconnection = IsHookDefined("OnClientConnection"); @@ -6369,34 +6480,6 @@ static void RestoreApe(void) { } } -static bool ShouldAutocomplete(const char *s) { - int c, m, l, r; - l = 0; - r = ARRAYLEN(kDontAutoComplete) - 1; - while (l <= r) { - m = (l + r) >> 1; - c = strcmp(kDontAutoComplete[m], s); - if (c < 0) { - l = m + 1; - } else if (c > 0) { - r = m - 1; - } else { - return false; - } - } - return true; -} - -static void HandleCompletions(const char *p, linenoiseCompletions *c) { - size_t i, j; - for (j = i = 0; i < c->len; ++i) { - if (ShouldAutocomplete(c->cvec[i])) { - c->cvec[j++] = c->cvec[i]; - } - } - c->len = j; -} - static int HandleReadline(void) { int status; for (;;) { @@ -6405,7 +6488,7 @@ static int HandleReadline(void) { if (status == -1) { OnTerm(SIGHUP); // eof INFOF("got repl eof"); - write(1, "\r\n", 2); + write(1, "\n", 1); return -1; } else if (errno == EINTR) { errno = 0; @@ -6419,14 +6502,14 @@ static int HandleReadline(void) { return -1; } } - write(1, "\r\n", 2); + write(1, "\n", 1); linenoiseDisableRawMode(); LUA_REPL_LOCK; if (status == LUA_OK) { status = lua_runchunk(GL, 0, LUA_MULTRET); } if (status == LUA_OK) { - lua_l_print(GL); + LuaPrint(GL); } else { lua_report(GL, status); } @@ -6781,6 +6864,7 @@ static void GetOpts(int argc, char *argv[]) { #ifndef STATIC CASE('e', LuaEvalCode(optarg)); CASE('F', LuaEvalFile(optarg)); + CASE('i', interpretermode = true); CASE('E', leakcrashreports = true); CASE('A', storeasset = true; StorePath(optarg)); #endif