diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index 89c8adc0b..df9521d24 100755 Binary files a/build/bootstrap/compile.com and b/build/bootstrap/compile.com differ diff --git a/build/definitions.mk b/build/definitions.mk index 7a08e72a7..cfb7eafe8 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -169,7 +169,7 @@ DEFAULT_LDFLAGS = \ --gc-sections \ --build-id=none \ --no-dynamic-linker \ - -zmax-page-size=0x1000 + -zmax-page-size=0x1000 #--cref -Map=$@.map ZIPOBJ_FLAGS = \ -b$(IMAGE_BASE_VIRTUAL) diff --git a/dsp/tty/setraw.c b/dsp/tty/setraw.c index 72eb4a36b..6d2b8d273 100644 --- a/dsp/tty/setraw.c +++ b/dsp/tty/setraw.c @@ -31,13 +31,6 @@ int ttysetraw(struct termios *conf, int64_t flags) { conf->c_cflag &= ~(CSIZE | PARENB); conf->c_cflag |= CS8; conf->c_iflag |= IUTF8; - /* if (flags & kTtyLfToCrLf) { */ - /* /\* conf->c_oflag &= ~(OLCUC | OCRNL | ONLRET | OFILL | OFDEL); *\/ */ - /* /\* conf->c_oflag |= ONLCR | ONOCR; *\/ */ - /* conf->c_oflag |= ONLCR; */ - /* } else { */ - /* conf->c_oflag &= ~OPOST; */ - /* } */ if (!(flags & kTtySigs)) { conf->c_iflag &= ~(IGNBRK | BRKINT); conf->c_lflag &= ~(ISIG); diff --git a/examples/breakpoint.c b/examples/breakpoint.c index c9a7a58e5..c4de23644 100644 --- a/examples/breakpoint.c +++ b/examples/breakpoint.c @@ -14,7 +14,7 @@ #include "libc/runtime/symbols.internal.h" int main(int argc, char *argv[]) { - // ShowCrashReports(); + ShowCrashReports(); if (IsDebuggerPresent(false)) { kprintf("debugger found!%n"); diff --git a/examples/datauri.c b/examples/datauri.c index 4c49d4c73..c07f569ff 100644 --- a/examples/datauri.c +++ b/examples/datauri.c @@ -43,7 +43,6 @@ void PrintUri(const char *path) { } int main(int argc, char *argv[]) { - ShowCrashReports(); int i; while ((i = getopt(argc, argv, "?h")) != -1) { switch (i) { diff --git a/examples/examples.mk b/examples/examples.mk index 3d958f8e3..a76e23ebd 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -140,7 +140,7 @@ o/$(MODE)/examples/nesemu1.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/examples/.nesemu1/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/examples/.nesemu1/.symtab o/$(MODE)/examples/hello.com.dbg: \ @@ -151,14 +151,6 @@ o/$(MODE)/examples/hello.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) -o/$(MODE)/examples/printargs.com.dbg: \ - $(EXAMPLES_DEPS) \ - o/$(MODE)/examples/printargs.o \ - o/$(MODE)/examples/examples.pkg \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - o/$(MODE)/examples/nesemu1.o: QUOTA += -M512m $(EXAMPLES_OBJS): examples/examples.mk diff --git a/examples/img.c b/examples/img.c index e456e3a85..37af07737 100644 --- a/examples/img.c +++ b/examples/img.c @@ -7,6 +7,7 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/dce.h" #include "libc/log/log.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/stdio.h" @@ -67,7 +68,7 @@ void PrintImg(const char *path) { } int main(int argc, char *argv[]) { - ShowCrashReports(); + if (!NoDebug()) ShowCrashReports(); int i; while ((i = getopt(argc, argv, "?huas01234")) != -1) { switch (i) { diff --git a/examples/ispell.c b/examples/ispell.c index 0a90fef64..8ce96e96d 100644 --- a/examples/ispell.c +++ b/examples/ispell.c @@ -153,7 +153,7 @@ void LoadWords(void) { } int main(int argc, char *argv[]) { - ShowCrashReports(); + if (!NoDebug()) ShowCrashReports(); LoadWords(); SpellChecker(); return 0; diff --git a/examples/panels.c b/examples/panels.c index 53d6d1ba9..4e3361126 100644 --- a/examples/panels.c +++ b/examples/panels.c @@ -12,6 +12,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/winsize.h" +#include "libc/dce.h" #include "libc/log/check.h" #include "libc/log/gdb.h" #include "libc/log/log.h" @@ -152,7 +153,7 @@ void Draw(void) { int main(int argc, char *argv[]) { struct sigaction sa[2] = {{.sa_handler = OnShutdown}, {.sa_handler = OnInvalidate}}; - ShowCrashReports(); + if (!NoDebug()) ShowCrashReports(); Setup(); Enter(); GetTtySize(); diff --git a/examples/printargs.c b/examples/printargs.c index 11c057ce2..60ce148ed 100644 --- a/examples/printargs.c +++ b/examples/printargs.c @@ -7,30 +7,8 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/bits/safemacros.internal.h" -#include "libc/calls/calls.h" #include "libc/runtime/runtime.h" int main() { - int pip[2]; - char buf[PATH_MAX]; - char *args[2] = {0}; - if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && - ((args[0] = commandv("less", buf)) || - (args[0] = commandv("more", buf)))) { - close(0); - close(2); - pipe(pip); - if (!vfork()) { - close(2); - execv(args[0], args); - _Exit(127); - } - close(0); - __printargs(""); - close(2); - wait(0); - } else { - __printargs(""); - } + __printargs(""); } diff --git a/examples/rlimit.c b/examples/rlimit.c index c98aa4113..47325062c 100644 --- a/examples/rlimit.c +++ b/examples/rlimit.c @@ -36,12 +36,12 @@ static void SetLimit(int resource, uint64_t soft, uint64_t hard) { lim.rlim_max = MIN(hard, old.rlim_max); lim.rlim_cur = MIN(soft, lim.rlim_max); if (!setrlimit(resource, &lim)) { - fprintf(stderr, "%snote: setrlimit(%s) downgraded to {%,ld, %,ld}\n", + fprintf(stderr, "%sNOTE: SETRLIMIT(%s) DOWNGRADED TO {%,ld, %,ld}\n", __strace_rlimit_name(resource), lim.rlim_cur, lim.rlim_max); return; } } - fprintf(stderr, "error: setrlimit(%s, %,ld, %,ld) failed %m%n", + fprintf(stderr, "ERROR: SETRLIMIT(%s, %,ld, %,ld) FAILED %m%n", __strace_rlimit_name(resource), soft, hard); exit(1); } @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { for (i = 0; i < RLIM_NLIMITS; ++i) { rc = getrlimit(i, &rlim); - printf("setrlimit(%-20s, %,16ld, %,16ld) → %d %s\n", + printf("SETRLIMIT(%-20s, %,16ld, %,16ld) → %d %s\n", __strace_rlimit_name(i), rlim.rlim_cur, rlim.rlim_max, rc, !rc ? "" : strerror(errno)); } diff --git a/examples/seq.c b/examples/seq.c index 7e2782cc1..dc7e2295a 100644 --- a/examples/seq.c +++ b/examples/seq.c @@ -7,8 +7,9 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/calls/calls.h" #include "libc/fmt/conv.h" -#include "libc/stdio/stdio.h" +#include "libc/fmt/itoa.h" /** * @fileoverview Prints sequence of numbers. @@ -16,6 +17,7 @@ int main(int argc, char *argv[]) { long a, b, i; + char buf[21], *p; switch (argc) { case 2: a = 1; @@ -29,6 +31,9 @@ int main(int argc, char *argv[]) { return 1; } for (i = a; i <= b; ++i) { - printf("%ld\n", i); + p = buf; + p = FormatInt64(p, i); + *p++ = '\n'; + write(1, buf, p - buf); } } diff --git a/examples/time.c b/examples/time.c index 2213ba405..a09d0b0aa 100644 --- a/examples/time.c +++ b/examples/time.c @@ -69,7 +69,7 @@ int main(int argc, char *argv[]) { long double real; char exebuf[PATH_MAX]; if (argc >= 2) { - if ((exepath = commandv(argv[1], exebuf))) { + if ((exepath = commandv(argv[1], exebuf, sizeof(exebuf)))) { real = nowl(); argv[1] = exepath; if ((ws = xvspawn(OnChild, argv + 1, &r)) != -1) { diff --git a/examples/touch.c b/examples/touch.c index 0325bfc92..58e179f44 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) { int i; for (i = 1; i < argc; ++i) { - if (touch(argv[i], 0644) == -1) { + if (touch(argv[i], 0666) == -1) { fprintf(stderr, "ERROR: %s: %s\n", argv[i], strerror(errno)); exit(1); } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index e50c44cdc..ee64d4a59 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -17,6 +17,10 @@ #include "libc/sysv/consts/s.h" #include "libc/sysv/consts/sig.h" +#define _POSIX_VERSION 200809L +#define _POSIX2_VERSION _POSIX_VERSION +#define _XOPEN_VERSION 700 + #define EOF -1 /* end of file */ #define WEOF -1u /* end of file (multibyte) */ #define _IOFBF 0 /* fully buffered */ @@ -48,14 +52,14 @@ #define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) #define S_ISSOCK(mode) (((mode)&S_IFMT) == S_IFSOCK) -#define WCOREDUMP(s) ((s)&0x80) -#define WEXITSTATUS(s) (((s)&0xff00) >> 8) +#define WCOREDUMP(s) (0x80 & (s)) +#define WEXITSTATUS(s) ((0xff00 & (s)) >> 8) #define WIFCONTINUED(s) ((s) == 0xffff) #define WIFEXITED(s) (!WTERMSIG(s)) -#define WIFSIGNALED(s) (((s)&0xffff) - 1u < 0xffu) -#define WIFSTOPPED(s) ((short)((((s)&0xffff) * 0x10001) >> 8) > 0x7f00) +#define WIFSIGNALED(s) ((0xffff & (s)) - 1u < 0xffu) +#define WIFSTOPPED(s) ((short)(((0xffff & (s)) * 0x10001) >> 8) > 0x7f00) #define WSTOPSIG(s) WEXITSTATUS(s) -#define WTERMSIG(s) ((s)&0x7f) +#define WTERMSIG(s) (127 & (s)) #define W_STOPCODE(s) ((s) << 8 | 0177) #if !(__ASSEMBLER__ + __LINKER__ + 0) @@ -75,7 +79,7 @@ bool isregularfile(const char *); bool issymlink(const char *); bool32 isatty(int) nosideeffect; bool32 ischardev(int) nosideeffect; -char *commandv(const char *, char[hasatleast PATH_MAX]); +char *commandv(const char *, char *, size_t); char *get_current_dir_name(void) dontdiscard; char *getcwd(char *, size_t); char *realpath(const char *, char *); @@ -119,14 +123,21 @@ 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); +int getpgrp(void) nosideeffect; int getpid(void); -int gettid(void); int getppid(void); int getpriority(int, unsigned); 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; @@ -165,15 +176,18 @@ int rmdir(const char *); int sched_getaffinity(int, uint64_t, void *); int sched_setaffinity(int, uint64_t, const void *); int sched_yield(void); +int seccomp(unsigned, unsigned, void *); int setegid(uint32_t); int seteuid(uint32_t); int setgid(int); -int setpgrp(void); int setpgid(int, int); +int setpgrp(void); int setpriority(int, unsigned, int); int setregid(uint32_t, uint32_t); int setresgid(uint32_t, uint32_t, uint32_t); int setresuid(uint32_t, uint32_t, uint32_t); +int getresgid(uint32_t *, uint32_t *, uint32_t *); +int getresuid(uint32_t *, uint32_t *, uint32_t *); int setreuid(uint32_t, uint32_t); int setrlimit(int, const struct rlimit *); int setsid(void); @@ -192,6 +206,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 **); @@ -206,9 +221,9 @@ long ptrace(int, ...); long telldir(DIR *); long times(struct tms *); size_t GetFileSize(const char *); -ssize_t getfiledescriptorsize(int); ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t); ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t); +ssize_t getfiledescriptorsize(int); ssize_t lseek(int, int64_t, unsigned); ssize_t pread(int, void *, size_t, int64_t); ssize_t preadv(int, struct iovec *, int, int64_t); @@ -222,17 +237,10 @@ 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 getpgrp(void) nosideeffect; -uint32_t getsid(int) nosideeffect; -uint32_t getuid(void) nosideeffect; -uint32_t umask(uint32_t); void rewinddir(DIR *); void sync(void); -int getloadavg(double *, int); -int seccomp(unsigned, unsigned, void *); +int pledge(const char *, const char *); + int clone(int (*)(void *), void *, size_t, int, void *, int *, void *, size_t, int *); diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index dfa690101..09231eb65 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -65,6 +65,7 @@ $(LIBC_CALLS_A).pkg: \ $(LIBC_CALLS_A_OBJS) \ $(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) +o/$(MODE)/libc/calls/vdsofunc.greg.o \ o/$(MODE)/libc/calls/directmap.o \ o/$(MODE)/libc/calls/directmap-nt.o \ o/$(MODE)/libc/calls/raise.o: \ @@ -108,9 +109,12 @@ o//libc/calls/fcntl.o: \ o/$(MODE)/libc/calls/execl.o \ o/$(MODE)/libc/calls/execle.o \ o/$(MODE)/libc/calls/execlp.o \ +o/$(MODE)/libc/calls/copyfile.o \ o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-sysv.o \ +o/$(MODE)/libc/calls/symlinkat-nt.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/clock_gettime.c b/libc/calls/clock_gettime.c index 4d206301c..f091d6719 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -26,6 +26,8 @@ #include "libc/nt/synchronization.h" #include "libc/sysv/errfuns.h" +static typeof(sys_clock_gettime) *__clock_gettime = sys_clock_gettime; + /** * Returns nanosecond time. * @@ -52,7 +54,7 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { rc = einval(); } else if (!IsWindows()) { e = errno; - if ((rc = sys_clock_gettime(clockid, ts))) { + if ((rc = __clock_gettime(clockid, ts))) { errno = e; ad = sys_gettimeofday((struct timeval *)ts, NULL, NULL); assert(ad.ax != -1); @@ -72,3 +74,23 @@ noinstrument int clock_gettime(int clockid, struct timespec *ts) { } return rc; } + +/** + * Returns fast system clock_gettime() if it exists. + */ +void *__get_clock_gettime(void) { + void *vdso; + static bool once; + static void *result; + if (!once) { + if ((vdso = __vdsofunc("__vdso_clock_gettime"))) { + __clock_gettime = result = vdso; + } + once = true; + } + return result; +} + +const void *const __clock_gettime_ctor[] initarray = { + __get_clock_gettime, +}; diff --git a/libc/calls/close.c b/libc/calls/close.c index c8137e0ee..13cfe7b21 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -73,7 +73,9 @@ int close(int fd) { } } } - if (!__vforked) __releasefd(fd); + if (!__vforked) { + __releasefd(fd); + } } STRACE("%s(%d) → %d% m", "close", fd, rc); return rc; diff --git a/libc/calls/commandv.c b/libc/calls/commandv.c index fad7460f3..79ad7b629 100644 --- a/libc/calls/commandv.c +++ b/libc/calls/commandv.c @@ -43,15 +43,16 @@ static bool IsComDbgPath(const char *s, size_t n) { READ64LE(s + n - 8) == READ64LE(".COM.DBG")); } -static bool AccessCommand(const char *name, char path[hasatleast PATH_MAX], +static bool AccessCommand(const char *name, char *path, size_t pathsz, size_t namelen, int *err, const char *suffix, size_t pathlen) { size_t suffixlen; suffixlen = strlen(suffix); - if (pathlen + 1 + namelen + suffixlen + 1 > PATH_MAX) return false; + if (pathlen + 1 + namelen + suffixlen + 1 > pathsz) return false; if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) { - path[pathlen] = - !IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/'; + path[pathlen] = !IsWindows() ? '/' + : memchr(path, '\\', pathlen) ? '\\' + : '/'; pathlen++; } memcpy(path + pathlen, name, namelen); @@ -61,7 +62,7 @@ static bool AccessCommand(const char *name, char path[hasatleast PATH_MAX], return false; } -static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], +static bool SearchPath(const char *name, char *path, size_t pathsz, size_t namelen, int *err, const char *suffix) { char sep; size_t i; @@ -70,11 +71,11 @@ static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], sep = IsWindows() && strchr(p, ';') ? ';' : ':'; for (;;) { for (i = 0; p[i] && p[i] != sep; ++i) { - if (i < PATH_MAX) { + if (i < pathsz) { path[i] = p[i]; } } - if (AccessCommand(name, path, namelen, err, suffix, i)) { + if (AccessCommand(name, path, pathsz, namelen, err, suffix, i)) { return true; } if (p[i] == sep) { @@ -86,36 +87,36 @@ static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], return false; } -static bool FindCommand(const char *name, char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, const char *suffix, - int *err) { - if (priorityonly && - (memchr(name, '/', namelen) || memchr(name, '\\', namelen))) { - pathbuf[0] = 0; - return AccessCommand(name, pathbuf, namelen, err, suffix, 0); +static bool FindCommand(const char *name, char *pb, size_t pbsz, size_t namelen, + bool pri, const char *suffix, int *err) { + if (pri && (memchr(name, '/', namelen) || memchr(name, '\\', namelen))) { + pb[0] = 0; + return AccessCommand(name, pb, pbsz, namelen, err, suffix, 0); } - if (IsWindows() && priorityonly) { - return AccessCommand(name, pathbuf, namelen, err, suffix, - stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) || - AccessCommand(name, pathbuf, namelen, err, suffix, - stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf); + if (IsWindows() && pri && + pbsz > max(strlen(kNtSystemDirectory), strlen(kNtWindowsDirectory))) { + return AccessCommand(name, pb, pbsz, namelen, err, suffix, + stpcpy(pb, kNtSystemDirectory) - pb) || + AccessCommand(name, pb, pbsz, namelen, err, suffix, + stpcpy(pb, kNtWindowsDirectory) - pb); } - return (IsWindows() && AccessCommand(name, pathbuf, namelen, err, suffix, - stpcpy(pathbuf, ".") - pathbuf)) || - SearchPath(name, pathbuf, namelen, err, suffix); + return (IsWindows() && + (pbsz > 1 && AccessCommand(name, pb, pbsz, namelen, err, suffix, + stpcpy(pb, ".") - pb))) || + SearchPath(name, pb, pbsz, namelen, err, suffix); } -static bool FindVerbatim(const char *name, char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, int *err) { - return FindCommand(name, pathbuf, namelen, priorityonly, "", err); +static bool FindVerbatim(const char *name, char *pb, size_t pbsz, + size_t namelen, bool pri, int *err) { + return FindCommand(name, pb, pbsz, namelen, pri, "", err); } -static bool FindSuffixed(const char *name, char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, int *err) { +static bool FindSuffixed(const char *name, char *pb, size_t pbsz, + size_t namelen, bool pri, int *err) { return !IsExePath(name, namelen) && !IsComPath(name, namelen) && !IsComDbgPath(name, namelen) && - (FindCommand(name, pathbuf, namelen, priorityonly, ".com", err) || - FindCommand(name, pathbuf, namelen, priorityonly, ".exe", err)); + (FindCommand(name, pb, pbsz, namelen, pri, ".com", err) || + FindCommand(name, pb, pbsz, namelen, pri, ".exe", err)); } /** @@ -127,7 +128,7 @@ static bool FindSuffixed(const char *name, char pathbuf[hasatleast PATH_MAX], * @asyncsignalsafe * @vforksafe */ -char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { +char *commandv(const char *name, char *pathbuf, size_t pathbufsz) { int e, f; char *res; size_t namelen; @@ -136,25 +137,27 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { efault(); } else if (!(namelen = strlen(name))) { enoent(); - } else if (namelen + 1 > PATH_MAX) { + } else if (namelen + 1 > pathbufsz) { enametoolong(); } else { e = errno; f = ENOENT; - if ((IsWindows() && (FindSuffixed(name, pathbuf, namelen, true, &f) || - FindVerbatim(name, pathbuf, namelen, true, &f) || - FindSuffixed(name, pathbuf, namelen, false, &f) || - FindVerbatim(name, pathbuf, namelen, false, &f))) || - (!IsWindows() && (FindVerbatim(name, pathbuf, namelen, true, &f) || - FindSuffixed(name, pathbuf, namelen, true, &f) || - FindVerbatim(name, pathbuf, namelen, false, &f) || - FindSuffixed(name, pathbuf, namelen, false, &f)))) { + if ((IsWindows() && + (FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) || + FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) || + FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f) || + FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f))) || + (!IsWindows() && + (FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) || + FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) || + FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f) || + FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f)))) { errno = e; res = pathbuf; } else { errno = f; } } - STRACE("commandv(%#s, %p) → %#s% m", name, pathbuf, res); + STRACE("commandv(%#s, %p, %'zu) → %#s% m", name, pathbuf, pathbufsz, res); return res; } diff --git a/libc/calls/copyfile.c b/libc/calls/copyfile.c index c9e9cc93a..270532537 100644 --- a/libc/calls/copyfile.c +++ b/libc/calls/copyfile.c @@ -28,6 +28,7 @@ #include "libc/nt/enum/filesharemode.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/filetime.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/o.h" 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/execlp.c b/libc/calls/execlp.c index 3444fe589..a2d97538d 100644 --- a/libc/calls/execlp.c +++ b/libc/calls/execlp.c @@ -40,7 +40,7 @@ int execlp(const char *prog, const char *arg, ... /*, NULL*/) { char **argv; va_list va, vb; char pathbuf[PATH_MAX]; - if (!(exe = commandv(prog, pathbuf))) return -1; + if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1; va_copy(vb, va); va_start(va, arg); for (i = 0; va_arg(va, const char *); ++i) donothing; diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 28b842734..60c4f5189 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -33,7 +33,8 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) { shargs = alloca((i + 2) * sizeof(char *)); memcpy(shargs + 2, argv + 1, i * sizeof(char *)); if (IsFreebsd() || IsNetbsd()) { - shargs[0] = firstnonnull(commandv("bash", alloca(PATH_MAX)), _PATH_BSHELL); + shargs[0] = firstnonnull(commandv("bash", alloca(PATH_MAX), PATH_MAX), + _PATH_BSHELL); } else { shargs[0] = _PATH_BSHELL; } diff --git a/libc/calls/execve.c b/libc/calls/execve.c index f7a278ecb..9df027a14 100644 --- a/libc/calls/execve.c +++ b/libc/calls/execve.c @@ -60,7 +60,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) { if (i) kprintf(", "); kprintf("%#s", envp[i]); } - kprintf("})%n"); + kprintf("})\n"); } #endif for (i = 3; i < g_fds.n; ++i) { diff --git a/libc/calls/execvpe.c b/libc/calls/execvpe.c index 8227f2eb1..40824c924 100644 --- a/libc/calls/execvpe.c +++ b/libc/calls/execvpe.c @@ -38,6 +38,6 @@ int execvpe(const char *prog, char *const argv[], char *const *envp) { char *exe; char pathbuf[PATH_MAX]; if (IsAsan() && !__asan_is_valid(prog, 1)) return efault(); - if (!(exe = commandv(prog, pathbuf))) return -1; + if (!(exe = commandv(prog, pathbuf, sizeof(pathbuf)))) return -1; return execve(exe, argv, envp); } diff --git a/libc/calls/faccessat-nt.c b/libc/calls/faccessat-nt.c index c1c66d4da..2630da0fb 100644 --- a/libc/calls/faccessat-nt.c +++ b/libc/calls/faccessat-nt.c @@ -21,7 +21,8 @@ #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" -int sys_faccessat_nt(int dirfd, const char *path, int mode, uint32_t flags) { +textwindows int sys_faccessat_nt(int dirfd, const char *path, int mode, + uint32_t flags) { char16_t path16[PATH_MAX]; if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; return __fix_enotdir(ntaccesscheck(path16, mode), path16); diff --git a/libc/calls/faccessat.c b/libc/calls/faccessat.c index 6b2c7ac3b..cadba32f0 100644 --- a/libc/calls/faccessat.c +++ b/libc/calls/faccessat.c @@ -33,7 +33,7 @@ * file is a relative path, then file is opened relative to dirfd * @param path is a filename or directory * @param mode can be R_OK, W_OK, X_OK, F_OK - * @param flags should be 0 + * @param flags can have AT_EACCESS, AT_SYMLINK_NOFOLLOW * @return 0 if ok, or -1 and sets errno * @asyncsignalsafe */ diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 01ffa70c5..125ef8e09 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -106,7 +106,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) { st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow; st->st_blksize = PAGESIZE; st->st_dev = wst.dwVolumeSerialNumber; - st->st_rdev = wst.dwVolumeSerialNumber; + st->st_rdev = 0; st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow; st->st_nlink = wst.nNumberOfLinks; if (S_ISLNK(st->st_mode)) { 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/getcwd-nt.c b/libc/calls/getcwd-nt.c index b2236e276..475824679 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -28,15 +28,22 @@ textwindows char *sys_getcwd_nt(char *buf, size_t size) { uint64_t w; wint_t x, y; uint32_t n, i, j; - char16_t name16[PATH_MAX + 1]; - if ((n = GetCurrentDirectory(ARRAYLEN(name16), name16))) { - if (n <= PATH_MAX) { - tprecode16to8(buf, size, name16); - for (j = i = 0; i < n;) { - x = name16[i++] & 0xffff; + char16_t p[PATH_MAX]; + if ((n = GetCurrentDirectory(ARRAYLEN(p), p))) { + if (4 + n + 1 <= size && 4 + n + 1 <= ARRAYLEN(p)) { + tprecode16to8(buf, size, p); + j = 0; + if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { + buf[j++] = '/'; + buf[j++] = '/'; + buf[j++] = '?'; + buf[j++] = '/'; + } + for (i = 0; i < n;) { + x = p[i++] & 0xffff; if (!IsUcs2(x)) { if (i < n) { - y = name16[i++] & 0xffff; + y = p[i++] & 0xffff; x = MergeUtf16(x, y); } else { x = 0xfffd; diff --git a/libc/calls/getcwd.c b/libc/calls/getcwd.c index caa17091f..158185a56 100644 --- a/libc/calls/getcwd.c +++ b/libc/calls/getcwd.c @@ -51,7 +51,7 @@ char *getcwd(char *buf, size_t size) { } } else if (weaken(malloc)) { assert(!__vforked); - if (!size) size = PATH_MAX + 1; + if (!size) size = PATH_MAX; if (!(p = weaken(malloc)(size))) { STRACE("getcwd(%p, %'zu) %m", buf, size); return 0; diff --git a/libc/calls/getegid.c b/libc/calls/getegid.c index a6c4709bb..e142d34f1 100644 --- a/libc/calls/getegid.c +++ b/libc/calls/getegid.c @@ -19,13 +19,19 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" +#include "libc/dce.h" /** * Returns effective group ID of calling process. + * @return group id */ -uint32_t getegid(void) { +int getegid(void) { int rc; - rc = sys_getegid(); + if (!IsWindows()) { + rc = sys_getegid(); + } else { + rc = getgid(); + } STRACE("%s() → %d% m", "getegid", rc); return rc; } diff --git a/libc/calls/geteuid.c b/libc/calls/geteuid.c index f1990087c..ad16d7419 100644 --- a/libc/calls/geteuid.c +++ b/libc/calls/geteuid.c @@ -22,10 +22,15 @@ /** * Returns effective user ID of calling process. + * @return user id */ -uint32_t geteuid(void) { +int geteuid(void) { int rc; - rc = sys_geteuid(); + if (!IsWindows()) { + rc = sys_geteuid(); + } else { + rc = getuid(); + } STRACE("%s() → %d% m", "geteuid", rc); return rc; } diff --git a/libc/calls/program_executable_name.c b/libc/calls/getexecutablename.c similarity index 50% rename from libc/calls/program_executable_name.c rename to libc/calls/getexecutablename.c index 0ef78b22f..455942bb7 100644 --- a/libc/calls/program_executable_name.c +++ b/libc/calls/getexecutablename.c @@ -16,28 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/spinlock.h" -#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" -#include "libc/mem/alloca.h" #include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" -#include "libc/str/path.h" -#include "libc/str/str.h" -#include "libc/str/tpenc.h" -#include "libc/str/utf16.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/ok.h" -#include "libc/sysv/consts/prot.h" #define SIZE 1024 #define CTL_KERN 1 @@ -45,97 +32,90 @@ #define KERN_PROC_PATHNAME_FREEBSD 12 #define KERN_PROC_PATHNAME_NETBSD 5 -char program_executable_name[SIZE]; +char program_executable_name[PATH_MAX]; -static textwindows bool GetNtExePath(char exe[SIZE]) { - bool32 rc; - uint64_t w; - wint_t x, y; - uint32_t i, j; - char16_t path16[PATH_MAX + 1]; - path16[0] = 0; - rc = GetModuleFileName(0, path16, ARRAYLEN(path16)); - NTTRACE("GetModuleFileName(0, [%#hs]) → %hhhd", path16, rc); - if (!rc) return false; - for (i = j = 0; (x = path16[i++] & 0xffff);) { - if (!IsUcs2(x)) { - y = path16[i++] & 0xffff; - x = MergeUtf16(x, y); - } - if (x == '\\') x = '/'; - w = tpenc(x); - do { - exe[j] = w; - if (++j == SIZE) { - return false; - } - } while ((w >>= 8)); - } - exe[j] = 0; - return true; -} +static inline void GetProgramExecutableNameImpl(char *p, char *e) { + char *q; + ssize_t rc; + size_t i, n; + union { + int cmd[4]; + char16_t path16[PATH_MAX]; + } u; -static void ReadProgramExecutableName(char exe[SIZE], char *argv0, - uintptr_t *auxv) { - int e; - size_t m; - ssize_t n; - int cmd[4]; - char *p, *t; - e = errno; - if (!IsWindows() || !GetNtExePath(exe)) { - for (p = 0; *auxv; auxv += 2) { - if (*auxv == AT_EXECFN) { - p = (char *)auxv[1]; - break; + if (IsWindows()) { + n = GetModuleFileName(0, u.path16, ARRAYLEN(u.path16)); + for (i = 0; i < n; ++i) { + if (u.path16[i] == '\\') { + u.path16[i] = '/'; } } - n = 0; - if (!p) p = argv0; - if (p) { - if (!_isabspath(p)) { - if (getcwd(exe, SIZE - 1)) { - n = strlen(exe); - exe[n++] = '/'; - } - } - for (; *p; ++p) { - if (n + 1 < SIZE) { - exe[n++] = *p; - } - } + if (isalpha(u.path16[0]) && u.path16[1] == ':' && u.path16[2] == '/') { + p[0] = '/'; + p[1] = '/'; + p[2] = '?'; + p[3] = '/'; + p += 4; + } + tprecode16to8(p, e - p, u.path16); + return; + } + + if (__argc && (q = __argv[0]) && !sys_faccessat(AT_FDCWD, q, F_OK, 0)) { + if (*q != '/') { + if (q[0] == '.' && q[1] == '/') { + q += 2; + } + if (getcwd(p, e - p)) { + while (*p) ++p; + *p++ = '/'; + } + } + for (i = 0; *q && p + 1 < e; ++p, ++q) { + *p = *q; + } + *p = 0; + return; + } + + if ((rc = sys_readlinkat(AT_FDCWD, "/proc/self/exe", p, e - p - 1)) > 0 || + (rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, e - p - 1)) > 0) { + p[rc] = 0; + return; + } + + if (IsFreebsd() || IsNetbsd()) { + u.cmd[0] = CTL_KERN; + u.cmd[1] = KERN_PROC; + if (IsFreebsd()) { + u.cmd[2] = KERN_PROC_PATHNAME_FREEBSD; + } else { + u.cmd[2] = KERN_PROC_PATHNAME_NETBSD; + } + u.cmd[3] = -1; // current process + n = e - p; + if (sysctl(u.cmd, ARRAYLEN(u.cmd), p, &n, 0, 0) != -1) { + return; } - exe[n] = 0; } - errno = e; } /** * Returns absolute path of executable. - * - * This variable is initialized automatically at startup. The path is - * basically `argv[0]` except some extra vetting is done to provide - * stronger assurance that the path can be counted upon to exist. - * - * For example, if your program is executed as a relative path and then - * your program calls `chdir()`, then `argv[0]` will be incorrect; but - * `program_executable_name` will work, because it prefixed `getcwd()` - * early in the initialization phase. - * - * @see GetInterpreterExecutableName() - * @see program_invocation_short_name - * @see program_invocation_name */ char *GetProgramExecutableName(void) { + int e; static bool once; if (!once) { - ReadProgramExecutableName(program_executable_name, __argv[0], __auxv); - once = true; + e = errno; + GetProgramExecutableNameImpl( + program_executable_name, + program_executable_name + sizeof(program_executable_name)); + errno = e; } return program_executable_name; } -// try our best to memoize it before a chdir() happens -const void *const program_executable_name_init_ctor[] initarray = { +const void *const GetProgramExecutableNameCtor[] initarray = { GetProgramExecutableName, }; diff --git a/libc/calls/getpgrp.c b/libc/calls/getpgrp.c index e6edd52f4..9a15d1c6e 100644 --- a/libc/calls/getpgrp.c +++ b/libc/calls/getpgrp.c @@ -24,7 +24,7 @@ /** * Returns process group id of calling process. */ -uint32_t getpgrp(void) { +int getpgrp(void) { int rc; if (!IsWindows()) { rc = sys_getpgrp(); diff --git a/libc/calls/getresgid.c b/libc/calls/getresgid.c new file mode 100644 index 000000000..1760de051 --- /dev/null +++ b/libc/calls/getresgid.c @@ -0,0 +1,51 @@ +/*-*- 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 2020 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" + +/** + * Gets real, effective, and "saved" group ids. + * + * @param real receives real user id, or null to do nothing + * @param effective receives effective user id, or null to do nothing + * @param saved receives saved user id, or null to do nothing + * @return 0 on success or -1 w/ errno + * @see setresuid() + */ +int getresgid(uint32_t *real, uint32_t *effective, uint32_t *saved) { + int rc, gid; + if (IsWindows()) { + gid = getgid(); + if (real) *real = gid; + if (effective) *effective = gid; + if (saved) *saved = gid; + rc = 0; + } else if (saved) { + rc = sys_getresgid(real, effective, saved); + } else { + if (real) *real = sys_getgid(); + if (effective) *effective = sys_getegid(); + rc = 0; + } + STRACE("getresgid([%d], [%d], [%d]) → %d% m", real ? *real : 0, + effective ? *effective : 0, saved ? *saved : 0, rc); + return rc; +} diff --git a/libc/calls/getresuid.c b/libc/calls/getresuid.c new file mode 100644 index 000000000..80a0f10a7 --- /dev/null +++ b/libc/calls/getresuid.c @@ -0,0 +1,51 @@ +/*-*- 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 2020 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" + +/** + * Gets real, effective, and "saved" user ids. + * + * @param real receives real user id, or null to do nothing + * @param effective receives effective user id, or null to do nothing + * @param saved receives saved user id, or null to do nothing + * @return 0 on success or -1 w/ errno + * @see setresuid() + */ +int getresuid(uint32_t *real, uint32_t *effective, uint32_t *saved) { + int rc, uid; + if (IsWindows()) { + uid = getuid(); + if (real) *real = uid; + if (effective) *effective = uid; + if (saved) *saved = uid; + rc = 0; + } else if (saved) { + rc = sys_getresuid(real, effective, saved); + } else { + if (real) *real = sys_getuid(); + if (effective) *effective = sys_geteuid(); + rc = 0; + } + STRACE("getresuid([%d], [%d], [%d]) → %d% m", real ? *real : 0, + effective ? *effective : 0, saved ? *saved : 0, rc); + return rc; +} diff --git a/libc/calls/getrusage-nt.c b/libc/calls/getrusage-nt.c index 341736356..35bb44e50 100644 --- a/libc/calls/getrusage-nt.c +++ b/libc/calls/getrusage-nt.c @@ -18,11 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sig.internal.h" #include "libc/calls/struct/rusage.h" #include "libc/fmt/conv.h" +#include "libc/intrin/spinlock.h" #include "libc/nt/accounting.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/iocounters.h" #include "libc/nt/struct/processmemorycounters.h" #include "libc/nt/thread.h" #include "libc/str/str.h" @@ -30,22 +33,32 @@ #include "libc/sysv/errfuns.h" textwindows int sys_getrusage_nt(int who, struct rusage *usage) { + int64_t me, nsignals; + struct NtIoCounters iocount; struct NtProcessMemoryCountersEx memcount; struct NtFileTime ftExit, ftUser, ftKernel, ftCreation; if (!usage) return efault(); if (who == 99) return enosys(); // @see libc/sysv/consts.sh if (!usage) return 0; + me = GetCurrentProcess(); if (!(who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)( (who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(), &ftCreation, &ftExit, &ftKernel, &ftUser) || - !GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) { + !GetProcessMemoryInfo(me, &memcount, sizeof(memcount)) || + !GetProcessIoCounters(me, &iocount)) { return __winerr(); } + _spinlock(&__sig_lock); + nsignals = __sig_count; + _spunlock(&__sig_lock); *usage = (struct rusage){ .ru_utime = WindowsDurationToTimeVal(ReadFileTime(ftUser)), .ru_stime = WindowsDurationToTimeVal(ReadFileTime(ftKernel)), .ru_maxrss = memcount.PeakWorkingSetSize / 1024, .ru_majflt = memcount.PageFaultCount, + .ru_inblock = iocount.ReadOperationCount, + .ru_oublock = iocount.WriteOperationCount, + .ru_nsignals = __sig_count, }; return 0; } diff --git a/libc/calls/getsid.c b/libc/calls/getsid.c index 4c29139f2..1d199e2b0 100644 --- a/libc/calls/getsid.c +++ b/libc/calls/getsid.c @@ -23,7 +23,7 @@ /** * Creates session and sets the process group id. */ -uint32_t getsid(int pid) { +int getsid(int pid) { int rc; rc = sys_getsid(pid); STRACE("%s(%d) → %d% m", "getsid", pid, rc); diff --git a/libc/calls/gettimeofday-nt.c b/libc/calls/gettimeofday-nt.c index becf42dd2..cf113f721 100644 --- a/libc/calls/gettimeofday-nt.c +++ b/libc/calls/gettimeofday-nt.c @@ -25,7 +25,7 @@ #include "libc/str/str.h" #include "libc/time/struct/timezone.h" -int sys_gettimeofday_nt(struct timeval *tv, struct timezone *tz) { +textwindows int sys_gettimeofday_nt(struct timeval *tv, struct timezone *tz) { struct NtFileTime ft; GetSystemTimeAsFileTime(&ft); if (tv) *tv = FileTimeToTimeVal(ft); diff --git a/libc/calls/gettimeofday.c b/libc/calls/gettimeofday.c index 425549707..927e785a4 100644 --- a/libc/calls/gettimeofday.c +++ b/libc/calls/gettimeofday.c @@ -25,6 +25,8 @@ #include "libc/time/struct/timezone.h" #include "libc/time/time.h" +static typeof(sys_gettimeofday) *__gettimeofday = sys_gettimeofday; + /** * Returns system wall time in microseconds. * @@ -40,7 +42,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { return efault(); } if (!IsWindows() && !IsMetal()) { - ad = sys_gettimeofday(tv, tz, NULL); + ad = __gettimeofday(tv, tz, NULL); assert(ad.ax != -1); if (SupportsXnu() && ad.ax && tv) { tv->tv_sec = ad.ax; @@ -53,3 +55,14 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { return sys_gettimeofday_nt(tv, tz); } } + +static textstartup void __gettimeofday_init(void) { + void *vdso; + if ((vdso = __vdsofunc("__vdso_gettimeofday"))) { + __gettimeofday = vdso; + } +} + +const void *const __gettimeofday_ctor[] initarray = { + __gettimeofday_init, +}; 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/internal.h b/libc/calls/internal.h index 713383060..50c0e3a85 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -88,7 +88,7 @@ int __reservefd(int) hidden; void __releasefd(int) hidden; int __ensurefds(int) hidden; int64_t __getfdhandleactual(int) hidden; -void __printfds(void); +void __printfds(void) hidden; forceinline bool __isfdopen(int fd) { return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind != kFdEmpty; @@ -163,8 +163,11 @@ i32 sys_getpgid(i32) hidden; i32 sys_getpgrp(void) hidden; i32 sys_getppid(void) hidden; i32 sys_getpriority(i32, u32) hidden; +i32 sys_getresgid(u32 *, u32 *, u32 *); +i32 sys_getresuid(u32 *, u32 *, u32 *); i32 sys_getrlimit(i32, struct rlimit *) hidden; i32 sys_getrusage(i32, struct rusage *) hidden; +i32 sys_getsid(int) hidden; i32 sys_ioctl(i32, u64, ...) hidden; i32 sys_kill(i32, i32, i32) hidden; i32 sys_linkat(i32, const char *, i32, const char *, i32) hidden; @@ -183,6 +186,7 @@ i32 sys_openat(i32, const char *, i32, u32) hidden; i32 sys_pause(void) hidden; i32 sys_pipe(i32[hasatleast 2]) hidden; i32 sys_pipe2(i32[hasatleast 2], u32) hidden; +i32 sys_pledge(const char *, const char *) hidden; i32 sys_posix_openpt(i32) hidden; i32 sys_renameat(i32, const char *, i32, const char *) hidden; i32 sys_sched_setaffinity(i32, u64, const void *) hidden; @@ -191,8 +195,10 @@ i32 sys_setgid(i32) hidden; i32 sys_setitimer(i32, const struct itimerval *, struct itimerval *) hidden; i32 sys_setpgid(i32, i32) hidden; i32 sys_setpriority(i32, u32, i32) hidden; -i32 sys_setresgid(uint32_t, uint32_t, uint32_t) hidden; -i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden; +i32 sys_setregid(u32, u32) hidden; +i32 sys_setresgid(u32, u32, u32) hidden; +i32 sys_setresuid(u32, u32, u32) hidden; +i32 sys_setreuid(u32, u32) hidden; i32 sys_setrlimit(i32, const struct rlimit *) hidden; i32 sys_setsid(void) hidden; i32 sys_setuid(i32) hidden; @@ -230,7 +236,6 @@ i64 sys_write(i32, const void *, u64) hidden; u32 sys_getegid(void) hidden; u32 sys_geteuid(void) hidden; u32 sys_getgid(void) hidden; -u32 sys_getsid(int) hidden; u32 sys_gettid(void) hidden; u32 sys_getuid(void) hidden; u32 sys_umask(u32) hidden; @@ -243,6 +248,8 @@ void sys_exit(int) hidden; ╚────────────────────────────────────────────────────────────────────────────│*/ void __onfork(void) hidden; +void *__vdsofunc(const char *) hidden; +void *__get_clock_gettime(void) hidden; i32 __fixupnewfd(i32, i32) hidden; void __restore_rt() hidden; int sys_utimensat_xnu(int, const char *, const struct timespec *, int) hidden; @@ -326,6 +333,7 @@ int ioctl_tiocgwinsz_nt(struct Fd *, struct winsize *) hidden; │ cosmopolitan § syscalls » windows nt » support ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +bool __is_linux_2_6_23(void) hidden; int64_t __fix_enotdir(int64_t, char16_t *) hidden; int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *) hidden; bool _check_interrupts(bool, struct Fd *) hidden; @@ -337,9 +345,9 @@ bool isregularfile_nt(const char *) hidden; bool issymlink_nt(const char *) hidden; bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; char16_t *CreatePipeName(char16_t *) hidden; -int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden; -int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden; -int __mkntpathat(int, const char *, int, char16_t[PATH_MAX]) hidden; +int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]) hidden; +int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int) hidden; +int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]) hidden; int sys_clock_gettime_nt(int, struct timespec *) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; int sys_getsetpriority_nt(int, int, int, int (*)(int)); diff --git a/libc/calls/ioctl_tcgets-nt.c b/libc/calls/ioctl_tcgets-nt.c index c3b0110ac..ac04c84f5 100644 --- a/libc/calls/ioctl_tcgets-nt.c +++ b/libc/calls/ioctl_tcgets-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/termios.h" +#include "libc/calls/ttydefaults.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" @@ -35,6 +36,23 @@ textwindows int ioctl_tcgets_nt(int ignored, struct termios *tio) { outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode); if (inok | outok) { bzero(tio, sizeof(*tio)); + + tio->c_cflag |= CS8; + + tio->c_cc[VINTR] = CTRL('C'); + tio->c_cc[VQUIT] = CTRL('\\'); + tio->c_cc[VERASE] = CTRL('?'); + tio->c_cc[VKILL] = CTRL('U'); + tio->c_cc[VEOF] = CTRL('D'); + tio->c_cc[VMIN] = CTRL('A'); + tio->c_cc[VSTART] = CTRL('Q'); + tio->c_cc[VSTOP] = CTRL('S'); + tio->c_cc[VSUSP] = CTRL('Z'); + tio->c_cc[VREPRINT] = CTRL('R'); + tio->c_cc[VDISCARD] = CTRL('O'); + tio->c_cc[VWERASE] = CTRL('W'); + tio->c_cc[VLNEXT] = CTRL('V'); + if (inok) { if (inmode & kNtEnableLineInput) { tio->c_lflag |= ICANON; @@ -44,16 +62,21 @@ textwindows int ioctl_tcgets_nt(int ignored, struct termios *tio) { } if (inmode & kNtEnableProcessedInput) { tio->c_lflag |= IEXTEN | ISIG; + if (tio->c_lflag | ECHO) { + tio->c_lflag |= ECHOE; + } } } + if (outok) { if (outmode & kNtEnableProcessedOutput) { tio->c_oflag |= OPOST; } if (!(outmode & kNtDisableNewlineAutoReturn)) { - tio->c_oflag |= ONLCR; + tio->c_oflag |= OPOST | ONLCR; } } + return 0; } else { return enotty(); diff --git a/libc/calls/ioctl_tcsets-nt.c b/libc/calls/ioctl_tcsets-nt.c index a457a1d4e..9eb023cad 100644 --- a/libc/calls/ioctl_tcsets-nt.c +++ b/libc/calls/ioctl_tcsets-nt.c @@ -17,8 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" #include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/termios.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/enum/version.h" @@ -29,35 +31,54 @@ textwindows int ioctl_tcsets_nt(int ignored, uint64_t request, const struct termios *tio) { int64_t in, out; - bool32 inok, outok; + bool32 ok, inok, outok; uint32_t inmode, outmode; inok = GetConsoleMode((in = __getfdhandleactual(0)), &inmode); outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode); if (inok | outok) { + if (inok) { if (request == TCSETSF) { FlushConsoleInputBuffer(in); } inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput); - if (tio->c_lflag & ICANON) inmode |= kNtEnableLineInput; - if (tio->c_lflag & ECHO) inmode |= kNtEnableEchoInput; - if (tio->c_lflag & (IEXTEN | ISIG)) inmode |= kNtEnableProcessedInput; inmode |= kNtEnableWindowInput; + if (tio->c_lflag & ICANON) { + inmode |= kNtEnableLineInput; + } + if (tio->c_lflag & ECHO) { + /* + * kNtEnableEchoInput can be used only if the ENABLE_LINE_INPUT mode + * is also enabled. --Quoth MSDN + */ + inmode |= kNtEnableEchoInput | kNtEnableLineInput; + } + if (tio->c_lflag & (IEXTEN | ISIG)) { + inmode |= kNtEnableProcessedInput; + } if (NtGetVersion() >= kNtVersionWindows10) { inmode |= kNtEnableVirtualTerminalInput; } - SetConsoleMode(in, inmode); + ok = SetConsoleMode(in, inmode); + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", in, + DescribeNtConsoleModeInputFlags(inmode), ok); } + if (outok) { - outmode |= kNtEnableWrapAtEolOutput; - if (tio->c_oflag & OPOST) outmode |= kNtEnableProcessedOutput; - if (!(tio->c_oflag & ONLCR)) outmode |= kNtDisableNewlineAutoReturn; + outmode &= ~(kNtDisableNewlineAutoReturn); + outmode |= kNtEnableProcessedOutput; + if (!(tio->c_oflag & ONLCR)) { + outmode |= kNtDisableNewlineAutoReturn; + } if (NtGetVersion() >= kNtVersionWindows10) { outmode |= kNtEnableVirtualTerminalProcessing; } - SetConsoleMode(out, outmode); + ok = SetConsoleMode(out, outmode); + NTTRACE("SetConsoleMode(%p, %s) → %hhhd", out, + DescribeNtConsoleModeOutputFlags(outmode), ok); } + return 0; } else { return enotty(); diff --git a/libc/calls/ioctl_tcsets.c b/libc/calls/ioctl_tcsets.c index b7f8a4155..34e2592fa 100644 --- a/libc/calls/ioctl_tcsets.c +++ b/libc/calls/ioctl_tcsets.c @@ -83,11 +83,6 @@ int ioctl_tcsets(int fd, uint64_t request, ...) { } else { rc = einval(); } - if (rc != -1) { - if (__nomultics == 0 || __nomultics == 1) { - __nomultics = !(tio->c_oflag & OPOST); - } - } STRACE("ioctl_tcsets(%d, %p, %p) → %d% m", fd, request, tio, rc); return rc; } diff --git a/libc/calls/islinux.c b/libc/calls/islinux.c new file mode 100644 index 000000000..1209f959e --- /dev/null +++ b/libc/calls/islinux.c @@ -0,0 +1,32 @@ +/*-*- 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/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/sysv/consts/pr.h" + +bool __is_linux_2_6_23(void) { + int rc; + if (!IsLinux()) return false; + asm volatile("syscall" + : "=a"(rc) + : "0"(157), "D"(PR_GET_SECCOMP) + : "rcx", "r11", "memory"); + return rc != -EINVAL; +} 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..a0749a5af --- /dev/null +++ b/libc/calls/kopenflags.S @@ -0,0 +1,65 @@ +/*-*- 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_NONBLOCK,"NONBLOCK" // + .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/mkdir.c b/libc/calls/mkdir.c index cda89a91c..d41c67814 100644 --- a/libc/calls/mkdir.c +++ b/libc/calls/mkdir.c @@ -36,7 +36,7 @@ * @param path is a UTF-8 string, preferably relative w/ forward slashes * @param mode can be, for example, 0755 * @return 0 on success or -1 w/ errno - * @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES + * @error ENAMETOOLONG if >246 characters on NT * @asyncsignalsafe * @see makedirs() */ diff --git a/libc/calls/mkdirat-nt.c b/libc/calls/mkdirat-nt.c index b167b9b5b..16133342a 100644 --- a/libc/calls/mkdirat-nt.c +++ b/libc/calls/mkdirat-nt.c @@ -18,10 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/nt/files.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" textwindows int sys_mkdirat_nt(int dirfd, const char *path, uint32_t mode) { int e; char16_t *p, path16[PATH_MAX]; + /* if (strlen(path) > 248) return enametoolong(); */ if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if (CreateDirectory(path16, 0)) return 0; return __fix_enotdir(-1, path16); diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index 78694df12..29eacc9b1 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -23,10 +23,12 @@ #include "libc/str/utf16.h" #include "libc/sysv/errfuns.h" -#define APPEND(c) \ - do { \ - cmdline[k++] = c; \ - if (k == ARG_MAX) return e2big(); \ +#define APPEND(c) \ + do { \ + cmdline[k++] = c; \ + if (k == ARG_MAX / 2) { \ + return e2big(); \ + } \ } while (0) static noasan bool NeedsQuotes(const char *s) { @@ -52,8 +54,8 @@ static noasan bool NeedsQuotes(const char *s) { * @return freshly allocated lpCommandLine or NULL w/ errno * @see libc/runtime/dosargv.c */ -textwindows noasan int mkntcmdline(char16_t cmdline[ARG_MAX], const char *prog, - char *const argv[]) { +textwindows noasan int mkntcmdline(char16_t cmdline[ARG_MAX / 2], + const char *prog, char *const argv[]) { char *arg; uint64_t w; wint_t x, y; diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index 5c94eb3d0..95287bb6d 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -55,9 +55,9 @@ static noasan void InsertString(char **a, size_t i, char *s) { * @param envp is an a NULL-terminated array of UTF-8 strings * @param extravar is a VAR=val string we consider part of envp or NULL * @return 0 on success, or -1 w/ errno - * @error E2BIG if total number of shorts exceeded ARG_MAX (0x8000) + * @error E2BIG if total number of shorts exceeded ARG_MAX/2 (32767) */ -textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX], +textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[], const char *extravar) { bool v; char *t; @@ -98,7 +98,9 @@ textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX], w = EncodeUtf16(x); do { envvars[k++] = w & 0xffff; - if (k == ARG_MAX) return e2big(); + if (k == ARG_MAX / 2) { + return e2big(); + } } while ((w >>= 16)); } while (x); } diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index 85248baad..a2a36a28a 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/strace.internal.h" +#include "libc/macros.internal.h" #include "libc/nt/systeminfo.h" #include "libc/str/oldutf16.internal.h" #include "libc/str/str.h" @@ -49,7 +50,7 @@ textwindows static const char *FixNtMagicPath(const char *path, } textwindows int __mkntpath(const char *path, - char16_t path16[hasatleast PATH_MAX - 16]) { + char16_t path16[hasatleast PATH_MAX]) { return __mkntpath2(path, path16, -1); } @@ -67,14 +68,13 @@ textwindows int __mkntpath(const char *path, * @error ENAMETOOLONG */ textwindows int __mkntpath2(const char *path, - char16_t path16[hasatleast PATH_MAX - 16], - int flags) { + char16_t path16[hasatleast PATH_MAX], int flags) { /* - * 1. Reserve +1 for NUL-terminator - * 2. Reserve +1 for UTF-16 overflow - * 3. Reserve ≥2 for SetCurrentDirectory trailing slash requirement - * 4. Reserve ≥10 for CreateNamedPipe "\\.\pipe\" prefix requirement - * 5. Reserve ≥13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0" + * 1. Need +1 for NUL-terminator + * 2. Need +1 for UTF-16 overflow + * 3. Need ≥2 for SetCurrentDirectory trailing slash requirement + * 5. Need ≥13 for mkdir() i.e. 1+8+3+1, e.g. "\\ffffffff.xxx\0" + * which is an "8.3 filename" from the DOS days */ char16_t *p; const char *q; @@ -83,7 +83,12 @@ textwindows int __mkntpath2(const char *path, path = FixNtMagicPath(path, flags); p = path16; q = path; - z = PATH_MAX - 16; + if (IsSlash(path[0]) && IsSlash(path[1]) && path[2] == '?' && + IsSlash(path[3])) { + z = MIN(32767, PATH_MAX); + } else { + z = MIN(260, PATH_MAX); + } if (IsSlash(q[0]) && q[1] == 't' && q[2] == 'm' && q[3] == 'p' && (IsSlash(q[4]) || !q[4])) { m = GetTempPath(z, p); @@ -95,7 +100,7 @@ textwindows int __mkntpath2(const char *path, m = 0; } n = tprecode8to16(p, z, q).ax; - if (n == z - 1) { + if (n >= z - 1) { STRACE("path too long for windows: %#s", path); return enametoolong(); } diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index 0bd1e4e12..f2258721b 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -25,7 +25,7 @@ #include "libc/sysv/errfuns.h" int __mkntpathat(int dirfd, const char *path, int flags, - char16_t file[PATH_MAX]) { + char16_t file[hasatleast PATH_MAX]) { char16_t dir[PATH_MAX]; uint32_t dirlen, filelen; if ((filelen = __mkntpath2(path, file, flags)) == -1) return -1; diff --git a/libc/calls/nanosleep-nt.c b/libc/calls/nanosleep-nt.c index c7b7d7125..275bf3086 100644 --- a/libc/calls/nanosleep-nt.c +++ b/libc/calls/nanosleep-nt.c @@ -20,6 +20,7 @@ #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" #include "libc/errno.h" +#include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nt/errors.h" #include "libc/nt/nt/time.h" @@ -34,7 +35,7 @@ textwindows noinstrument int sys_nanosleep_nt(const struct timespec *req, int64_t ms, sec, nsec; if (__builtin_mul_overflow(req->tv_sec, 1000, &ms) || __builtin_add_overflow(ms, req->tv_nsec / 1000000, &ms)) { - ms = -1; + ms = INT64_MAX; } if (!ms && (req->tv_sec || req->tv_nsec)) { ms = 1; diff --git a/libc/calls/now.c b/libc/calls/now.c index 2e5cf754a..a7ec88b88 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -31,9 +31,9 @@ #include "libc/time/time.h" static struct Now { - bool once; uint64_t k0; long double r0, cpn; + typeof(sys_clock_gettime) *clock_gettime; } g_now; static long double GetTimeSample(void) { @@ -73,22 +73,39 @@ void RefreshTime(void) { now.cpn = MeasureNanosPerCycle(); now.r0 = dtime(CLOCK_REALTIME); now.k0 = rdtsc(); - now.once = true; memcpy(&g_now, &now, sizeof(now)); } -long double ConvertTicksToNanos(uint64_t ticks) { - if (!g_now.once) RefreshTime(); - return ticks * g_now.cpn; /* pico scale */ -} - -long double nowl_sys(void) { +static long double nowl_sys(void) { return dtime(CLOCK_REALTIME); } -long double nowl_art(void) { - uint64_t ticks; - if (!g_now.once) RefreshTime(); - ticks = unsignedsubtract(rdtsc(), g_now.k0); +static long double nowl_art(void) { + uint64_t ticks = rdtsc() - g_now.k0; return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn)); } + +static long double nowl_vdso(void) { + long double secs; + struct timespec tv; + g_now.clock_gettime(CLOCK_REALTIME, &tv); + secs = tv.tv_nsec; + secs *= 1 / 1e9L; + secs += tv.tv_sec; + return secs; +} + +long double nowl_setup(void) { + uint64_t ticks; + if ((g_now.clock_gettime = __get_clock_gettime())) { + nowl = nowl_vdso; + } else if (X86_HAVE(INVTSC)) { + RefreshTime(); + nowl = nowl_art; + } else { + nowl = nowl_sys; + } + return nowl(); +} + +long double (*nowl)(void) = nowl_setup; diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index d5f5055bb..fe63fa82a 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -32,8 +32,13 @@ #include "libc/nt/struct/startupinfo.h" struct SpawnBlock { - char16_t cmdline[ARG_MAX]; - char16_t envvars[ARG_MAX]; + union { + struct { + char16_t cmdline[ARG_MAX / 2]; + char16_t envvars[ARG_MAX / 2]; + }; + char __pad[ROUNDUP(ARG_MAX / 2 * 2 * sizeof(char16_t), FRAMESIZE)]; + }; }; /** @@ -68,20 +73,15 @@ textwindows int ntspawn( struct NtProcessInformation *opt_out_lpProcessInformation) { int rc; int64_t handle; - size_t blocksize; struct SpawnBlock *block; char16_t prog16[PATH_MAX]; rc = -1; block = NULL; if (__mkntpath(prog, prog16) == -1) return -1; - blocksize = ROUNDUP(sizeof(*block), FRAMESIZE); - if ((handle = CreateFileMapping( - -1, - &(struct NtSecurityAttributes){sizeof(struct NtSecurityAttributes), - NULL, false}, - pushpop(kNtPageReadwrite), 0, blocksize, NULL)) && + if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0, + sizeof(*block), 0)) && (block = MapViewOfFileEx(handle, kNtFileMapRead | kNtFileMapWrite, 0, 0, - blocksize, NULL)) && + sizeof(*block), 0)) && mkntcmdline(block->cmdline, prog, argv) != -1 && mkntenvblock(block->envvars, envp, extravar) != -1 && CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, diff --git a/libc/calls/ntspawn.h b/libc/calls/ntspawn.h index b794d5cce..df1955b43 100644 --- a/libc/calls/ntspawn.h +++ b/libc/calls/ntspawn.h @@ -6,8 +6,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int mkntcmdline(char16_t[ARG_MAX], const char *, char *const[]) hidden; -int mkntenvblock(char16_t[ARG_MAX], char *const[], const char *) hidden; +int mkntcmdline(char16_t[ARG_MAX / 2], const char *, char *const[]) hidden; +int mkntenvblock(char16_t[ARG_MAX / 2], char *const[], const char *) hidden; int ntspawn(const char *, char *const[], char *const[], const char *, struct NtSecurityAttributes *, struct NtSecurityAttributes *, bool32, uint32_t, const char16_t *, const struct NtStartupInfo *, diff --git a/libc/calls/oldbench.c b/libc/calls/oldbench.c new file mode 100644 index 000000000..ee1ab4c41 --- /dev/null +++ b/libc/calls/oldbench.c @@ -0,0 +1,83 @@ +/*-*- 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 2020 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/bits/bits.h" +#include "libc/bits/initializer.internal.h" +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/macros.internal.h" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/x86feature.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/clock.h" +#include "libc/time/time.h" + +static struct Now { + bool once; + uint64_t k0; + long double r0, cpn; +} g_now; + +static long double GetTimeSample(void) { + uint64_t tick1, tick2; + long double time1, time2; + sched_yield(); + time1 = dtime(CLOCK_REALTIME); + tick1 = rdtsc(); + nanosleep(&(struct timespec){0, 1000000}, NULL); + time2 = dtime(CLOCK_REALTIME); + tick2 = rdtsc(); + return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); +} + +static long double MeasureNanosPerCycle(void) { + bool tc; + int i, n; + long double avg, samp; + tc = __time_critical; + __time_critical = true; + if (IsWindows()) { + n = 30; + } else { + n = 20; + } + for (avg = 1.0L, i = 1; i < n; ++i) { + samp = GetTimeSample(); + avg += (samp - avg) / i; + } + __time_critical = tc; + STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); + return avg; +} + +static void Refresh(void) { + struct Now now; + now.cpn = MeasureNanosPerCycle(); + now.r0 = dtime(CLOCK_REALTIME); + now.k0 = rdtsc(); + now.once = true; + memcpy(&g_now, &now, sizeof(now)); +} + +long double ConvertTicksToNanos(uint64_t ticks) { + if (!g_now.once) Refresh(); + return ticks * g_now.cpn; /* pico scale */ +} 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/openbsd.internal.h b/libc/calls/openbsd.internal.h index 8a8b62bc7..b158ac9ff 100644 --- a/libc/calls/openbsd.internal.h +++ b/libc/calls/openbsd.internal.h @@ -8,8 +8,6 @@ COSMOPOLITAN_C_START_ typedef unsigned char u_char; -int pledge(const char *promises, const char *execpromises); - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_COMPAT_OPENBSD_H_ */ diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 7728acdcd..0c0db0823 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -23,6 +23,7 @@ #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/ipc.h" #include "libc/nt/runtime.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" @@ -42,9 +43,9 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { } else { mode = kNtPipeTypeMessage | kNtPipeReadmodeMessage; } - if ((hin = CreateNamedPipe(pipename, - kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, - 1, 512, 512, 0, &kNtIsInheritable)) != -1) { + if ((hin = CreateNamedPipe( + pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, mode, 1, + PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable)) != -1) { if ((hout = CreateFile(pipename, kNtGenericWrite, 0, &kNtIsInheritable, kNtOpenExisting, kNtFileFlagOverlapped, 0)) != -1) { g_fds.p[reader].kind = kFdFile; diff --git a/libc/calls/poll.c b/libc/calls/poll.c index acada2c86..f58228e32 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -95,7 +95,7 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) { DescribePollFlags(flagbuf[0], sizeof(flagbuf[0]), fds[i].events), DescribePollFlags(flagbuf[1], sizeof(flagbuf[1]), fds[i].revents)); } - kprintf("%s}, %'zu, %'d) → %d% lm%n", i == 5 ? "..." : "", nfds, + kprintf("%s}, %'zu, %'d) → %d% lm\n", i == 5 ? "..." : "", nfds, timeout_ms, rc); } } diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index b4faa2411..94f83a1b8 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -118,7 +118,7 @@ ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { } else { kprintf(STRACE_PROLOGUE "preadv(%d, [", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf("], %d, %'ld) → %'ld% m%n", iovlen, off, rc); + kprintf("], %d, %'ld) → %'ld% m\n", iovlen, off, rc); } } #endif diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index 8b5d93ffd..26ad273b2 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" static const char *__fdkind2str(int x) { switch (x) { @@ -44,6 +45,7 @@ static const char *__fdkind2str(int x) { void __printfds(void) { int i; + _spinlock(&__fds_lock); for (i = 0; i < g_fds.n; ++i) { if (!g_fds.p[i].kind) continue; kprintf("%3d %s", i, __fdkind2str(g_fds.p[i].kind)); @@ -53,6 +55,7 @@ void __printfds(void) { if (g_fds.p[i].handle) kprintf(" handle=%ld", g_fds.p[i].handle); if (g_fds.p[i].extra) kprintf(" extra=%ld", g_fds.p[i].extra); if (g_fds.p[i].worker) kprintf(" worker=%p", g_fds.p[i].worker); - kprintf("%n", g_fds.p[i].zombie); + kprintf("\n"); } + _spunlock(&__fds_lock); } diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index e6e08df84..a480338bc 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -123,7 +123,7 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { } else { kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf(", %d, %'ld) → %'ld% m%n", iovlen, off, rc); + kprintf(", %d, %'ld) → %'ld% m\n", iovlen, off, rc); } } #endif diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index bc9ac7048..e55136d35 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -27,6 +27,7 @@ #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/reparsedatabuffer.h" +#include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" #include "libc/sysv/errfuns.h" @@ -56,6 +57,12 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t); p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset); + if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { + buf[j++] = '/'; + buf[j++] = '/'; + buf[j++] = '?'; + buf[j++] = '/'; + } while (i < n) { x = p[i++] & 0xffff; if (!IsUcs2(x)) { diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 287641fd9..490f02c1c 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -72,7 +72,7 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) { } else { kprintf(STRACE_PROLOGUE "readv(%d, [", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf("], %d) → %'ld% m%n", iovlen, rc); + kprintf("], %d) → %'ld% m\n", iovlen, rc); } } #endif diff --git a/libc/calls/realpath.c b/libc/calls/realpath.c index 4f0a5aed6..cb79c7142 100644 --- a/libc/calls/realpath.c +++ b/libc/calls/realpath.c @@ -81,7 +81,7 @@ char *realpath(const char *filename, char *resolved) ssize_t rc; int e, up, check_dir=0; size_t k, p, q, l, l0, cnt=0, nup=0; - char output[PATH_MAX], stack[PATH_MAX+1], *z; + char output[PATH_MAX], stack[PATH_MAX], *z; if (!filename) { einval(); diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index 1937d2e08..a68a56e7c 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -88,9 +88,9 @@ int __reservefd(int start) { /** * Closes non-stdio file descriptors to free dynamic memory. */ -static void __freefds(void) { +static void FreeFds(void) { int i; - NTTRACE("__freefds()"); + NTTRACE("FreeFds()"); for (i = 3; i < g_fds.n; ++i) { if (g_fds.p[i].kind) { close(i); @@ -104,10 +104,10 @@ static void __freefds(void) { } } -static textstartup void __freefds_init(void) { - atexit(__freefds); +static textstartup void FreeFdsInit(void) { + atexit(FreeFds); } -const void *const __freefds_ctor[] initarray = { - __freefds_init, +const void *const FreeFdsCtor[] initarray = { + FreeFdsInit, }; diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index 508d61748..f1c8df0cd 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -29,8 +29,8 @@ #include "libc/str/str.h" static textwindows dontinline int sys_sched_setaffinity_nt(int pid, - uint64_t bitsetsize, - const void *bitset) { + uint64_t bitsetsize, + const void *bitset) { int rc; uintptr_t mask; int64_t handle; diff --git a/libc/calls/setegid.c b/libc/calls/setegid.c index 315ea4cbd..6a5c89087 100644 --- a/libc/calls/setegid.c +++ b/libc/calls/setegid.c @@ -22,9 +22,6 @@ /** * Sets effective group ID. */ -int setegid(unsigned egid) { - int rc; - rc = setregid(-1, egid); - STRACE("%s(%u) → %d% m", "setegid", egid, rc); - return rc; +int setegid(uint32_t egid) { + return setregid(-1, egid); } diff --git a/libc/calls/seteuid.c b/libc/calls/seteuid.c index 996ea7d98..2361627e1 100644 --- a/libc/calls/seteuid.c +++ b/libc/calls/seteuid.c @@ -22,9 +22,6 @@ /** * Sets effective user ID. */ -int seteuid(unsigned euid) { - int rc; - rc = setreuid(-1, euid); - STRACE("%s(%u) → %d% m", "seteuid", euid, rc); - return rc; +int seteuid(uint32_t euid) { + return setregid(euid, -1); } diff --git a/libc/calls/setgid.c b/libc/calls/setgid.c index 76a5d2989..93431f3a4 100644 --- a/libc/calls/setgid.c +++ b/libc/calls/setgid.c @@ -21,12 +21,16 @@ #include "libc/calls/strace.internal.h" /** - * Sets effective group id of current process. + * Sets group id of current process. * @return 0 on success or -1 w/ errno */ int setgid(int gid) { int rc; - rc = sys_setgid(gid); - STRACE("%s(%d) → %d% m", "setgid", gid); + if (IsWindows() && gid == getgid()) { + rc = 0; + } else { + rc = sys_setgid(gid); + } + STRACE("setgid(%d) → %d% m", gid, rc); return rc; } diff --git a/libc/calls/setregid.c b/libc/calls/setregid.c new file mode 100644 index 000000000..e2ee4dc94 --- /dev/null +++ b/libc/calls/setregid.c @@ -0,0 +1,35 @@ +/*-*- 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" + +/** + * Sets real and/or effective group ids. + * + * @param rgid is real group id or -1 to leave it unchanged + * @param egid is effective group id or -1 to leave it unchanged + * @return 0 on success or -1 w/ errno + */ +int setregid(uint32_t rgid, uint32_t egid) { + int rc; + rc = sys_setregid(rgid, egid); + STRACE("setregid(%d, %d) → %d% m", rgid, egid, rc); + return rc; +} diff --git a/libc/calls/setresgid.c b/libc/calls/setresgid.c index b1af4e79d..846df7a93 100644 --- a/libc/calls/setresgid.c +++ b/libc/calls/setresgid.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Sets real, effective, and "saved" group ids. @@ -25,9 +26,17 @@ * @param real sets real group id or -1 to do nothing * @param effective sets effective group id or -1 to do nothing * @param saved sets saved group id or -1 to do nothing - * @see setregid(), getauxval(AT_SECURE) + * @see setresuid(), getauxval(AT_SECURE) + * @raise ENOSYS on Windows NT */ int setresgid(uint32_t real, uint32_t effective, uint32_t saved) { - if (saved == -1) return setregid(real, effective); - return sys_setresgid(real, effective, saved); + int rc; + if (saved != -1) { + rc = sys_setresgid(real, effective, saved); + } else { + // polyfill xnu and netbsd + rc = sys_setregid(real, effective); + } + STRACE("setresgid(%d, %d, %d) → %d% m", real, effective, saved, rc); + return rc; } diff --git a/libc/calls/setresuid.c b/libc/calls/setresuid.c index c4bc134de..a585dc91c 100644 --- a/libc/calls/setresuid.c +++ b/libc/calls/setresuid.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" /** * Sets real, effective, and "saved" user ids. @@ -25,9 +26,17 @@ * @param real sets real user id or -1 to do nothing * @param effective sets effective user id or -1 to do nothing * @param saved sets saved user id or -1 to do nothing - * @see setreuid(), getauxval(AT_SECURE) + * @see setresgid(), getauxval(AT_SECURE) + * @raise ENOSYS on Windows NT */ int setresuid(uint32_t real, uint32_t effective, uint32_t saved) { - if (saved == -1) return setreuid(real, effective); - return sys_setresuid(real, effective, saved); + int rc; + if (saved != -1) { + rc = sys_setresuid(real, effective, saved); + } else { + // polyfill xnu and netbsd + rc = sys_setreuid(real, effective); + } + STRACE("setresuid(%d, %d, %d) → %d% m", real, effective, saved, rc); + return rc; } diff --git a/libc/calls/setreuid.c b/libc/calls/setreuid.c new file mode 100644 index 000000000..e3ff17143 --- /dev/null +++ b/libc/calls/setreuid.c @@ -0,0 +1,35 @@ +/*-*- 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" + +/** + * Sets real and/or effective user ids. + * + * @param ruid is real user id or -1 to leave it unchanged + * @param euid is effective user id or -1 to leave it unchanged + * @return 0 on success or -1 w/ errno + */ +int setreuid(uint32_t ruid, uint32_t euid) { + int rc; + rc = sys_setreuid(ruid, euid); + STRACE("setreuid(%d, %d) → %d% m", ruid, euid, rc); + return rc; +} diff --git a/libc/calls/setuid.c b/libc/calls/setuid.c index edf316f04..f46735559 100644 --- a/libc/calls/setuid.c +++ b/libc/calls/setuid.c @@ -21,12 +21,16 @@ #include "libc/calls/strace.internal.h" /** - * Sets effective group id of current process. + * Sets user id of current process. * @return 0 on success or -1 w/ errno */ int setuid(int uid) { int rc; - rc = sys_setuid(uid); - STRACE("%s(%d) → %d% m", "setuid", uid); + if (IsWindows() && uid == getuid()) { + rc = 0; + } else { + rc = sys_setuid(uid); + } + STRACE("setuid(%d) → %d% m", uid, rc); return rc; } diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 40d3fabd9..2f58b1d6f 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -24,6 +24,7 @@ struct Signals { }; extern struct Signals __sig; // TODO(jart): Need TLS +extern long __sig_count; bool __sig_check(bool) hidden; bool __sig_handle(bool, int, int, ucontext_t *) hidden; diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index e48b5fc29..45626eff3 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -222,6 +222,7 @@ textwindows int __sig_add(int sig, int si_code) { if (1 <= sig && sig <= NSIG) { STRACE("enqueuing %G", sig); _spinlock(&__sig_lock); + ++__sig_count; if ((mem = __sig_alloc())) { mem->sig = sig; mem->si_code = si_code; diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 093b56f00..17ac1962d 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/bits.h" +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sigbits.h" @@ -34,6 +35,8 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/spinlock.h" #include "libc/limits.h" +#include "libc/log/backtrace.internal.h" +#include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" diff --git a/libc/calls/sigaddset.c b/libc/calls/sigaddset.c index e730d90b7..260b80304 100644 --- a/libc/calls/sigaddset.c +++ b/libc/calls/sigaddset.c @@ -22,13 +22,14 @@ /** * Adds signal to set. * - * @return true, false, or -1 w/ errno + * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe */ int sigaddset(sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - set->__bits[i >> 6] |= 1ull << (i & 63); + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); return 0; } else { return einval(); diff --git a/libc/calls/sigcount.c b/libc/calls/sigcount.c new file mode 100644 index 000000000..74986db40 --- /dev/null +++ b/libc/calls/sigcount.c @@ -0,0 +1,21 @@ +/*-*- 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/calls/sig.internal.h" + +long __sig_count; diff --git a/libc/calls/sigdelset.c b/libc/calls/sigdelset.c index 856573808..84ca3129b 100644 --- a/libc/calls/sigdelset.c +++ b/libc/calls/sigdelset.c @@ -23,12 +23,13 @@ * Removes signal from set. * * @return 0 on success, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe */ int sigdelset(sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - set->__bits[i >> 6] &= ~(1ull << (i & 63)); + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63)); return 0; } else { return einval(); diff --git a/libc/calls/sigismember.c b/libc/calls/sigismember.c index bbb57fe73..246ac55db 100644 --- a/libc/calls/sigismember.c +++ b/libc/calls/sigismember.c @@ -22,14 +22,15 @@ /** * Returns true if signal is member of set. * - * @return true, false, or -1 w/ errno + * @return 1 if set, 0 if not set, or -1 w/ errno + * @raises EINVAL if `1 ≤ sig ≤ NSIG` isn't the case * @asyncsignalsafe * @vforksafe */ int sigismember(const sigset_t *set, int sig) { - unsigned i = sig - 1; - if (i < sizeof(set->__bits) * 8) { - return (set->__bits[i >> 6] >> (i & 63)) & 1; + _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); + if (1 <= sig && sig <= NSIG) { + return !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))); } else { return einval(); } diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index aea83829a..509bfcd8f 100644 --- a/libc/calls/strace.internal.h +++ b/libc/calls/strace.internal.h @@ -5,10 +5,10 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" -#define _KERNTRACE 1 /* not configurable w/ flag yet */ -#define _POLLTRACE 1 /* not configurable w/ flag yet */ +#define _KERNTRACE 0 /* not configurable w/ flag yet */ +#define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ -#define _NTTRACE 1 /* not configurable w/ flag yet */ +#define _NTTRACE 0 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %5P %'18T " @@ -19,7 +19,7 @@ COSMOPOLITAN_C_START_ #define STRACE(FMT, ...) \ do { \ if (__strace > 0) { \ - __stracef(STRACE_PROLOGUE FMT "%n", ##__VA_ARGS__); \ + __stracef(STRACE_PROLOGUE FMT "\n", ##__VA_ARGS__); \ } \ } while (0) #else diff --git a/libc/calls/struct/stat.h b/libc/calls/struct/stat.h index fc39ec4a5..b744420cd 100644 --- a/libc/calls/struct/stat.h +++ b/libc/calls/struct/stat.h @@ -10,7 +10,7 @@ struct stat { /* cosmo abi */ uint32_t st_mode; /* 24: octal file mask thing */ uint32_t st_uid; /* 28: user id of owner */ uint32_t st_gid; /* group id of owning group */ - uint32_t st_flags; /* flags (bsd-only) */ + uint32_t st_flags; /* nt/xnu/bsd-only */ uint64_t st_rdev; /* id of device if a special file */ int64_t st_size; /* bytes in file */ int64_t st_blksize; /* preferred chunking for underlying filesystem */ @@ -19,7 +19,7 @@ struct stat { /* cosmo abi */ struct timespec st_mtim; /* modified time */ struct timespec st_ctim; /* complicated time */ struct timespec st_birthtim; - uint64_t st_gen; + uint64_t st_gen; /* xnu/bsd only */ }; #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/sync-nt.c b/libc/calls/sync-nt.c index 5188199f0..31dbc395b 100644 --- a/libc/calls/sync-nt.c +++ b/libc/calls/sync-nt.c @@ -29,7 +29,7 @@ /** * Flushes all open file handles and, if possible, all disk drives. */ -int sys_sync_nt(void) { +textwindows int sys_sync_nt(void) { unsigned i; int64_t volume; uint32_t drives; 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/calls/uname.c b/libc/calls/uname.c index b96686840..fa187db12 100644 --- a/libc/calls/uname.c +++ b/libc/calls/uname.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" @@ -23,6 +24,7 @@ #include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" +#include "libc/log/log.h" #include "libc/nt/enum/computernameformat.h" #include "libc/nt/struct/teb.h" #include "libc/runtime/runtime.h" diff --git a/libc/calls/vdsofunc.greg.c b/libc/calls/vdsofunc.greg.c new file mode 100644 index 000000000..843b45822 --- /dev/null +++ b/libc/calls/vdsofunc.greg.c @@ -0,0 +1,94 @@ +/*-*- 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/bits/bits.h" +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/elf/scalar.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/elf/struct/shdr.h" +#include "libc/elf/struct/sym.h" +#include "libc/log/libfatal.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +#define LAZY_RHEL7_RELOCATION 0xfffff + +#define GetStr(tab, rva) ((char *)(tab) + (rva)) +#define GetSection(e, s) ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset)) +#define GetShstrtab(e) GetSection(e, GetShdr(e, (e)->e_shstrndx)) +#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name) +#define GetPhdr(e, i) \ + ((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \ + (size_t)(e)->e_phentsize * (i))) +#define GetShdr(e, i) \ + ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \ + (size_t)(e)->e_shentsize * (i))) + +static char *GetDynamicStringTable(Elf64_Ehdr *e, size_t *n) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = 0; i < e->e_shnum; ++i) { + shdr = GetShdr(e, i); + name = GetSectionName(e, GetShdr(e, i)); + if (shdr->sh_type == SHT_STRTAB) { + name = GetSectionName(e, GetShdr(e, i)); + if (name && READ64LE(name) == READ64LE(".dynstr")) { + if (n) *n = shdr->sh_size; + return GetSection(e, shdr); + } + } + } + return 0; +} + +static Elf64_Sym *GetDynamicSymbolTable(Elf64_Ehdr *e, Elf64_Xword *n) { + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = e->e_shnum; i > 0; --i) { + shdr = GetShdr(e, i - 1); + if (shdr->sh_type == SHT_DYNSYM) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (n) *n = shdr->sh_size / shdr->sh_entsize; + return GetSection(e, shdr); + } + } + return 0; +} + +/** + * Returns Linux Kernel Virtual Dynamic Shared Object function address. + */ +void *__vdsofunc(const char *name) { + size_t m; + char *names; + Elf64_Ehdr *ehdr; + Elf64_Xword i, n; + Elf64_Sym *symtab, *sym; + if ((ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR)) && + (names = GetDynamicStringTable(ehdr, &m)) && + (symtab = GetDynamicSymbolTable(ehdr, &n))) { + for (i = 0; i < n; ++i) { + if (!__strcmp(names + symtab[i].st_name, name)) { + return (char *)ehdr + (symtab[i].st_value & LAZY_RHEL7_RELOCATION); + } + } + } + return 0; +} diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index c411018eb..14e00a01b 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -95,7 +95,11 @@ static textwindows int sys_wait4_nt_impl(int pid, int *opt_out_wstatus, return 0; } } else { - i = WaitForMultipleObjects(count, handles, false, -1); + i = WaitForMultipleObjects(count, handles, false, + __SIG_POLLING_INTERVAL_MS); + if (i == kNtWaitTimeout) { + continue; + } } if (i == kNtWaitFailed) { STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError()); diff --git a/libc/calls/weirdtypes.h b/libc/calls/weirdtypes.h index 4b0cb8a65..de7973ae1 100644 --- a/libc/calls/weirdtypes.h +++ b/libc/calls/weirdtypes.h @@ -48,6 +48,26 @@ typedef __UINT_FAST32_TYPE__ uint_fast32_t; typedef __INT_FAST64_TYPE__ int_fast64_t; typedef __UINT_FAST64_TYPE__ uint_fast64_t; +#define TIME_T_MAX __INT64_MAX__ +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ + +#define TIME_T_MIN (-TIME_T_MAX - 1) +#define UINT_FAST64_MIN (-UINT_FAST64_MAX - 1) +#define UINT_FAST8_MIN (-UINT_FAST8_MAX - 1) +#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) +#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) +#define UINT_FAST32_MIN (-UINT_FAST32_MAX - 1) +#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) +#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) +#define UINT_FAST16_MIN (-UINT_FAST16_MAX - 1) + #define atomic_bool _Atomic(_Bool) #define atomic_bool32 atomic_int_fast32_t #define atomic_char _Atomic(char) diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 296fb5353..08bc6df0c 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -79,7 +79,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { } else { kprintf(STRACE_PROLOGUE "writev(%d, ", fd); __strace_iov(iov, iovlen, rc != -1 ? rc : 0); - kprintf(", %d) → %'ld% m%n", iovlen, rc); + kprintf(", %d) → %'ld% m\n", iovlen, rc); } } #endif diff --git a/libc/complex.h b/libc/complex.h index 51f7dd322..ebde8e5b6 100644 --- a/libc/complex.h +++ b/libc/complex.h @@ -82,6 +82,39 @@ complex long double clogl(complex long double); complex long double conjl(complex long double); complex long double cpowl(complex long double, complex long double); +#ifndef __cplusplus +#define __CIMAG(x, t) \ + (+(union { \ + _Complex t __z; \ + t __xy[2]; \ + }){(_Complex t)(x)} \ + .__xy[1]) +#define creal(x) ((double)(x)) +#define crealf(x) ((float)(x)) +#define creall(x) ((long double)(x)) +#define cimag(x) __CIMAG(x, double) +#define cimagf(x) __CIMAG(x, float) +#define cimagl(x) __CIMAG(x, long double) +#endif + +#ifdef __GNUC__ +#define _Complex_I (__extension__(0.0f + 1.0fi)) +#else +#define _Complex_I (0.0f + 1.0fi) +#endif + +#ifdef _Imaginary_I +#define __CMPLX(x, y, t) ((t)(x) + _Imaginary_I * (t)(y)) +#elif defined(__clang__) +#define __CMPLX(x, y, t) (+(_Complex t){(t)(x), (t)(y)}) +#else +#define __CMPLX(x, y, t) (__builtin_complex((t)(x), (t)(y))) +#endif + +#define CMPLX(x, y) __CMPLX(x, y, double) +#define CMPLXF(x, y) __CMPLX(x, y, float) +#define CMPLXL(x, y) __CMPLX(x, y, long double) + #endif /* C11 */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/dns/gethoststxt.c b/libc/dns/gethoststxt.c index 9a8b8a012..16598bdde 100644 --- a/libc/dns/gethoststxt.c +++ b/libc/dns/gethoststxt.c @@ -37,7 +37,7 @@ static struct HostsTxtInitialStaticMemory { } g_hoststxt_init; static textwindows dontinline char *GetNtHostsTxtPath(char *pathbuf, - uint32_t size) { + uint32_t size) { const char *const kWinHostsPath = "\\drivers\\etc\\hosts"; uint32_t len = GetSystemDirectoryA(&pathbuf[0], size); if (len && len + strlen(kWinHostsPath) + 1 < size) { diff --git a/libc/dns/lookupprotobyname.c b/libc/dns/lookupprotobyname.c index 921cf371d..f319d2ed6 100644 --- a/libc/dns/lookupprotobyname.c +++ b/libc/dns/lookupprotobyname.c @@ -48,10 +48,10 @@ int LookupProtoByName(const char *protoname, char *buf, size_t bufsize, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; const char *path; size_t linesize; int found, result; + char pathbuf[PATH_MAX]; char *name, *number, *alias, *comment, *tok; if (!(path = filepath)) { path = "/etc/protocols"; diff --git a/libc/dns/lookupprotobynumber.c b/libc/dns/lookupprotobynumber.c index 6d455d4e8..e86e1c4da 100644 --- a/libc/dns/lookupprotobynumber.c +++ b/libc/dns/lookupprotobynumber.c @@ -52,10 +52,10 @@ int LookupProtoByNumber(const int protonum, char *buf, size_t bufsize, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; - const char *path; - size_t linesize; int found; + size_t linesize; + const char *path; + char pathbuf[PATH_MAX]; char *name, *number, *comment, *tok; if (!(path = filepath)) { path = "/etc/protocols"; diff --git a/libc/dns/lookupservicesbyname.c b/libc/dns/lookupservicesbyname.c index ffefbf45c..14458531e 100644 --- a/libc/dns/lookupservicesbyname.c +++ b/libc/dns/lookupservicesbyname.c @@ -53,10 +53,10 @@ int LookupServicesByName(const char *servname, char *servproto, const char *filepath) { FILE *f; char *line; - char pathbuf[PATH_MAX]; const char *path; size_t linesize; int found, result; + char pathbuf[PATH_MAX]; char *name, *port, *proto, *alias, *comment, *tok; if (!(path = filepath)) { path = "/etc/services"; diff --git a/libc/elf/elf.h b/libc/elf/elf.h index ed9abee8f..9df8827ac 100644 --- a/libc/elf/elf.h +++ b/libc/elf/elf.h @@ -26,6 +26,8 @@ void GetElfVirtualAddressRange(const Elf64_Ehdr *, size_t, intptr_t *, intptr_t *); char *GetElfString(const Elf64_Ehdr *, size_t, const char *, Elf64_Word); const char *GetElfSectionName(const Elf64_Ehdr *, size_t, Elf64_Shdr *); +Elf64_Sym *GetElfDynSymbolTable(const Elf64_Ehdr *, size_t, Elf64_Xword *); +char *GetElfDynStringTable(const Elf64_Ehdr *, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/time/asctime_r.c b/libc/elf/getelfdynstringtable.c similarity index 73% rename from libc/time/asctime_r.c rename to libc/elf/getelfdynstringtable.c index fda926f26..6ed29f0a9 100644 --- a/libc/time/asctime_r.c +++ b/libc/elf/getelfdynstringtable.c @@ -16,25 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" -#include "libc/time/struct/tm.h" -#include "libc/time/time.h" +#include "libc/elf/def.h" +#include "libc/elf/elf.h" +#include "libc/str/str.h" -static unsigned clip(unsigned index, unsigned count) { - return index < count ? index : 0; -} - -/** - * Converts date time to string. - * - * @param buf needs to have 64 bytes - * @return pointer to buf - * @see asctime_r for reentrant version - */ -char *asctime_r(const struct tm *date, char buf[hasatleast 64]) { - (snprintf)(buf, 64, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", - kWeekdayNameShort[clip(date->tm_wday, 7)], - kMonthNameShort[clip(date->tm_mon, 12)], date->tm_mday, - date->tm_hour, date->tm_min, date->tm_sec, 1900 + date->tm_year); - return buf; +char *GetElfDynStringTable(const Elf64_Ehdr *elf, size_t mapsize) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + if (elf->e_shentsize) { + for (i = 0; i < elf->e_shnum; ++i) { + shdr = GetElfSectionHeaderAddress(elf, mapsize, i); + if (shdr->sh_type == SHT_STRTAB) { + name = GetElfSectionName(elf, mapsize, + GetElfSectionHeaderAddress(elf, mapsize, i)); + if (name && !strcmp(name, ".dynstr")) { + return GetElfSectionAddress(elf, mapsize, shdr); + } + } + } + } + return NULL; } diff --git a/libc/elf/getelfdynsymboltable.c b/libc/elf/getelfdynsymboltable.c new file mode 100644 index 000000000..09256fda6 --- /dev/null +++ b/libc/elf/getelfdynsymboltable.c @@ -0,0 +1,37 @@ +/*-*- 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 2020 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/elf/def.h" +#include "libc/elf/elf.h" + +Elf64_Sym *GetElfDynSymbolTable(const Elf64_Ehdr *elf, size_t mapsize, + Elf64_Xword *out_count) { + Elf64_Half i; + Elf64_Shdr *shdr; + if (elf->e_shentsize) { + for (i = elf->e_shnum; i > 0; --i) { + shdr = GetElfSectionHeaderAddress(elf, mapsize, i - 1); + if (shdr->sh_type == SHT_DYNSYM) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (out_count) *out_count = shdr->sh_size / shdr->sh_entsize; + return GetElfSectionAddress(elf, mapsize, shdr); + } + } + } + return NULL; +} diff --git a/libc/fmt/abs.c b/libc/fmt/abs.c index ab4b6b6a8..1baff0057 100644 --- a/libc/fmt/abs.c +++ b/libc/fmt/abs.c @@ -18,6 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -int(abs)(int x) { +/** + * Returns absolute value of 32-bit integer. + * @note `labs(LONG_MIN)` returns `LONG_MIN` unless `-ftrapv` + * @note consider ABS() to avoid narrowing + */ +int abs(int x) { return 0 < x ? x : -x; } diff --git a/libc/fmt/fmt.c b/libc/fmt/fmt.c index 488a10cfb..0166660d7 100644 --- a/libc/fmt/fmt.c +++ b/libc/fmt/fmt.c @@ -351,7 +351,7 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { // undocumented %r specifier // used for good carriage return // helps integrate loggers with repls - if (!__replmode) { + if (!__replstderr) { break; } else { p = "\r\e[K"; @@ -368,9 +368,6 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { } break; case 'n': - // nonstandard %n specifier - // used to print newlines that work in raw terminal modes - if (__nomultics) __FMT_PUT('\r'); __FMT_PUT('\n'); break; case 'F': diff --git a/libc/fmt/fmt.h b/libc/fmt/fmt.h index 3bc5b89c2..199b004a6 100644 --- a/libc/fmt/fmt.h +++ b/libc/fmt/fmt.h @@ -26,10 +26,6 @@ int sscanf(const char *, const char *, ...) scanfesque(2); int vsscanf(const char *, const char *, va_list); int vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *, va_list); -int strerror_r(int, char *, size_t) dontthrow nocallback; -int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; -const char *strerror_short(int) nosideeffect; -const char *strerror_long(int) nosideeffect; int __fmt(void *, void *, const char *, va_list) hidden; char *itoa(int, char *, int) compatfn; char *fcvt(double, int, int *, int *); diff --git a/libc/fmt/formatbinary64.c b/libc/fmt/formatbinary64.c new file mode 100644 index 000000000..5e4e1b011 --- /dev/null +++ b/libc/fmt/formatbinary64.c @@ -0,0 +1,63 @@ +/*-*- 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 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/itoa.h" +#include "libc/nexgen32e/bsr.h" + +static inline int PickGoodWidth(unsigned x) { + if (x < 16) { + if (x < 2) return 0; + if (x < 8) return 7; + return 15; + } else { + if (x < 32) return 31; + return 63; + } +} + +/** + * Converts unsigned 64-bit integer to binary string. + * + * @param p needs at least 67 bytes + * @param z is 0 for DIGITS, 1 for 0bDIGITS, 2 for 0bDIGITS if ≠0 + * @return pointer to nul byte + */ +char *FormatBinary64(char p[hasatleast 67], uint64_t x, char z) { + int i; + uint64_t b; + if (x) { + if (z) { + *p++ = '0'; + *p++ = 'b'; + } + i = PickGoodWidth(bsrl(x)); + do { + b = 1; + b <<= i; + *p++ = '0' + !!(x & b); + } while (i--); + } else { + if (z == 1) { + *p++ = '0'; + *p++ = 'b'; + } + *p++ = '0'; + } + *p = 0; + return p; +} diff --git a/libc/fmt/formatflex64.c b/libc/fmt/formatflex64.c new file mode 100644 index 000000000..2c8b7f106 --- /dev/null +++ b/libc/fmt/formatflex64.c @@ -0,0 +1,63 @@ +/*-*- 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/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/math.h" + +static inline int CountZeroesHex(uint64_t x) { + int n; + for (n = 0; x;) { + if (!(x & 15)) { + ++n; + } + x >>= 4; + } + return n; +} + +static inline int CountZeroesDec(int64_t s) { + int n, r; + uint64_t x; + x = s >= 0 ? s : -(uint64_t)s; + for (n = 0; x;) { + r = x % 10; + x = x / 10; + if (!r) ++n; + } + return n; +} + +/** + * Formats integer using decimal or hexadecimal. + * + * We choose hex vs. decimal based on whichever one has the most zeroes. + * We only bother counting zeroes for numbers outside -256 ≤ 𝑥 ≤ 256. + */ +char *FormatFlex64(char p[hasatleast 24], int64_t x, char z) { + int zhex, zdec; + if (-256 <= x && x <= 256) goto UseDecimal; + zhex = CountZeroesHex(x) * 16; + zdec = CountZeroesDec(x) * 10; + if (zdec >= zhex) { + UseDecimal: + return FormatInt64(p, x); + } else { + return FormatHex64(p, x, z); + } +} diff --git a/libc/fmt/formathex64.c b/libc/fmt/formathex64.c new file mode 100644 index 000000000..528150e4b --- /dev/null +++ b/libc/fmt/formathex64.c @@ -0,0 +1,64 @@ +/*-*- 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 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/itoa.h" +#include "libc/macros.internal.h" +#include "libc/nexgen32e/bsr.h" + +static inline int PickGoodWidth(unsigned x, char z) { + if (z) { + if (x < 16) { + if (x < 8) return 8; + return 16; + } else { + if (x < 32) return 32; + return 64; + } + } else { + return ROUNDUP(x + 1, 4); + } +} + +/** + * Converts unsigned 64-bit integer to hex string. + * + * @param p needs at least 19 bytes + * @param z is 0 for DIGITS, 1 for 0bDIGITS, 2 for 0bDIGITS if ≠0 + * @return pointer to nul byte + */ +char *FormatHex64(char p[hasatleast 19], uint64_t x, char z) { + int i; + if (x) { + if (z) { + *p++ = '0'; + *p++ = 'x'; + } + i = PickGoodWidth(bsrl(x), z); + do { + *p++ = "0123456789abcdef"[(x >> (i -= 4)) & 15]; + } while (i); + } else { + if (z == 1) { + *p++ = '0'; + *p++ = 'x'; + } + *p++ = '0'; + } + *p = 0; + return p; +} diff --git a/libc/fmt/formatoctal64.c b/libc/fmt/formatoctal64.c index c500467ab..c82171967 100644 --- a/libc/fmt/formatoctal64.c +++ b/libc/fmt/formatoctal64.c @@ -21,7 +21,7 @@ /** * Converts unsigned 64-bit integer to octal string. * - * @param p needs at least 12 bytes + * @param p needs at least 24 bytes * @param z ensures it starts with zero * @return pointer to nul byte */ diff --git a/libc/fmt/itoa.h b/libc/fmt/itoa.h index f123dc271..a492328d1 100644 --- a/libc/fmt/itoa.h +++ b/libc/fmt/itoa.h @@ -15,6 +15,9 @@ char *FormatInt64Thousands(char[hasatleast 27], int64_t); char *FormatUint64Thousands(char[hasatleast 27], uint64_t); char *FormatOctal32(char[hasatleast 13], uint32_t, bool); char *FormatOctal64(char[hasatleast 24], uint64_t, bool); +char *FormatBinary64(char[hasatleast 67], uint64_t, char); +char *FormatHex64(char[hasatleast 19], uint64_t, char); +char *FormatFlex64(char[hasatleast 24], int64_t, char); size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]); size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t); size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]); diff --git a/libc/fmt/kerrornameslong.S b/libc/fmt/kerrnodocs.S similarity index 97% rename from libc/fmt/kerrornameslong.S rename to libc/fmt/kerrnodocs.S index 127638a41..d03750d07 100644 --- a/libc/fmt/kerrornameslong.S +++ b/libc/fmt/kerrnodocs.S @@ -16,11 +16,12 @@ │ 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 - kErrorNamesLong - .long 1f - kErrorNamesLong + .long \e - kErrnoDocs + .long 1f - kErrnoDocs .rodata.str1.1 1: .asciz "\s" .previous @@ -29,7 +30,7 @@ .section .rodata .align 4 .underrun -kErrorNamesLong: +kErrnoDocs: .e EINVAL,"Invalid argument" .e ENOSYS,"Function not implemented" .e EPERM,"Operation not permitted" @@ -115,6 +116,6 @@ kErrorNamesLong: .e ENOTRECOVERABLE,"State not recoverable" .e ENONET,"Machine is not on the network" .e ERESTART,"Interrupted system call should be restarted" - .long 0 - .endobj kErrorNamesLong,globl,hidden + .long MAGNUM_TERMINATOR + .endobj kErrnoDocs,globl,hidden .overrun diff --git a/libc/fmt/kerrornames.S b/libc/fmt/kerrnonames.S similarity index 94% rename from libc/fmt/kerrornames.S rename to libc/fmt/kerrnonames.S index 6755891c0..85394e0e2 100644 --- a/libc/fmt/kerrornames.S +++ b/libc/fmt/kerrnonames.S @@ -16,20 +16,21 @@ │ 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 - .long \e - kErrorNames - .long 1f - kErrorNames + .long \e - kErrnoNames + .long 1f - kErrnoNames .rodata.str1.1 1: .string "\e" .previous .endm .section .rodata - .align 4 + .align 4 .underrun -kErrorNames: +kErrnoNames: .e EINVAL .e ENOSYS .e EPERM @@ -116,6 +117,6 @@ kErrorNames: .e ENONET .e ERESTART .e ENODATA - .long 0 - .endobj kErrorNames,globl,hidden + .long MAGNUM_TERMINATOR + .endobj kErrnoNames,globl,hidden .overrun diff --git a/libc/fmt/kerrornames.internal.h b/libc/fmt/kerrornames.internal.h deleted file mode 100644 index b8a2636c1..000000000 --- a/libc/fmt/kerrornames.internal.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct ErrorName { - int x, s; -}; - -extern const struct ErrorName kStrSignal[]; -extern const struct ErrorName kErrorNames[]; -extern const struct ErrorName kErrorNamesLong[]; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_FMT_KERRORNAMES_INTERNAL_H_ */ diff --git a/libc/fmt/labs.c b/libc/fmt/labs.c index e3a1d3916..c47c0a361 100644 --- a/libc/fmt/labs.c +++ b/libc/fmt/labs.c @@ -17,8 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/macros.internal.h" -long(labs)(long x) { - return ABS(x); +/** + * Returns absolute value of long integer. + * @note `labs(LONG_MIN)` returns `LONG_MIN` unless `-ftrapv` + * @note consider ABS() to avoid narrowing + */ +long labs(long x) { + return 0 < x ? x : -x; } diff --git a/libc/fmt/llabs.c b/libc/fmt/llabs.c index 5ded9754c..fa1670b13 100644 --- a/libc/fmt/llabs.c +++ b/libc/fmt/llabs.c @@ -17,8 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/macros.internal.h" -long long(llabs)(long long x) { - return ABS(x); +/** + * Returns absolute value of long long integer. + * @note `llabs(LONG_LONG_MIN)` returns `LONG_LONG_MIN` unless `-ftrapv` + * @note consider ABS() to avoid narrowing + */ +long long llabs(long long x) { + return 0 < x ? x : -x; } diff --git a/libc/fmt/magnumstrs.internal.h b/libc/fmt/magnumstrs.internal.h new file mode 100644 index 000000000..af425fc93 --- /dev/null +++ b/libc/fmt/magnumstrs.internal.h @@ -0,0 +1,36 @@ +#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; +}; + +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[]; + +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) */ +#endif /* COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ */ diff --git a/libc/fmt/strerror_long.greg.c b/libc/fmt/strerdoc.greg.c similarity index 82% rename from libc/fmt/strerror_long.greg.c rename to libc/fmt/strerdoc.greg.c index c5081970a..b8e89b384 100644 --- a/libc/fmt/strerror_long.greg.c +++ b/libc/fmt/strerdoc.greg.c @@ -17,23 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" /** * Converts errno value to descriptive sentence. * @return non-null rodata string or null if not found */ -privileged const char *strerror_long(int x) { - /* kprintf() weakly depends on this function */ - int i; +char *strerdoc(int x) { if (x) { - for (i = 0; kErrorNamesLong[i].x; ++i) { - if (x == - *(const long *)((uintptr_t)kErrorNamesLong + kErrorNamesLong[i].x)) { - return (const char *)((uintptr_t)kErrorNamesLong + - kErrorNamesLong[i].s); - } - } + return GetMagnumStr(kErrnoDocs, x); + } else { + return 0; } - return 0; } diff --git a/libc/fmt/strerror_short.greg.c b/libc/fmt/strerrno.greg.c similarity index 83% rename from libc/fmt/strerror_short.greg.c rename to libc/fmt/strerrno.greg.c index 5075af526..145727831 100644 --- a/libc/fmt/strerror_short.greg.c +++ b/libc/fmt/strerrno.greg.c @@ -16,22 +16,17 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/fmt.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/str/str.h" /** * Converts errno value to symbolic name. * @return non-null rodata string or null if not found */ -privileged const char *strerror_short(int x) { - /* kprintf() weakly depends on this function */ - int i; +char *strerrno(int x) { if (x) { - for (i = 0; kErrorNames[i].x; ++i) { - if (x == *(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x)) { - return (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s); - } - } + return GetMagnumStr(kErrnoNames, x); + } else { + return 0; } - return 0; } diff --git a/libc/fmt/strerror.c b/libc/fmt/strerror.c index 29f9f23fe..8cfefe61b 100644 --- a/libc/fmt/strerror.c +++ b/libc/fmt/strerror.c @@ -23,9 +23,9 @@ * Converts errno value to string non-reentrantly. * @see strerror_r() */ -noasan char *strerror(int err) { +char *strerror(int err) { if (IsTiny()) { - return firstnonnull(strerror_short(err), "EUNKNOWN"); + return firstnonnull(strerrno(err), "EUNKNOWN"); } else { _Alignas(1) static char buf[512]; strerror_r(err, buf, sizeof(buf)); diff --git a/libc/fmt/strerror_r.greg.c b/libc/fmt/strerror_r.greg.c index 22365785b..bec848b21 100644 --- a/libc/fmt/strerror_r.greg.c +++ b/libc/fmt/strerror_r.greg.c @@ -25,6 +25,6 @@ * @param err is error number or zero if unknown * @return 0 on success, or error code */ -privileged int strerror_r(int err, char *buf, size_t size) { +int strerror_r(int err, char *buf, size_t size) { return strerror_wr(err, GetLastError(), buf, size); } diff --git a/libc/fmt/strerror_wr.greg.c b/libc/fmt/strerror_wr.greg.c index b4c795038..8fad73863 100644 --- a/libc/fmt/strerror_wr.greg.c +++ b/libc/fmt/strerror_wr.greg.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#define ShouldUseMsabiAttribute() 1 #include "libc/bits/safemacros.internal.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" @@ -32,30 +31,30 @@ * @param err is error number or zero if unknown * @return 0 on success, or error code */ -privileged int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { +int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) { /* kprintf() weakly depends on this function */ int c, n; char16_t winmsg[256]; const char *sym, *msg; - sym = firstnonnull(strerror_short(err), "EUNKNOWN"); - msg = firstnonnull(strerror_long(err), "No error information"); + sym = firstnonnull(strerrno(err), "EUNKNOWN"); + msg = firstnonnull(strerdoc(err), "No error information"); if (IsTiny()) { if (!sym) sym = "EUNKNOWN"; for (; (c = *sym++); --size) if (size > 1) *buf++ = c; if (size) *buf = 0; - } else if (!IsWindows()) { - ksnprintf(buf, size, "%s[%d][%s]", sym, err, msg); + } else if (!IsWindows() || err == winerr || !winerr) { + ksnprintf(buf, size, "%s/%d/%s", sym, err, msg); } else { - if ((n = __imp_FormatMessageW( + if ((n = FormatMessage( kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0, winerr, MAKELANGID(kNtLangNeutral, kNtSublangDefault), winmsg, ARRAYLEN(winmsg), 0))) { while ((n && winmsg[n - 1] <= ' ') || winmsg[n - 1] == '.') --n; - ksnprintf(buf, size, "%s[%d][%s][%.*hs][%d]", sym, err, msg, n, winmsg, - winerr); + ksnprintf(buf, size, "%s/%d/%s/%d/%.*hs", sym, err, msg, winerr, n, + winmsg); } else { - ksnprintf(buf, size, "%s[%d][%s][%d]", sym, err, msg, winerr); + ksnprintf(buf, size, "%s/%d/%s/%d", sym, err, msg, winerr); } } return 0; diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 489d5a7f4..4cf6d3d06 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -70,13 +70,13 @@ #define BUFSIZ 0x1000 /* best stdio default */ #define CACHELINE 0x40 /* nexgen32e */ #define CHAR_BIT 8 /* b/c von neumann */ -#define ARG_MAX 0x8000 /* b/c windows */ -#define PATH_MAX 248 /* b/c win32 apis limit ~248..260 */ +#define ARG_MAX 0xfffe /* for argv and envp; see CreateProcess (32767*2) */ +#define PATH_MAX 1024 /* b/c _XOPEN_PATH_MAX */ #define NAME_MAX 63 /* b/c dns */ -#define CHILD_MAX 25 /* only if malloc isn't linked */ +#define CHILD_MAX 16 /* only if malloc isn't linked */ #define OPEN_MAX 16 /* only if malloc isn't linked */ #define ATEXIT_MAX 32 /* only if malloc isn't linked */ -#define NSIG 128 /* it's complicated */ +#define NSIG 128 /* b/c freebsd */ #if defined(__LP64__) && !defined(__INT64_TYPE__) #include "libc/integral/lp64.inc" diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index aa5a86468..582a59dee 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -59,7 +59,7 @@ STATIC_YOINK("_init_asan"); #define ASAN_MORGUE_ITEMS 512 #define ASAN_MORGUE_THRESHOLD 65536 // morgue memory O(ITEMS*THRESHOLD) -#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin +#define ASAN_TRACE_ITEMS 16 // backtrace limit on malloc origin /** * @fileoverview Cosmopolitan Address Sanitizer Runtime. @@ -102,7 +102,7 @@ STATIC_YOINK("_init_asan"); #define REQUIRE(FUNC) \ do { \ if (!weaken(FUNC)) { \ - kprintf("error: asan needs %s%n", #FUNC); \ + kprintf("error: asan needs %s\n", #FUNC); \ __asan_die()(); \ __asan_unreachable(); \ } \ @@ -179,8 +179,7 @@ static uint64_t __asan_roundup2pow(uint64_t x) { static char *__asan_utf8cpy(char *p, unsigned c) { uint64_t z; z = tpenc(c); - do - *p++ = z; + do *p++ = z; while ((z >>= 8)); return p; } @@ -322,9 +321,9 @@ static char *__asan_hexcpy(char *p, uint64_t x, uint8_t k) { } static void __asan_exit(void) { - kprintf("your asan runtime needs%n" - "\tSTATIC_YOINK(\"__die\");%n" - "in order to show you backtraces%n"); + kprintf("your asan runtime needs\n" + "\tSTATIC_YOINK(\"__die\");\n" + "in order to show you backtraces\n"); __restorewintty(); _Exit(99); } @@ -373,6 +372,7 @@ void __asan_unpoison(long p, long n) { } static bool __asan_is_mapped(int x) { + // xxx: we can't lock because no reentrant locks yet int i; struct MemoryIntervals *m; m = weaken(_mmi); @@ -609,7 +609,7 @@ const char *__asan_describe_access_poison(signed char kind) { dontdiscard static __asan_die_f *__asan_report_invalid_pointer( const void *addr) { - kprintf("%n\e[J\e[1;31masan error\e[0m: this corruption at %p shadow %p%n", + kprintf("\n\e[J\e[1;31masan error\e[0m: this corruption at %p shadow %p\n", addr, SHADOW(addr)); return __asan_die(); } @@ -629,7 +629,6 @@ static char *__asan_format_section(char *p, const void *p1, const void *p2, if (a <= (intptr_t)addr && (intptr_t)addr <= b) { p = __stpcpy(p, " ←address"); } - if (__nomultics) *p++ = '\r'; *p++ = '\n'; } return p; @@ -638,7 +637,7 @@ static char *__asan_format_section(char *p, const void *p1, const void *p2, static void __asan_report_memory_origin_image(intptr_t a, int z) { unsigned l, m, r, n, k; struct SymbolTable *st; - kprintf("%nthe memory belongs to image symbols%n"); + kprintf("\nthe memory belongs to image symbols\n"); if (weaken(GetSymbolTable)) { if ((st = weaken(GetSymbolTable)())) { l = 0; @@ -656,7 +655,7 @@ static void __asan_report_memory_origin_image(intptr_t a, int z) { if ((st->symbols[l].x <= k && k <= st->symbols[l].y) || (st->symbols[l].x <= k + z && k + z <= st->symbols[l].y) || (k < st->symbols[l].x && st->symbols[l].y < k + z)) { - kprintf("\t%s [%#x,%#x] size %'d%n", st->name_base + st->names[l], + kprintf("\t%s [%#x,%#x] size %'d\n", st->name_base + st->names[l], st->addr_base + st->symbols[l].x, st->addr_base + st->symbols[l].y, st->symbols[l].y - st->symbols[l].x + 1); @@ -665,10 +664,10 @@ static void __asan_report_memory_origin_image(intptr_t a, int z) { } } } else { - kprintf("\tunknown please supply .com.dbg symbols or set COMDBG%n"); + kprintf("\tunknown please supply .com.dbg symbols or set COMDBG\n"); } } else { - kprintf("\tunknown please STATIC_YOINK(\"GetSymbolTable\");%n"); + kprintf("\tunknown please STATIC_YOINK(\"GetSymbolTable\");\n"); } } @@ -686,13 +685,13 @@ static noasan void OnMemory(void *x, void *y, size_t n, void *a) { static void __asan_report_memory_origin_heap(const unsigned char *a, int z) { struct ReportOriginHeap t; - kprintf("%nthe memory was allocated by%n"); + kprintf("\nthe memory was allocated by\n"); if (weaken(malloc_inspect_all)) { t.a = a; t.z = z; weaken(malloc_inspect_all)(OnMemory, &t); } else { - kprintf("\tunknown please STATIC_YOINK(\"malloc_inspect_all\");%n"); + kprintf("\tunknown please STATIC_YOINK(\"malloc_inspect_all\");\n"); } } @@ -737,7 +736,7 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, struct MemoryIntervals *m; ++g_ftrace; p = __fatalbuf; - kprintf("%n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p%n%s%n", + kprintf("\n\e[J\e[1;31masan error\e[0m: %s %d-byte %s at %p shadow %p\n%s\n", __asan_describe_access_poison(kind), size, message, addr, SHADOW(addr), __argv[0]); if (0 < size && size < 80) { @@ -753,7 +752,6 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, *p++ = ' '; } } - if (__nomultics) *p++ = '\r'; *p++ = '\n'; for (c = i = 0; i < 80; ++i) { if (!(t = __asan_check(base + i, 1).kind)) { @@ -771,7 +769,6 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, } } p = __stpcpy(p, "\e[39m"); - if (__nomultics) *p++ = '\r'; *p++ = '\n'; for (i = 0; (intptr_t)(base + i) & 7; ++i) *p++ = ' '; for (; i + 8 <= 80; i += 8) { @@ -788,19 +785,18 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, } } for (; i < 80; ++i) *p++ = ' '; - if (__nomultics) *p++ = '\r'; *p++ = '\n'; for (i = 0; i < 80; ++i) { p = __asan_utf8cpy(p, __asan_exists(base + i) ? kCp437[((unsigned char *)base)[i]] : L'⋅'); } - if (__nomultics) *p++ = '\r'; *p++ = '\n'; } p = __asan_format_section(p, _base, _etext, ".text", addr); p = __asan_format_section(p, _etext, _edata, ".data", addr); p = __asan_format_section(p, _end, _edata, ".bss", addr); + // xxx: we can't lock because no reentrant locks yet for (m = weaken(_mmi), i = 0; i < m->i; ++i) { x = m->p[i].x; y = m->p[i].y; @@ -809,13 +805,12 @@ dontdiscard static __asan_die_f *__asan_report(const void *addr, int size, if (x <= z && z <= y) p = __stpcpy(p, " ←address"); z = (((intptr_t)addr >> 3) + 0x7fff8000) >> 16; if (x <= z && z <= y) p = __stpcpy(p, " ←shadow"); - if (__nomultics) *p++ = '\r'; *p++ = '\n'; } *p = 0; kprintf("%s", __fatalbuf); __asan_report_memory_origin(addr, size, kind); - kprintf("%nthe crash was caused by%n"); + kprintf("\nthe crash was caused by\n"); --g_ftrace; return __asan_die(); } @@ -924,8 +919,7 @@ static void __asan_trace(struct AsanTrace *bt, const struct StackFrame *bp) { if (!__asan_checka(SHADOW(bp), sizeof(*bp) >> 3).kind) { addr = bp->addr; if (addr == weakaddr("__gc") && weakaddr("__gc")) { - do - --gi; + do --gi; while ((addr = garbage->p[gi].ret) == weakaddr("__gc")); } bt->p[i] = addr; @@ -1018,12 +1012,12 @@ int __asan_print_trace(void *p) { kprintf(" bad cookie"); return -1; } - kprintf("%n%p %,lu bytes [asan]", (char *)p, n); + kprintf("\n%p %,lu bytes [asan]", (char *)p, n); if (!__asan_is_mapped((((intptr_t)p >> 3) + 0x7fff8000) >> 16)) { kprintf(" (shadow not mapped?!)"); } for (i = 0; i < ARRAYLEN(e->bt.p) && e->bt.p[i]; ++i) { - kprintf("%n%*lx %s", 12, e->bt.p[i], + kprintf("\n%*lx %s", 12, e->bt.p[i], weaken(__get_symbol_by_addr) ? weaken(__get_symbol_by_addr)(e->bt.p[i]) : "please STATIC_YOINK(\"__get_symbol_by_addr\")"); @@ -1203,7 +1197,7 @@ void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) { struct AsanTrace tr; __asan_rawtrace(&tr, __builtin_frame_address(0)); kprintf( - "WARNING: ASAN error during %s bad %d byte %s at %x bt %x %x %x %x %x%n", + "WARNING: ASAN error during %s bad %d byte %s at %x bt %x %x %x %x %x\n", s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3], tr.p[4], tr.p[5]); } @@ -1281,6 +1275,7 @@ void __asan_install_malloc_hooks(void) { } void __asan_map_shadow(uintptr_t p, size_t n) { + // assume _mmi.lock is held void *addr; int i, a, b; size_t size; @@ -1311,7 +1306,7 @@ void __asan_map_shadow(uintptr_t p, size_t n) { m, a, a + i - 1, sm.maphandle, PROT_READ | PROT_WRITE, MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED, false, false, 0, size) == -1) { - kprintf("error: could not map asan shadow memory%n"); + kprintf("error: could not map asan shadow memory\n"); __asan_die()(); __asan_unreachable(); } diff --git a/libc/intrin/assertfail.c b/libc/intrin/assertfail.c index ba38f3dc5..1be6fe260 100644 --- a/libc/intrin/assertfail.c +++ b/libc/intrin/assertfail.c @@ -35,12 +35,12 @@ relegated wontreturn void __assert_fail(const char *expr, const char *file, static bool noreentry; __strace = 0; g_ftrace = 0; - kprintf("%s:%d: assert(%s) failed%n", file, line, expr); + kprintf("%s:%d: assert(%s) failed\n", file, line, expr); if (_lockcmpxchg(&noreentry, false, true)) { if (weaken(__die)) { weaken(__die)(); } else { - kprintf("can't backtrace b/c `__die` not linked%n"); + kprintf("can't backtrace b/c `__die` not linked\n"); } rc = 23; } else { diff --git a/libc/intrin/bzero.c b/libc/intrin/bzero.c index c321dc59b..40becb89b 100644 --- a/libc/intrin/bzero.c +++ b/libc/intrin/bzero.c @@ -26,7 +26,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); -noasan static dontinline antiquity void bzero_sse(char *p, size_t n) { +static dontinline antiquity void bzero_sse(char *p, size_t n) { xmm_t v = {0}; if (IsAsan()) __asan_verify(p, n); if (n <= 32) { @@ -43,7 +43,7 @@ noasan static dontinline antiquity void bzero_sse(char *p, size_t n) { } } -noasan microarchitecture("avx") static void bzero_avx(char *p, size_t n) { +microarchitecture("avx") static void bzero_avx(char *p, size_t n) { xmm_t v = {0}; if (IsAsan()) __asan_verify(p, n); if (n <= 32) { 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/getexitcodeprocess.greg.c b/libc/intrin/getexitcodeprocess.greg.c new file mode 100644 index 000000000..9fd02ea1e --- /dev/null +++ b/libc/intrin/getexitcodeprocess.greg.c @@ -0,0 +1,36 @@ +/*-*- 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/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/nt/accounting.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess; + +/** + * Obtains exit code for process. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +textwindows int32_t GetExitCodeProcess(int64_t hProcess, uint32_t *lpExitCode) { + int32_t rc; + rc = __imp_GetExitCodeProcess(hProcess, lpExitCode); + if (!rc) __winerr(); + NTTRACE("GetExitCodeProcess(%ld, [%u]) → %u% m", hProcess, *lpExitCode, rc); + return rc; +} diff --git a/libc/intrin/getmagnumstr.greg.c b/libc/intrin/getmagnumstr.greg.c new file mode 100644 index 000000000..20834e936 --- /dev/null +++ b/libc/intrin/getmagnumstr.greg.c @@ -0,0 +1,29 @@ +/*-*- 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/magnumstrs.internal.h" + +char *GetMagnumStr(const struct MagnumStr *ms, int x) { + int i; + 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/intrin/intrin.mk b/libc/intrin/intrin.mk index 77acd0597..02b18b99b 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -89,9 +89,12 @@ o/$(MODE)/libc/intrin/flushfilebuffers.greg.o \ o/$(MODE)/libc/intrin/terminateprocess.greg.o \ o/$(MODE)/libc/intrin/describemapflags.greg.o \ o/$(MODE)/libc/intrin/getfileattributes.greg.o \ +o/$(MODE)/libc/intrin/getexitcodeprocess.greg.o \ +o/$(MODE)/libc/intrin/waitforsingleobject.greg.o \ o/$(MODE)/libc/intrin/setcurrentdirectory.greg.o \ o/$(MODE)/libc/intrin/mapviewoffileexnuma.greg.o \ o/$(MODE)/libc/intrin/createfilemappingnuma.greg.o \ +o/$(MODE)/libc/intrin/waitformultipleobjects.greg.o \ o/$(MODE)/libc/intrin/generateconsolectrlevent.greg.o \ o/$(MODE)/libc/intrin/kstarttsc.o \ o/$(MODE)/libc/intrin/nomultics.o \ diff --git a/libc/intrin/kdos2errno.S b/libc/intrin/kdos2errno.S index 180d796c6..f09390e7f 100644 --- a/libc/intrin/kdos2errno.S +++ b/libc/intrin/kdos2errno.S @@ -114,7 +114,7 @@ kDos2Errno: .e kNtErrorCrc,EACCES .e kNtErrorDirNotEmpty,ENOTEMPTY .e kNtErrorDupName,EADDRINUSE - .e kNtErrorFilenameExcedRange,ENOENT + .e kNtErrorFilenameExcedRange,ENAMETOOLONG .e kNtErrorGenFailure,EACCES .e kNtErrorGracefulDisconnect,EPIPE .e kNtErrorHostDown,EHOSTUNREACH diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 39d604a92..9595606ae 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -32,6 +32,7 @@ #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/spinlock.h" #include "libc/limits.h" +#include "libc/log/internal.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/uart.internal.h" @@ -174,6 +175,7 @@ privileged static inline bool kismemtrackhosed(void) { } privileged static bool kismapped(int x) { + // xxx: we can't lock because no reentrant locks yet size_t m, r, l = 0; if (!weaken(_mmi)) return true; if (kismemtrackhosed()) return false; @@ -450,8 +452,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, i = 0; m = (1 << base) - 1; if (hash && x) sign = hash; - do - z[i++ & 127] = abet[x & m]; + do z[i++ & 127] = abet[x & m]; while ((x >>= base) || (pdot && i < prec)); goto EmitNumber; @@ -556,11 +557,6 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, case 'n': // nonstandard %n specifier - // used to print newlines that work in raw terminal modes - if (__nomultics) { - if (p < e) *p = '\r'; - ++p; - } if (p < e) *p = '\n'; ++p; break; @@ -569,7 +565,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va, // undocumented %r specifier // used for good carriage return // helps integrate loggers with repls - if (!__replmode) { + if (!__replstderr || __nocolor) { break; } else { s = "\r\033[K"; @@ -845,12 +841,12 @@ privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) { */ privileged void kvprintf(const char *fmt, va_list v) { size_t n; - char b[2048]; + char b[4000]; struct Timestamps t; if (!v) return; t = kenter(); n = kformat(b, sizeof(b), fmt, v, t); - klog(b, MIN(n, sizeof(b))); + klog(b, MIN(n, sizeof(b) - 1)); kleave(t); } diff --git a/libc/intrin/memset.c b/libc/intrin/memset.c index 30d8e8f72..b13c61325 100644 --- a/libc/intrin/memset.c +++ b/libc/intrin/memset.c @@ -26,7 +26,7 @@ typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); -noasan static dontinline antiquity void *memset_sse(char *p, char c, size_t n) { +static dontinline antiquity void *memset_sse(char *p, char c, size_t n) { xmm_t v = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; if (IsAsan()) __asan_verify(p, n); if (n <= 32) { @@ -44,8 +44,7 @@ noasan static dontinline antiquity void *memset_sse(char *p, char c, size_t n) { return p; } -noasan microarchitecture("avx") static void *memset_avx(char *p, char c, - size_t n) { +microarchitecture("avx") static void *memset_avx(char *p, char c, size_t n) { char *t; xmm_t v = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; if (IsAsan()) __asan_verify(p, n); diff --git a/libc/intrin/nomultics.c b/libc/intrin/nomultics.c index 797465a4b..e0003b831 100644 --- a/libc/intrin/nomultics.c +++ b/libc/intrin/nomultics.c @@ -17,17 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -/** - * Controls disablement of MULTICS newlines. - * - * Normally we use `\n` for newlines. If this is `true` then we'll try - * our best to use `\r\n`. This is toggled automatically on Windows or - * when `ioctl(TCSETS)` disables `OPOST`. - * - * @see kprintf() - */ -char __nomultics; - /** * Controls ANSI prefix for log emissions. * @@ -36,3 +25,4 @@ char __nomultics; * @see kprintf(), vflogf(), linenoise() */ char __replmode; +char __replstderr; diff --git a/libc/intrin/nomultics.internal.h b/libc/intrin/nomultics.internal.h index 73298e7d2..ffc15900c 100644 --- a/libc/intrin/nomultics.internal.h +++ b/libc/intrin/nomultics.internal.h @@ -3,8 +3,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern bool __nomultics; -extern bool __replmode; +extern char __replmode; +extern char __replstderr; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/onarithmeticoverflow.S b/libc/intrin/onarithmeticoverflow.S index c74d4446e..4e3d1efc2 100644 --- a/libc/intrin/onarithmeticoverflow.S +++ b/libc/intrin/onarithmeticoverflow.S @@ -25,5 +25,6 @@ __on_arithmetic_overflow: push %rbp mov %rsp,%rbp int3 - call abort +0: ud2 + jmp 0b .endfn __on_arithmetic_overflow,weak diff --git a/libc/intrin/ubsan.c b/libc/intrin/ubsan.c index 20c015296..ec357de0f 100644 --- a/libc/intrin/ubsan.c +++ b/libc/intrin/ubsan.c @@ -197,9 +197,9 @@ static wontreturn void __ubsan_unreachable(void) { } static void __ubsan_exit(void) { - kprintf("your ubsan runtime needs%n" - "\tSTATIC_YOINK(\"__die\");%n" - "in order to show you backtraces%n"); + kprintf("your ubsan runtime needs\n" + "\tSTATIC_YOINK(\"__die\");\n" + "in order to show you backtraces\n"); __restorewintty(); _Exit(99); } @@ -214,13 +214,13 @@ dontdiscard static __ubsan_die_f *__ubsan_die(void) { static void __ubsan_warning(const struct UbsanSourceLocation *loc, const char *description) { - kprintf("%s:%d: %subsan warning: %s is undefined behavior%s%n", loc->file, + kprintf("%s:%d: %subsan warning: %s is undefined behavior%s\n", loc->file, loc->line, SUBTLE, description, RESET); } dontdiscard __ubsan_die_f *__ubsan_abort(const struct UbsanSourceLocation *loc, const char *description) { - kprintf("%n%s:%d: %subsan error%s: %s%n", loc->file, loc->line, RED2, RESET, + kprintf("\n%s:%d: %subsan error%s: %s\n", loc->file, loc->line, RED2, RESET, description); return __ubsan_die(); } diff --git a/libc/intrin/waitformultipleobjects.greg.c b/libc/intrin/waitformultipleobjects.greg.c new file mode 100644 index 000000000..2b6aa0be0 --- /dev/null +++ b/libc/intrin/waitformultipleobjects.greg.c @@ -0,0 +1,39 @@ +/*-*- 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/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(WaitForMultipleObjects) *const + __imp_WaitForMultipleObjects; + +/** + * Waits for handles to change status. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +uint32_t WaitForMultipleObjects(uint32_t nCount, const int64_t *lpHandles, + bool32 bWaitAll, uint32_t dwMilliseconds) { + uint32_t x; + x = __imp_WaitForMultipleObjects(nCount, lpHandles, bWaitAll, dwMilliseconds); + if (x == -1u) __winerr(); + POLLTRACE("WaitForMultipleObjects(%ld, %p, %hhhd, %'d) → %d% m", nCount, + lpHandles, bWaitAll, dwMilliseconds, x); + return x; +} diff --git a/libc/intrin/waitforsingleobject.greg.c b/libc/intrin/waitforsingleobject.greg.c new file mode 100644 index 000000000..d345ef17b --- /dev/null +++ b/libc/intrin/waitforsingleobject.greg.c @@ -0,0 +1,37 @@ +/*-*- 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/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; + +/** + * Waits for handle to change status. + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds) { + uint32_t rc; + rc = __imp_WaitForSingleObject(hHandle, dwMilliseconds); + if (rc == -1u) __winerr(); + POLLTRACE("WaitForSingleObject(%ld, %'d) → %d% m", hHandle, dwMilliseconds, + rc); + return rc; +} diff --git a/libc/isystem/complex.h b/libc/isystem/complex.h index 8267a4409..1da2fa62f 100644 --- a/libc/isystem/complex.h +++ b/libc/isystem/complex.h @@ -1,4 +1,6 @@ #ifndef LIBC_ISYSTEM_COMPLEX_H_ #define LIBC_ISYSTEM_COMPLEX_H_ +#include "libc/complex.h" #include "libc/math.h" +#define I _Complex_I #endif diff --git a/libc/log/appendresourcereport.c b/libc/log/appendresourcereport.c index 2f160de5f..b48d1a315 100644 --- a/libc/log/appendresourcereport.c +++ b/libc/log/appendresourcereport.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/calls/struct/rusage.h" #include "libc/fmt/itoa.h" #include "libc/log/log.h" @@ -102,7 +103,11 @@ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { } if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { AppendInt(st, ru->ru_nvcsw + ru->ru_nivcsw); - appends(b, " context switch ("); + appends(b, " context switch"); + if ((ru->ru_nvcsw + ru->ru_nivcsw) > 1) { + appendw(b, READ16LE("es")); + } + appendw(b, READ16LE(" (")); AppendInt(st, (long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100); appends(b, "% consensual)"); diff --git a/libc/log/backtrace2.greg.c b/libc/log/backtrace2.greg.c index 642625fe1..376a2a580 100644 --- a/libc/log/backtrace2.greg.c +++ b/libc/log/backtrace2.greg.c @@ -50,7 +50,7 @@ #define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (18 + 1)) static void ShowHint(const char *s) { - kprintf("%snote: %s%s%n", SUBTLE, s, RESET); + kprintf("%snote: %s%s\n", SUBTLE, s, RESET); } static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { @@ -63,8 +63,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { char *debugbin, *p1, *p2, *p3, *addr2line; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; - return -1; - if (!(debugbin = FindDebugBinary())) { return -1; } @@ -118,7 +116,17 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { _exit(127); } close(pipefds[1]); - while ((got = read(pipefds[0], buf, kBacktraceBufSize)) > 0) { + for (;;) { + got = read(pipefds[0], buf, kBacktraceBufSize); + if (!got) break; + if (got == -1 && errno == EINTR) { + errno = 0; + continue; + } + if (got == -1) { + kprintf("error reading backtrace %m\n"); + break; + } p1 = buf; p3 = p1 + got; /* @@ -176,8 +184,8 @@ void ShowBacktrace(int fd, const struct StackFrame *bp) { __strace = st; g_ftrace = ft; #else - kprintf("ShowBacktrace() needs these flags to show C backtrace:%n" - "\t-D__FNO_OMIT_FRAME_POINTER__%n" - "\t-fno-omit-frame-pointer%n"); + (fprintf)(stderr, "ShowBacktrace() needs these flags to show C backtrace:\n" + "\t-D__FNO_OMIT_FRAME_POINTER__\n" + "\t-fno-omit-frame-pointer\n"); #endif } diff --git a/libc/log/backtrace3.greg.c b/libc/log/backtrace3.greg.c index 24f8d15d1..e1857751e 100644 --- a/libc/log/backtrace3.greg.c +++ b/libc/log/backtrace3.greg.c @@ -56,11 +56,11 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, gi = garbage ? garbage->i : 0; for (i = 0, frame = bp; frame; frame = frame->next) { if (!IsValidStackFramePointer(frame)) { - kprintf("%p corrupt frame pointer%n", frame); + kprintf("%p corrupt frame pointer\n", frame); break; } if (++i == LIMIT) { - kprintf("%n"); + kprintf("\n"); break; } addr = frame->addr; @@ -84,8 +84,8 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, } else { addend = 0; } - kprintf("%012lx %012lx %s%+d\r%n", frame, addr, - __get_symbol_name(st, symbol), addend); + kprintf("%012lx %012lx %s%+d\n", frame, addr, __get_symbol_name(st, symbol), + addend); } return 0; } diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index 4944b53e9..d98055e7e 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -51,22 +51,22 @@ relegated void __check_fail(const char *suffix, const char *opstr, __start_fatal(file, line); __stpcpy(hostname, "unknown"); gethostname(hostname, sizeof(hostname)); - kprintf("check failed on %s pid %d%n", hostname, getpid()); - kprintf("\tCHECK_%^s(%s, %s);%n", suffix, wantstr, gotstr); - kprintf("\t\t → %p (%s)%n", want, wantstr); - kprintf("\t\t%s %p (%s)%n", opstr, got, gotstr); + kprintf("check failed on %s pid %d\n", hostname, getpid()); + kprintf("\tCHECK_%^s(%s, %s);\n", suffix, wantstr, gotstr); + kprintf("\t\t → %p (%s)\n", want, wantstr); + kprintf("\t\t%s %p (%s)\n", opstr, got, gotstr); if (!isempty(fmt)) { kprintf("\t"); va_start(va, fmt); kvprintf(fmt, va); va_end(va); - kprintf("%n"); + kprintf("\n"); } - kprintf("\t%m%n\t%s%s", SUBTLE, program_invocation_name); + kprintf("\t%m\n\t%s%s", SUBTLE, program_invocation_name); for (i = 1; i < __argc; ++i) { kprintf(" %s", __argv[i]); } - kprintf("%s%n", RESET); + kprintf("%s\n", RESET); if (!IsTiny() && e == ENOMEM) { PrintMemoryIntervals(2, &_mmi); } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index ff313a715..410364304 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -17,10 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/log/internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" /** @@ -36,9 +36,9 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, const char *opchar) { __restore_tty(); - kprintf("%n%serror: %s: check failed: 0x%x %s 0x%x (%s)%n", - !__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, got, - strerror(errno)); + (fprintf)(stderr, "\n%serror: %s: check failed: 0x%x %s 0x%x (%s)\n", + !__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, + got, strerror(errno)); __restorewintty(); _Exit(68); } diff --git a/libc/log/commandvenv.c b/libc/log/commandvenv.c index 5b1f10385..5586e8cfc 100644 --- a/libc/log/commandvenv.c +++ b/libc/log/commandvenv.c @@ -57,5 +57,5 @@ const char *commandvenv(const char *var, const char *cmd) { return NULL; } } - return commandv(cmd, pathbuf); + return commandv(cmd, pathbuf, sizeof(pathbuf)); } diff --git a/libc/log/countexpr_report.c b/libc/log/countexpr_report.c index baba2caec..f3ffbec0b 100644 --- a/libc/log/countexpr_report.c +++ b/libc/log/countexpr_report.c @@ -55,7 +55,7 @@ static void PrintHistogram(const long *h, size_t n, long t) { for (j = 0; j < p / 100; ++j) s[j] = '#'; s[j] = 0; logos = i ? 1ul << (i - 1) : 0; - kprintf("%'12lu %'16ld %3d.%02d%% %s%n", logos, h[i], p / 100, p % 100, + kprintf("%'12lu %'16ld %3d.%02d%% %s\n", logos, h[i], p / 100, p % 100, s); } } 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/log/oncrash.c b/libc/log/oncrash.c index 3bdfc1fd7..b46452bd5 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -63,7 +63,7 @@ relegated static void ShowFunctionCalls(ucontext_t *ctx) { struct StackFrame *bp; struct StackFrame goodframe; if (!ctx->uc_mcontext.rip) { - kprintf("%s is NULL can't show backtrace%n", "RIP"); + kprintf("%s is NULL can't show backtrace\n", "RIP"); } else { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; @@ -114,7 +114,7 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { long double st; char *p, buf[128]; p = buf; - kprintf("%n"); + kprintf("\n"); for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { if (j > 0) *p++ = ' '; if (!(s = kGregNames[(unsigned)kGregOrder[i]])[2]) *p++ = ' '; @@ -135,7 +135,7 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { p = __uintcpy(p, x / 1000), *p++ = '.'; p = __uintcpy(p, x % 1000); *p = 0; - kprintf("%s%n", buf); + kprintf("%s\n", buf); p = buf; } } @@ -143,14 +143,14 @@ relegated static void ShowGeneralRegisters(ucontext_t *ctx) { p, ctx->uc_mcontext.eflags, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->swd : 0, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->mxcsr : 0); - kprintf("%s%n", buf); + kprintf("%s\n", buf); } relegated static void ShowSseRegisters(ucontext_t *ctx) { size_t i; char *p, buf[128]; if (ctx->uc_mcontext.fpregs) { - kprintf("%n"); + kprintf("\n"); for (i = 0; i < 8; ++i) { p = buf; if (i >= 10) { @@ -175,7 +175,7 @@ relegated static void ShowSseRegisters(ucontext_t *ctx) { p = __fixcpy(p, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[1], 64); p = __fixcpy(p, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[0], 64); *p = 0; - kprintf("XMM%s%n", buf); + kprintf("XMM%s\n", buf); } } } @@ -201,10 +201,10 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, uname(&names); p = buf; errno = err; - kprintf("%n%serror%s: Uncaught %G (%s) on %s pid %d%n" - " %s%n" - " %m%n" - " %s %s %s %s%n", + kprintf("\n%serror%s: Uncaught %G (%s) on %s pid %d\n" + " %s\n" + " %m\n" + " %s %s %s %s\n", !__nocolor ? "\e[30;101m" : "", !__nocolor ? "\e[0m" : "", sig, (ctx && (ctx->uc_mcontext.rsp >= GetStaticStackAddr(0) && ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + PAGESIZE)) @@ -213,12 +213,12 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, host, getpid(), program_invocation_name, names.sysname, names.version, names.nodename, names.release); if (ctx) { - kprintf("%n"); + kprintf("\n"); ShowFunctionCalls(ctx); ShowGeneralRegisters(ctx); ShowSseRegisters(ctx); } - kprintf("%n"); + kprintf("\n"); PrintMemoryIntervals(2, &_mmi); /* PrintSystemMappings(2); */ if (__argv) { @@ -228,7 +228,7 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si, kprintf("%s ", __argv[i]); } } - kprintf("%n"); + kprintf("\n"); } relegated static void RestoreDefaultCrashSignalHandlers(void) { @@ -245,14 +245,14 @@ static wontreturn relegated noinstrument void __minicrash(int sig, struct siginfo *si, ucontext_t *ctx, const char *kind) { - kprintf("%n" - "%n" - "CRASHED %s WITH %G%n" - "%s%n" - "RIP %x%n" - "RSP %x%n" - "RBP %x%n" - "%n", + kprintf("\n" + "\n" + "CRASHED %s WITH %G\n" + "%s\n" + "RIP %x\n" + "RSP %x\n" + "RBP %x\n" + "\n", kind, sig, __argv[0], ctx ? ctx->uc_mcontext.rip : 0, ctx ? ctx->uc_mcontext.rsp : 0, ctx ? ctx->uc_mcontext.rbp : 0); __restorewintty(); diff --git a/libc/log/printgarbage.c b/libc/log/printgarbage.c index ac0afdb00..69f0bc574 100644 --- a/libc/log/printgarbage.c +++ b/libc/log/printgarbage.c @@ -30,10 +30,10 @@ void PrintGarbage(void) { size_t i; char name[19]; const char *symbol; - kprintf("%n"); - kprintf(" SHADOW STACK @ %p%n", __builtin_frame_address(0)); - kprintf("garbage ent. parent frame original ret callback arg %n"); - kprintf("------------ ------------ ------------------ ------------------ ------------------%n"); + kprintf("\n"); + kprintf(" SHADOW STACK @ %p\n", __builtin_frame_address(0)); + kprintf("garbage ent. parent frame original ret callback arg \n"); + kprintf("------------ ------------ ------------------ ------------------ ------------------\n"); if (__garbage.i) { for (i = __garbage.i; i--;) { symbol = __get_symbol_by_addr(__garbage.p[i].ret); @@ -42,7 +42,7 @@ void PrintGarbage(void) { } else { ksnprintf(name, sizeof(name), "%#014lx", __garbage.p[i].ret); } - kprintf("%12lx %12lx %18s %18s %#18lx%n", + kprintf("%12lx %12lx %18s %18s %#18lx\n", __garbage.p + i, __garbage.p[i].frame, name, @@ -50,7 +50,7 @@ void PrintGarbage(void) { __garbage.p[i].arg); } } else { - kprintf("%12s %12s %18s %18s %18s%n","empty","-","-","-","-"); + kprintf("%12s %12s %18s %18s %18s\n","empty","-","-","-","-"); } - kprintf("%n"); + kprintf("\n"); } diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 64a83772d..ffedd9fac 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -55,7 +55,7 @@ void vflogf_onfail(FILE *f) { fseek(f, SEEK_SET, 0); f->beg = f->end = 0; clearerr(f); - (fprintf)(f, "performed emergency log truncation: %s%n", strerror(err)); + (fprintf)(f, "performed emergency log truncation: %s\n", strerror(err)); } } @@ -105,7 +105,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, vflogf_onfail(f); } (vfprintf)(f, fmt, va); - fprintf(f, "%n"); + fprintf(f, "\n"); if (bufmode == _IOLBF) { f->bufmode = _IOLBF; fflush(f); @@ -114,7 +114,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, __start_fatal(file, line); strcpy(buf32, "unknown"); gethostname(buf32, sizeof(buf32)); - (dprintf)(STDERR_FILENO, "fatality %s pid %d%n", buf32, getpid()); + (dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid()); __die(); unreachable; } diff --git a/libc/mem/mem.mk b/libc/mem/mem.mk index 052fba956..86a2ef8a9 100644 --- a/libc/mem/mem.mk +++ b/libc/mem/mem.mk @@ -29,6 +29,7 @@ LIBC_MEM_A_CHECKS = \ LIBC_MEM_A_DIRECTDEPS = \ LIBC_CALLS \ + LIBC_SYSV_CALLS \ LIBC_FMT \ LIBC_INTRIN \ LIBC_NEXGEN32E \ diff --git a/libc/mem/pledge.c b/libc/mem/pledge.c new file mode 100644 index 000000000..b115d719f --- /dev/null +++ b/libc/mem/pledge.c @@ -0,0 +1,411 @@ +/*-*- 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/struct/filter.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/nrlinux.h" +#include "libc/sysv/consts/pr.h" +#include "libc/sysv/errfuns.h" +#include "tool/net/sandbox.h" + +static const uint16_t kPledgeLinuxDefault[] = { + __NR_linux_exit, // + __NR_linux_exit_group, // + 0, // +}; + +static const uint16_t kPledgeLinuxStdio[] = { + __NR_linux_clock_getres, // + __NR_linux_clock_gettime, // + __NR_linux_close, // + __NR_linux_dup, // + __NR_linux_dup2, // + __NR_linux_dup3, // + __NR_linux_fchdir, // + /* __NR_linux_fcntl, // */ + __NR_linux_fstat, // + __NR_linux_fsync, // + __NR_linux_ftruncate, // + __NR_linux_getdents, // + __NR_linux_getegid, // + __NR_linux_getrandom, // + __NR_linux_geteuid, // + __NR_linux_getgid, // + __NR_linux_getgroups, // + __NR_linux_getitimer, // + __NR_linux_getpgid, // + __NR_linux_getpgrp, // + __NR_linux_getpid, // + __NR_linux_getppid, // + __NR_linux_getresgid, // + __NR_linux_getresuid, // + __NR_linux_getrlimit, // + __NR_linux_getsid, // + __NR_linux_gettimeofday, // + __NR_linux_getuid, // + __NR_linux_lseek, // + __NR_linux_madvise, // + __NR_linux_brk, // + __NR_linux_mmap, // + __NR_linux_mprotect, // + __NR_linux_munmap, // + __NR_linux_nanosleep, // + __NR_linux_pipe, // + __NR_linux_pipe2, // + __NR_linux_poll, // + __NR_linux_pread, // + __NR_linux_preadv, // + __NR_linux_pwrite, // + __NR_linux_pwritev, // + __NR_linux_read, // + __NR_linux_readv, // + __NR_linux_recvfrom, // + __NR_linux_recvmsg, // + __NR_linux_select, // + __NR_linux_sendmsg, // + __NR_linux_sendto, // + __NR_linux_setitimer, // + __NR_linux_shutdown, // + __NR_linux_sigaction, // + __NR_linux_sigprocmask, // + __NR_linux_sigreturn, // + __NR_linux_socketpair, // + __NR_linux_umask, // + __NR_linux_wait4, // + __NR_linux_write, // + __NR_linux_writev, // + 0, // +}; + +static const uint16_t kPledgeLinuxRpath[] = { + __NR_linux_chdir, // + __NR_linux_getcwd, // + __NR_linux_openat, // + __NR_linux_fstatat, // + __NR_linux_faccessat, // + __NR_linux_readlinkat, // + __NR_linux_lstat, // + __NR_linux_chmod, // + __NR_linux_fchmod, // + __NR_linux_fchmodat, // + __NR_linux_chown, // + __NR_linux_fchown, // + __NR_linux_fchownat, // + __NR_linux_fstat, // + 0, // +}; + +static const uint16_t kPledgeLinuxWpath[] = { + __NR_linux_getcwd, // + __NR_linux_openat, // + __NR_linux_fstatat, // + __NR_linux_faccessat, // + __NR_linux_readlinkat, // + __NR_linux_lstat, // + __NR_linux_chmod, // + __NR_linux_fchmod, // + __NR_linux_fchmodat, // + __NR_linux_chown, // + __NR_linux_fchown, // + __NR_linux_fchownat, // + __NR_linux_fstat, // + 0, // +}; + +static const uint16_t kPledgeLinuxCpath[] = { + __NR_linux_rename, // + __NR_linux_renameat, // + __NR_linux_link, // + __NR_linux_linkat, // + __NR_linux_symlink, // + __NR_linux_symlinkat, // + __NR_linux_unlink, // + __NR_linux_unlinkat, // + __NR_linux_mkdir, // + __NR_linux_mkdirat, // + __NR_linux_rmdir, // + 0, // +}; + +static const uint16_t kPledgeLinuxDpath[] = { + __NR_linux_mknod, // + 0, // +}; + +static const uint16_t kPledgeLinuxTmppath[] = { + __NR_linux_lstat, // + __NR_linux_chmod, // + __NR_linux_chown, // + __NR_linux_unlink, // + __NR_linux_fstat, // + 0, // +}; + +static const uint16_t kPledgeLinuxInet[] = { + __NR_linux_socket, // + __NR_linux_listen, // + __NR_linux_bind, // + __NR_linux_connect, // + __NR_linux_accept4, // + __NR_linux_accept, // + __NR_linux_getpeername, // + __NR_linux_getsockname, // + __NR_linux_setsockopt, // + __NR_linux_getsockopt, // + 0, // +}; + +static const uint16_t kPledgeLinuxFattr[] = { + __NR_linux_utimes, // + __NR_linux_utimensat, // + __NR_linux_chmod, // + __NR_linux_fchmod, // + __NR_linux_fchmodat, // + __NR_linux_chown, // + __NR_linux_fchownat, // + __NR_linux_lchown, // + __NR_linux_fchown, // + __NR_linux_utimes, // + 0, // +}; + +static const uint16_t kPledgeLinuxUnix[] = { + __NR_linux_socket, // + __NR_linux_listen, // + __NR_linux_bind, // + __NR_linux_connect, // + __NR_linux_accept4, // + __NR_linux_accept, // + __NR_linux_getpeername, // + __NR_linux_getsockname, // + __NR_linux_setsockopt, // + __NR_linux_getsockopt, // + 0, // +}; + +static const uint16_t kPledgeLinuxDns[] = { + __NR_linux_sendto, // + __NR_linux_recvfrom, // + __NR_linux_socket, // + __NR_linux_connect, // + 0, // +}; + +static const uint16_t kPledgeLinuxProc[] = { + __NR_linux_fork, // + __NR_linux_vfork, // + __NR_linux_kill, // + __NR_linux_getpriority, // + __NR_linux_setpriority, // + __NR_linux_setrlimit, // + __NR_linux_setpgid, // + __NR_linux_setsid, // + 0, // +}; + +static const uint16_t kPledgeLinuxExec[] = { + __NR_linux_execve, // + 0, // +}; + +static const uint16_t kPledgeLinuxId[] = { + __NR_linux_setuid, // + __NR_linux_setreuid, // + __NR_linux_setresuid, // + __NR_linux_setgid, // + __NR_linux_setregid, // + __NR_linux_setresgid, // + __NR_linux_setgroups, // + __NR_linux_setrlimit, // + __NR_linux_getpriority, // + __NR_linux_setpriority, // + 0, // +}; + +static const struct Pledges { + const char *name; + const uint16_t *syscalls; +} kPledgeLinux[] = { + {"default", kPledgeLinuxDefault}, // + {"stdio", kPledgeLinuxStdio}, // + {"rpath", kPledgeLinuxRpath}, // + {"wpath", kPledgeLinuxWpath}, // + {"cpath", kPledgeLinuxCpath}, // + {"dpath", kPledgeLinuxDpath}, // + {"tmppath", kPledgeLinuxTmppath}, // + {"inet", kPledgeLinuxInet}, // + {"fattr", kPledgeLinuxFattr}, // + {"unix", kPledgeLinuxUnix}, // + {"dns", kPledgeLinuxDns}, // + {"proc", kPledgeLinuxProc}, // + {"exec", kPledgeLinuxExec}, // + {"id", kPledgeLinuxId}, // + {0}, // +}; + +static const struct sock_filter kFilterStart[] = { + _SECCOMP_MACHINE(AUDIT_ARCH_X86_64), // + _SECCOMP_LOAD_SYSCALL_NR(), // +}; + +static const struct sock_filter kFilterEnd[] = { + _SECCOMP_LOG_AND_RETURN_ERRNO(1), // EPERM +}; + +struct Filter { + size_t n; + struct sock_filter *p; +}; + +static bool AppendFilter(struct Filter *f, struct sock_filter *p, size_t n) { + size_t m; + struct sock_filter *q; + m = f->n + n; + if (!(q = realloc(f->p, m * sizeof(*f->p)))) return false; + memcpy(q + f->n, p, n * sizeof(*q)); + f->p = q; + f->n = m; + return true; +} + +static bool AppendPledge(struct Filter *f, const uint16_t *p) { + int i; + for (i = 0; p[i]; ++i) { + struct sock_filter fragment[] = {_SECCOMP_ALLOW_SYSCALL(p[i])}; + if (!AppendFilter(f, fragment, ARRAYLEN(fragment))) { + return false; + } + } + return true; +} + +static const uint16_t *FindPledge(const struct Pledges *p, const char *name) { + int i; + for (i = 0; p[i].name; ++i) { + if (!strcasecmp(name, p[i].name)) { + return p[i].syscalls; + } + } + return 0; +} + +static int sys_pledge_linux(const char *promises, const char *execpromises) { + bool ok; + int rc = -1; + struct Filter f = {0}; + const uint16_t *pledge; + char *s, *tok, *state, *start; + if (execpromises) return einval(); + if ((start = s = strdup(promises)) && + AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && + AppendPledge(&f, kPledgeLinuxDefault)) { + for (ok = true; (tok = strtok_r(start, " \t\r\n", &state)); start = 0) { + if (!(pledge = FindPledge(kPledgeLinux, tok))) { + ok = false; + rc = einval(); + break; + } + if (!AppendPledge(&f, pledge)) { + ok = false; + break; + } + } + if (ok && AppendFilter(&f, kFilterEnd, ARRAYLEN(kFilterEnd)) && + (rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1) { + struct sock_fprog sandbox = {.len = f.n, .filter = f.p}; + rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox); + } + } + free(f.p); + free(s); + return rc; +} + +/** + * Restricts system operations. + * + * Only available on OpenBSD and Linux. + * + * By default exit and exit_group are always allowed. This is useful + * for processes that perform pure computation and interface with the + * parent via shared memory. + * + * `promises` is a string that may include any of the following groups + * delimited by spaces. + * + * - "stdio" allows clock_getres, clock_gettime, close, dup, dup2, dup3, + * fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, + * geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, + * getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, + * getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, + * pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, + * recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, + * sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, write, + * writev. + * + * - "rpath" allows chdir, getcwd, openat, fstatat, faccessat, + * readlinkat, lstat, chmod, fchmod, fchmodat, chown, fchown, + * fchownat, fstat. + * + * - "wpath" allows getcwd, openat, fstatat, faccessat, readlinkat, + * lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + * + * - "cpath" allows rename, renameat, link, linkat, symlink, symlinkat, + * unlink, unlinkat, mkdir, mkdirat, rmdir. + * + * - "dpath" allows mknod + * + * - "tmppath" allows lstat, chmod, chown, unlink, fstat. + * + * - "inet" allows socket, listen, bind, connect, accept4, accept, + * getpeername, getsockname, setsockopt, getsockopt. + * + * - "fattr" allows utimes, utimensat, chmod, fchmod, fchmodat, chown, + * fchownat, lchown, fchown, utimes. + * + * - "unix" allows socket, listen, bind, connect, accept4, accept, + * getpeername, getsockname, setsockopt, getsockopt. + * + * - "dns" allows sendto, recvfrom, socket, connect. + * + * - "proc" allows fork, vfork, kill, getpriority, setpriority, + * setrlimit, setpgid, setsid. + * + * - "exec" allows execve. + * + * - "id" allows setuid, setreuid, setresuid, setgid, setregid, + * setresgid, setgroups, setrlimit, getpriority, setpriority. + * + */ +int pledge(const char *promises, const char *execpromises) { + int rc; + if (IsLinux()) { + rc = sys_pledge_linux(promises, execpromises); + } else { + rc = sys_pledge(promises, execpromises); + } + STRACE("pledge(%#s, %#s) → %d% m", promises, execpromises, rc); + return rc; +} diff --git a/libc/mem/vasprintf.c b/libc/mem/vasprintf.c index f74ca4251..26ad2fe6c 100644 --- a/libc/mem/vasprintf.c +++ b/libc/mem/vasprintf.c @@ -22,33 +22,36 @@ /** * Formats string w/ dynamic memory allocation. - * - * @param *strp is output-only and must be free'd, even on error; since - * that's the behavior that'll make your code most portable - * @return complete bytes written (excluding NUL) or -1 w/ errno * @see xasprintf() for a better API */ int(vasprintf)(char **strp, const char *fmt, va_list va) { - char *p; - size_t size; va_list vb; + size_t size; + char *p, *p2; int wrote, rc = -1; - if ((*strp = malloc((size = 512)))) { + if ((p = malloc((size = 512)))) { va_copy(vb, va); - wrote = (vsnprintf)(*strp, size, fmt, va); + wrote = (vsnprintf)(p, size, fmt, va); if (wrote < size) { - if ((p = realloc(*strp, wrote + 1))) *strp = p; - rc = wrote; + if ((p2 = realloc(p, wrote + 1))) { + p = p2; + rc = wrote; + } } else { size = wrote + 1; - if ((p = realloc(*strp, size))) { - *strp = p; - wrote = (vsnprintf)(*strp, size, fmt, vb); + if ((p2 = realloc(p, size))) { + p = p2; + wrote = (vsnprintf)(p, size, fmt, vb); assert(wrote == size - 1); rc = wrote; } } va_end(vb); } - return rc; + if (rc != -1) { + *strp = p; + return rc; + } else { + return -1; + } } diff --git a/libc/nt/errors.h b/libc/nt/errors.h index 6a0301d39..da05e1af2 100644 --- a/libc/nt/errors.h +++ b/libc/nt/errors.h @@ -164,7 +164,7 @@ #define kNtErrorInfloopInRelocChain 202 #define kNtErrorEnvvarNotFound 203 #define kNtErrorNoSignalSent 205 -#define kNtErrorFilenameExcedRange 206 +#define kNtErrorFilenameExcedRange 206 /* ENAMETOOLONG */ #define kNtErrorRing2StackInUse 207 #define kNtErrorMetaExpansionTooLong 208 #define kNtErrorInvalidSignalNumber 209 diff --git a/libc/nt/kernel32/GetExitCodeProcess.s b/libc/nt/kernel32/GetExitCodeProcess.s index 78b43b59a..bd7cdc55f 100644 --- a/libc/nt/kernel32/GetExitCodeProcess.s +++ b/libc/nt/kernel32/GetExitCodeProcess.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_GetExitCodeProcess,GetExitCodeProcess,0 .text.windows -GetExitCodeProcess: +__GetExitCodeProcess: push %rbp mov %rsp,%rbp .profilable mov __imp_GetExitCodeProcess(%rip),%rax jmp __sysv2nt - .endfn GetExitCodeProcess,globl + .endfn __GetExitCodeProcess,globl .previous diff --git a/libc/nt/kernel32/WaitForMultipleObjects.s b/libc/nt/kernel32/WaitForMultipleObjects.s index d5bd67864..ec06eccd6 100644 --- a/libc/nt/kernel32/WaitForMultipleObjects.s +++ b/libc/nt/kernel32/WaitForMultipleObjects.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_WaitForMultipleObjects,WaitForMultipleObjects,0 .text.windows -WaitForMultipleObjects: +__WaitForMultipleObjects: push %rbp mov %rsp,%rbp .profilable mov __imp_WaitForMultipleObjects(%rip),%rax jmp __sysv2nt - .endfn WaitForMultipleObjects,globl + .endfn __WaitForMultipleObjects,globl .previous diff --git a/libc/nt/kernel32/WaitForSingleObject.s b/libc/nt/kernel32/WaitForSingleObject.s index 90adca673..61363d463 100644 --- a/libc/nt/kernel32/WaitForSingleObject.s +++ b/libc/nt/kernel32/WaitForSingleObject.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_WaitForSingleObject,WaitForSingleObject,0 .text.windows -WaitForSingleObject: +__WaitForSingleObject: push %rbp mov %rsp,%rbp .profilable mov __imp_WaitForSingleObject(%rip),%rax jmp __sysv2nt - .endfn WaitForSingleObject,globl + .endfn __WaitForSingleObject,globl .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 562cb3c3b..50db7cd24 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -500,7 +500,6 @@ imp 'GetEnvironmentStringsA' GetEnvironmentStringsA kernel32 0 1 imp 'GetEnvironmentVariable' GetEnvironmentVariableW kernel32 0 3 imp 'GetEnvironmentVariableA' GetEnvironmentVariableA kernel32 0 3 imp 'GetErrorMode' GetErrorMode kernel32 0 -imp 'GetExitCodeProcess' GetExitCodeProcess kernel32 0 2 imp 'GetExitCodeThread' GetExitCodeThread kernel32 0 2 imp 'GetExpandedName' GetExpandedNameW kernel32 579 imp 'GetExpandedNameA' GetExpandedNameA kernel32 578 @@ -1275,9 +1274,7 @@ imp 'VirtualUnlock' VirtualUnlock kernel32 0 imp 'WTSGetActiveConsoleSessionId' WTSGetActiveConsoleSessionId kernel32 1497 imp 'WaitCommEvent' WaitCommEvent kernel32 0 imp 'WaitForDebugEvent' WaitForDebugEvent kernel32 0 -imp 'WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 imp 'WaitForMultipleObjectsEx' WaitForMultipleObjectsEx kernel32 0 5 -imp 'WaitForSingleObject' WaitForSingleObject kernel32 0 2 imp 'WaitForSingleObjectEx' WaitForSingleObjectEx kernel32 0 3 imp 'WaitNamedPipe' WaitNamedPipeW kernel32 0 imp 'WaitNamedPipeA' WaitNamedPipeA kernel32 1509 2 @@ -1357,6 +1354,7 @@ imp '__FindNextFile' FindNextFileW kernel32 0 2 imp '__FlushFileBuffers' FlushFileBuffers kernel32 0 1 imp '__FlushViewOfFile' FlushViewOfFile kernel32 0 2 imp '__GenerateConsoleCtrlEvent' GenerateConsoleCtrlEvent kernel32 0 2 +imp '__GetExitCodeProcess' GetExitCodeProcess kernel32 0 2 imp '__GetFileAttributes' GetFileAttributesW kernel32 0 1 imp '__MapViewOfFileEx' MapViewOfFileEx kernel32 0 6 imp '__MapViewOfFileExNuma' MapViewOfFileExNuma kernel32 0 7 @@ -1372,6 +1370,8 @@ imp '__TlsGetValue' TlsGetValue kernel32 0 1 imp '__TlsSetValue' TlsSetValue kernel32 0 2 imp '__UnmapViewOfFile' UnmapViewOfFile kernel32 0 1 imp '__VirtualProtect' VirtualProtect kernel32 0 4 +imp '__WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 +imp '__WaitForSingleObject' WaitForSingleObject kernel32 0 2 # ADVAPI32.DLL # 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/nt/thunk/accounting.inc b/libc/nt/thunk/accounting.inc index 860f1f14b..5c1bcbf64 100644 --- a/libc/nt/thunk/accounting.inc +++ b/libc/nt/thunk/accounting.inc @@ -6,6 +6,3 @@ extern typeof(GetThreadTimes) *const __imp_GetThreadTimes __msabi; #define GetUserName(...) __imp_GetUserNameW(__VA_ARGS__) extern typeof(GetUserName) *const __imp_GetUserNameW __msabi; - -#define GetExitCodeProcess(...) __imp_GetExitCodeProcess(__VA_ARGS__) -extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess __msabi; diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index 4230c33bd..aee215169 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/calls/calls.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" @@ -30,7 +31,7 @@ const char *FindDebugBinary(void) { static bool once; static char *res; - static char buf[PATH_MAX + 1]; + static char buf[PATH_MAX]; char *p; size_t n; if (!once) { @@ -40,12 +41,12 @@ const char *FindDebugBinary(void) { if (n > 4 && READ32LE(p + n - 4) == READ32LE(".dbg")) { res = p; } else if (n > 4 && READ32LE(p + n - 4) == READ32LE(".com") && - n + 4 <= PATH_MAX) { + n + 4 < ARRAYLEN(buf)) { mempcpy(mempcpy(buf, p, n), ".dbg", 5); if (fileexists(buf)) { res = buf; } - } else if (n + 8 <= PATH_MAX) { + } else if (n + 8 < ARRAYLEN(buf)) { mempcpy(mempcpy(buf, p, n), ".com.dbg", 9); if (fileexists(buf)) { res = buf; diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 093436195..4fd7fc4c5 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -212,9 +212,18 @@ textwindows void WinMainForked(void) { // since the handles closed on fork if (weaken(ForkNtStdinWorker)) weaken(ForkNtStdinWorker)(); struct Fds *fds = VEIL("r", &g_fds); - fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle); - fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle); - fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle); + fds->p[0].handle = fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle); + fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle); + fds->p[2].handle = fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle); + + // untrack the forked children of the parent since we marked the + // CreateProcess() process handle below as non-inheritable + for (i = 0; i < fds->n; ++i) { + if (fds->p[i].kind == kFdProcess) { + fds->p[i].kind = 0; + fds->f = MIN(i, fds->f); + } + } // restore the crash reporting stuff if (weaken(__wincrash_nt)) { @@ -269,9 +278,8 @@ textwindows int sys_fork_nt(void) { args = args2; } #endif - if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, - &kNtIsInheritable, NULL, true, 0, NULL, &startinfo, - &procinfo) != -1) { + if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, 0, 0, + true, 0, 0, &startinfo, &procinfo) != -1) { CloseHandle(procinfo.hThread); ok = WriteAll(writer, jb, sizeof(jb)) && WriteAll(writer, &_mmi.i, sizeof(_mmi.i)) && diff --git a/libc/runtime/getargmax.c b/libc/runtime/getargmax.c new file mode 100644 index 000000000..25aabc1ae --- /dev/null +++ b/libc/runtime/getargmax.c @@ -0,0 +1,33 @@ +/*-*- 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/dce.h" +#include "libc/runtime/runtime.h" + +/** + * Returns `ARG_MAX` for host platform. + */ +int __arg_max(void) { + if (IsWindows()) return 32767; + if (IsLinux()) return 128 * 1024; + if (IsNetbsd()) return 256 * 1024; + if (IsFreebsd()) return 512 * 1024; + if (IsOpenbsd()) return 512 * 1024; + if (IsXnu()) return 1024 * 1024; + return ARG_MAX; +} diff --git a/libc/runtime/getinterpreterexecutablename.c b/libc/runtime/getinterpreterexecutablename.c index fd4331b8a..2f3aa6c24 100644 --- a/libc/runtime/getinterpreterexecutablename.c +++ b/libc/runtime/getinterpreterexecutablename.c @@ -62,17 +62,17 @@ char *GetInterpreterExecutableName(char *p, size_t n) { } else if ((rc = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", p, n - 1)) > 0) { errno = e; - p[n] = 0; + p[rc] = 0; return p; } else if (IsFreebsd() || IsNetbsd()) { - cmd[0] = 1 /* CTL_KERN */; - cmd[1] = 14 /* KERN_PROC */; - if (IsFreebsd()) { - cmd[2] = 12 /* KERN_PROC_PATHNAME */; - } else { - cmd[2] = 5 /* KERN_PROC_PATHNAME */; - } - cmd[3] = -1; /* current process */ + cmd[0] = 1; // CTL_KERN + cmd[1] = 14; // KERN_PROC + if (IsFreebsd()) { // + cmd[2] = 12; // KERN_PROC_PATHNAME + } else { // + cmd[2] = 5; // KERN_PROC_PATHNAME + } // + cmd[3] = -1; // current process if (sysctl(cmd, ARRAYLEN(cmd), p, &n, 0, 0) != -1) { errno = e; return p; diff --git a/libc/runtime/getsymboltable.greg.c b/libc/runtime/getsymboltable.greg.c index 584170d26..60af4749a 100644 --- a/libc/runtime/getsymboltable.greg.c +++ b/libc/runtime/getsymboltable.greg.c @@ -54,7 +54,7 @@ static ssize_t FindSymtabInZip(struct Zipos *zipos) { * @note This code can't depend on dlmalloc() */ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) { - ssize_t cf, lf; + ssize_t rc, cf, lf; size_t size, size2; struct DeflateState ds; struct SymbolTable *res = 0; @@ -67,14 +67,16 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) { case kZipCompressionNone: memcpy(res, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), size); break; +#if 0 case kZipCompressionDeflate: - if (undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), - GetZipLfileCompressedSize(zipos->map + lf), - &ds) == -1) { + rc = undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), + GetZipLfileCompressedSize(zipos->map + lf), &ds); + if (rc == -1) { munmap(res, size2); res = 0; } break; +#endif default: munmap(res, size2); res = 0; @@ -115,11 +117,8 @@ static struct SymbolTable *GetSymbolTableFromElf(void) { * @return symbol table, or NULL w/ errno on first call */ struct SymbolTable *GetSymbolTable(void) { - int ft, st; struct Zipos *z; if (!g_symtab && !__isworker) { - ft = g_ftrace, g_ftrace = 0; - st = __strace, __strace = 0; if (weaken(__zipos_get) && (z = weaken(__zipos_get)())) { if ((g_symtab = GetSymbolTableFromZip(z))) { g_symtab->names = @@ -131,8 +130,6 @@ struct SymbolTable *GetSymbolTable(void) { if (!g_symtab) { g_symtab = GetSymbolTableFromElf(); } - g_ftrace = ft; - __strace = st; } return g_symtab; } diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 468d0d401..135130d48 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -30,6 +30,7 @@ int GetDosArgv(const char16_t *, char *, size_t, char **, size_t); Elf64_Ehdr *MapElfRead(const char *, struct MappedFile *) hidden; int GetDosEnviron(const char16_t *, char *, size_t, char **, size_t); bool __intercept_flag(int *, char *[], const char *); +int sys_mprotect_nt(void *, size_t, int) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/ismemtracked.greg.c b/libc/runtime/ismemtracked.greg.c index ba0b7b2ee..becbd54aa 100644 --- a/libc/runtime/ismemtracked.greg.c +++ b/libc/runtime/ismemtracked.greg.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/memtrack.internal.h" -bool IsMemtracked(int x, int y) { +static inline bool IsMemtrackedImpl(int x, int y) { unsigned i; i = FindMemoryInterval(&_mmi, x); if (i == _mmi.i) return false; @@ -29,3 +29,9 @@ bool IsMemtracked(int x, int y) { if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false; } } + +bool IsMemtracked(int x, int y) { + bool res; + res = IsMemtrackedImpl(x, y); + return res; +} diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index b6ee29e36..ce551b24b 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -46,6 +46,7 @@ struct MemoryIntervals { size_t i, n; struct MemoryInterval *p; struct MemoryInterval s[OPEN_MAX]; + _Alignas(64) char lock; }; extern hidden struct MemoryIntervals _mmi; diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 4e5895277..c929999a8 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -27,6 +27,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/spinlock.h" #include "libc/limits.h" #include "libc/log/backtrace.internal.h" #include "libc/log/libfatal.internal.h" @@ -198,6 +199,133 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size, return addr; } +static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags, + int fd, int64_t off) { +#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE) + if (IsWindows()) { + STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size, + DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off); + } +#endif + char *p = addr; + struct DirectMap dm; + size_t virtualused, virtualneed; + int a, b, i, f, m, n, x; + + if (UNLIKELY(!size)) { + STRACE("size=0"); + return VIP(einval()); + } + + if (UNLIKELY(!IsLegalSize(size))) { + STRACE("size isn't 48-bit"); + return VIP(einval()); + } + + if (UNLIKELY(!IsLegalPointer(p))) { + STRACE("p isn't 48-bit"); + return VIP(einval()); + } + + if (UNLIKELY(!ALIGNED(p))) { + STRACE("p isn't 64kb aligned"); + return VIP(einval()); + } + + if (UNLIKELY(fd < -1)) { + STRACE("mmap(%.12p, %'zu, fd=%d) EBADF", p, size, fd); + return VIP(ebadf()); + } + + if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) { + STRACE("fd anonymous mismatch"); + return VIP(einval()); + } + + if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) { + STRACE("MAP_SHARED ^ MAP_PRIVATE"); + return VIP(einval()); + } + + if (UNLIKELY(off < 0)) { + STRACE("neg off"); + return VIP(einval()); + } + + if (UNLIKELY(INT64_MAX - size < off)) { + STRACE("too large"); + return VIP(einval()); + } + + if (UNLIKELY(!ALIGNED(off))) { + STRACE("p isn't 64kb aligned"); + return VIP(einval()); + } + + if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) { +#ifdef SYSDEBUG + if (OverlapsImageSpace(p, size)) { + STRACE("overlaps image"); + } else { + STRACE("overlaps existing"); + } +#endif + return VIP(efault()); + } + + if (__isfdkind(fd, kFdZip)) { + STRACE("fd is zipos handle"); + return VIP(einval()); + } + + if (__virtualmax < LONG_MAX && + (__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)), size, + &virtualneed) || + virtualneed > __virtualmax)) { + STRACE("%'zu size + %'zu inuse exceeds virtual memory limit %'zu", size, + virtualused, __virtualmax); + return VIP(enomem()); + } + + if (fd == -1) { + size = ROUNDUP(size, FRAMESIZE); + if (IsWindows()) { + prot |= PROT_WRITE; /* kludge */ + } + } + + n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1)); + assert(n > 0); + f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; + if (flags & MAP_FIXED) { + x = FRAME(p); + if (IsWindows()) { + if (UntrackMemoryIntervals(p, size)) { + OnUnrecoverableMmapError("FIXED UNTRACK FAILED"); + } + } + } else if (!NeedAutomap(p, size)) { + x = FRAME(p); + } else if (!Automap(n, &x)) { + STRACE("AUTOMAP OUT OF MEMORY D:"); + return VIP(enomem()); + } + + p = (char *)ADDR(x); + if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ + dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off); + if (dm.addr == MAP_FAILED) { + return MAP_FAILED; + } + } + + if (!IsWindows()) { + return MapMemory(p, size, prot, flags, fd, off, f, x, n); + } else { + return MapMemories(p, size, prot, flags, fd, off, f, x, n); + } +} + /** * Beseeches system for page-table entries, e.g. * @@ -229,100 +357,10 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size, */ noasan void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { -#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE) - if (IsWindows()) { - STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size, - DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off); - } -#endif void *res; - char *p = addr; - struct DirectMap dm; - size_t virtualused, virtualneed; - int a, b, i, f, m, n, x; - if (UNLIKELY(!size)) { - STRACE("size=0"); - res = VIP(einval()); - } else if (UNLIKELY(!IsLegalSize(size))) { - STRACE("size isn't 48-bit"); - res = VIP(einval()); - } else if (UNLIKELY(!IsLegalPointer(p))) { - STRACE("p isn't 48-bit"); - res = VIP(einval()); - } else if (UNLIKELY(!ALIGNED(p))) { - STRACE("p isn't 64kb aligned"); - res = VIP(einval()); - } else if (UNLIKELY(fd < -1)) { - STRACE("mmap(%.12p, %'zu, fd=%d) EBADF", p, size, fd); - res = VIP(ebadf()); - } else if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) { - STRACE("fd anonymous mismatch"); - res = VIP(einval()); - } else if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) { - STRACE("MAP_SHARED ^ MAP_PRIVATE"); - res = VIP(einval()); - } else if (UNLIKELY(off < 0)) { - STRACE("neg off"); - res = VIP(einval()); - } else if (UNLIKELY(INT64_MAX - size < off)) { - STRACE("too large"); - res = VIP(einval()); - } else if (UNLIKELY(!ALIGNED(off))) { - STRACE("p isn't 64kb aligned"); - res = VIP(einval()); - } else if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) { -#ifdef SYSDEBUG - if (OverlapsImageSpace(p, size)) { - STRACE("overlaps image"); - } else { - STRACE("overlaps existing"); - } -#endif - res = VIP(efault()); - } else if (__isfdkind(fd, kFdZip)) { - STRACE("fd is zipos handle"); - res = VIP(einval()); - } else if (__virtualmax < LONG_MAX && - (__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)), - size, &virtualneed) || - virtualneed > __virtualmax)) { - STRACE("%'zu size + %'zu inuse exceeds virtual memory limit %'zu", size, - virtualused, __virtualmax); - res = VIP(enomem()); - } else { - if (fd == -1) { - size = ROUNDUP(size, FRAMESIZE); - if (IsWindows()) { - prot |= PROT_WRITE; /* kludge */ - } - } - n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1)); - assert(n > 0); - f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; - if (flags & MAP_FIXED) { - x = FRAME(p); - if (IsWindows()) { - if (UntrackMemoryIntervals(p, size)) { - OnUnrecoverableMmapError("FIXED UNTRACK FAILED"); - } - } - } else if (!NeedAutomap(p, size)) { - x = FRAME(p); - } else if (!Automap(n, &x)) { - STRACE("AUTOMAP OUT OF MEMORY D:"); - return VIP(enomem()); - } - p = (char *)ADDR(x); - if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ - dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off); - if (dm.addr == MAP_FAILED) res = MAP_FAILED; - } - if (!IsWindows()) { - res = MapMemory(p, size, prot, flags, fd, off, f, x, n); - } else { - res = MapMemories(p, size, prot, flags, fd, off, f, x, n); - } - } + _spinlock(&_mmi.lock); + res = Mmap(addr, size, prot, flags, fd, off); + _spunlock(&_mmi.lock); STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → %p% m", addr, size, DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off, res); return res; diff --git a/libc/runtime/mprotect-nt.greg.c b/libc/runtime/mprotect-nt.greg.c new file mode 100644 index 000000000..5172e6e55 --- /dev/null +++ b/libc/runtime/mprotect-nt.greg.c @@ -0,0 +1,63 @@ +/*-*- 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/intrin/spinlock.h" +#include "libc/nt/memory.h" +#include "libc/runtime/directmap.internal.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/memtrack.internal.h" + +#define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16)) + +textwindows int sys_mprotect_nt(void *addr, size_t size, int prot) { + int rc = 0; + unsigned i; + uint32_t op; + char *a, *b, *x, *y, *p; + _spinlock(&_mmi.lock); + p = addr; + i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16); + if (i == _mmi.i || (!i && p + size <= ADDR(_mmi.p[0].x))) { + // memory isn't in memtrack + // let's just trust the user then + // it's probably part of the executable + if (!VirtualProtect(addr, size, __prot2nt(prot, false), &op)) { + rc = -1; + } + } else { + // memory is in memtrack, so use memtrack, to do dimensioning + // we unfortunately must do something similar to this for cow + for (; i < _mmi.i; ++i) { + x = ADDR(_mmi.p[i].x); + y = x + _mmi.p[i].size; + if ((x <= p && p < y) || (x < p + size && p + size <= y) || + (p < x && y < p + size)) { + a = MIN(MAX(p, x), y); + b = MAX(MIN(p + size, y), x); + if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow), &op)) { + rc = -1; + break; + } + } else { + break; + } + } + } + _spunlock(&_mmi.lock); + return rc; +} diff --git a/libc/runtime/mprotect.greg.c b/libc/runtime/mprotect.greg.c index 0215270ad..14b27b7db 100644 --- a/libc/runtime/mprotect.greg.c +++ b/libc/runtime/mprotect.greg.c @@ -16,26 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/asmflag.h" #include "libc/bits/likely.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/macros.internal.h" -#include "libc/nt/memory.h" -#include "libc/nt/thunk/msabi.h" -#include "libc/runtime/directmap.internal.h" -#include "libc/runtime/memtrack.internal.h" -#include "libc/sysv/consts/nr.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" -__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; - -#define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16)) - /** * Modifies restrictions on virtual memory address range. * @@ -44,12 +34,8 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; * @return 0 on success, or -1 w/ errno * @see mmap() */ -noasan noubsan privileged int mprotect(void *addr, size_t size, int prot) { - bool cf; +privileged int mprotect(void *addr, size_t size, int prot) { int64_t rc; - unsigned i; - uint32_t op; - char *a, *b, *x, *y, *p; if (SupportsWindows() && (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GROWSDOWN | PROT_GROWSUP))) { rc = einval(); // unix checks prot before checking size @@ -58,48 +44,9 @@ noasan noubsan privileged int mprotect(void *addr, size_t size, int prot) { } else if (UNLIKELY((intptr_t)addr & 4095)) { rc = einval(); } else if (!IsWindows()) { - asm volatile(CFLAG_ASM("clc\n\tsyscall") - : CFLAG_CONSTRAINT(cf), "=a"(rc) - : "1"(__NR_mprotect), "D"(addr), "S"(size), "d"(prot) - : "rcx", "r11", "memory", "cc"); - if (cf) { - errno = rc; - rc = -1; - } else if (rc > -4096ul) { - errno = -rc; - rc = -1; - } + rc = sys_mprotect(addr, size, prot); } else { - rc = 0; - p = addr; - i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16); - if (i == _mmi.i || (!i && p + size <= ADDR(_mmi.p[0].x))) { - // memory isn't in memtrack - // let's just trust the user then - // it's probably part of the executable - if (!VirtualProtect(addr, size, __prot2nt(prot, false), &op)) { - rc = -1; - } - } else { - // memory is in memtrack, so use memtrack, to do dimensioning - // we unfortunately must do something similar to this for cow - for (; i < _mmi.i; ++i) { - x = ADDR(_mmi.p[i].x); - y = x + _mmi.p[i].size; - if ((x <= p && p < y) || (x < p + size && p + size <= y) || - (p < x && y < p + size)) { - a = MIN(MAX(p, x), y); - b = MAX(MIN(p + size, y), x); - if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow), - &op)) { - rc = -1; - break; - } - } else { - break; - } - } - } + rc = sys_mprotect_nt(addr, size, prot); } STRACE("mprotect(%p, %'zu, %s) → %d% m", addr, size, DescribeProtFlags(prot), rc); diff --git a/libc/runtime/msync-nt.c b/libc/runtime/msync-nt.c index 55fe5895f..22e680587 100644 --- a/libc/runtime/msync-nt.c +++ b/libc/runtime/msync-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/intrin/spinlock.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" #include "libc/nt/memory.h" @@ -31,6 +32,7 @@ noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { int i, rc = 0; char *a, *b, *x, *y; + _spinlock(&_mmi.lock); for (i = FindMemoryInterval(&_mmi, (intptr_t)addr >> 16); i < _mmi.i; ++i) { x = ADDR(_mmi.p[i].x); y = x + _mmi.p[i].size; @@ -47,30 +49,6 @@ noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { break; } } + _spunlock(&_mmi.lock); return rc; } - -#if 0 -noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) { - char *a, *b; - int rc, x, y, l, r, i; - rc = 0; - for (i = FindMemoryInterval(&_mmi, (intptr_t)addr >> 16); i < _mmi.i; ++i) { - if ((ADDR(_mmi.p[i].x) <= addr && addr < ADDR(_mmi.p[i].y + 1)) || - (ADDR(_mmi.p[i].x) < addr + size && - addr + size <= ADDR(_mmi.p[i].y + 1)) || - (addr < ADDR(_mmi.p[i].x) && ADDR(_mmi.p[i].y + 1) < addr + size)) { - a = MIN(MAX(addr, ADDR(_mmi.p[i].x)), ADDR(_mmi.p[i].y + 1)); - b = MAX(MIN(addr + size, ADDR(_mmi.p[i].y + 1)), ADDR(_mmi.p[i].x)); - if (!FlushViewOfFile(a, b - a)) { - rc = -1; - break; - } - // TODO(jart): FlushFileBuffers too on g_fds handle if MS_SYNC? - } else { - break; - } - } - return rc; -} -#endif diff --git a/libc/runtime/munmap.c b/libc/runtime/munmap.c index 47e2a51fe..19146c221 100644 --- a/libc/runtime/munmap.c +++ b/libc/runtime/munmap.c @@ -22,6 +22,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/directmap.internal.h" @@ -35,6 +36,76 @@ #define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) #define FRAME(x) ((int)((intptr_t)(x) >> 16)) +static noasan int Munmap(void *v, size_t n) { + char poison, *p = v; + intptr_t a, b, x, y; + assert(!__vforked); + + if (UNLIKELY(!n)) { + STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n); + return einval(); + } + + if (UNLIKELY(!IsLegalSize(n))) { + STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n); + return einval(); + } + + if (UNLIKELY(!IsLegalPointer(p))) { + STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n); + return einval(); + } + + if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) { + STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n); + return einval(); + } + + if (UNLIKELY(!ALIGNED(p))) { + STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n); + return einval(); + } + + if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) { + STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n); + return efault(); + } + + if (UntrackMemoryIntervals(p, n) == -1) { + return -1; + } + + if (IsWindows()) { + return 0; // UntrackMemoryIntervals does it for NT + } + + if (sys_munmap(p, n) == -1) { + return -1; // ouch + } + + if (IsAsan() && !OverlapsShadowSpace(p, n)) { + a = ((intptr_t)p >> 3) + 0x7fff8000; + b = a + (n >> 3); + if (IsMemtracked(FRAME(a), FRAME(b - 1))) { + x = ROUNDUP(a, FRAMESIZE); + y = ROUNDDOWN(b, FRAMESIZE); + if (x < y) { + // delete shadowspace if unmapping ≥512kb + __repstosb((void *)a, kAsanUnmapped, x - a); + Munmap((void *)x, y - x); + __repstosb((void *)y, kAsanUnmapped, b - y); + } else { + // otherwise just poison and assume reuse + __repstosb((void *)a, kAsanUnmapped, b - a); + } + } else { + STRACE("unshadow(%.12p, %p) EFAULT", a, b - a); + } + } + + return 0; +} + /** * Releases memory pages. * @@ -49,66 +120,11 @@ * and for files size needs to be perfect to the byte bc openbsd * @return 0 on success, or -1 w/ errno */ -noasan int munmap(void *v, size_t n) { - /* asan runtime depends on this function */ +noasan int munmap(void *p, size_t n) { int rc; - char poison, *p = v; - intptr_t a, b, x, y; - assert(!__vforked); - if (UNLIKELY(!n)) { - STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n); - return einval(); - } - if (UNLIKELY(!IsLegalSize(n))) { - STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n); - return einval(); - } - if (UNLIKELY(!IsLegalPointer(p))) { - STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n); - return einval(); - } - if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) { - STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n); - return einval(); - } - if (UNLIKELY(!ALIGNED(p))) { - STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n); - return einval(); - } - if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) { - STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n); - return efault(); - } - if (UntrackMemoryIntervals(p, n) != -1) { - if (!IsWindows()) { - rc = sys_munmap(p, n); - if (rc != -1) { - if (IsAsan() && !OverlapsShadowSpace(p, n)) { - a = ((intptr_t)p >> 3) + 0x7fff8000; - b = a + (n >> 3); - if (IsMemtracked(FRAME(a), FRAME(b - 1))) { - x = ROUNDUP(a, FRAMESIZE); - y = ROUNDDOWN(b, FRAMESIZE); - if (x < y) { - /* delete shadowspace if unmapping ≥512kb */ - __repstosb((void *)a, kAsanUnmapped, x - a); - munmap((void *)x, y - x); - __repstosb((void *)y, kAsanUnmapped, b - y); - } else { - /* otherwise just poison and assume reuse */ - __repstosb((void *)a, kAsanUnmapped, b - a); - } - } else { - STRACE("unshadow(%.12p, %p) EFAULT", a, b - a); - } - } - } - } else { - rc = 0; /* UntrackMemoryIntervals does it for NT */ - } - } else { - rc = -1; - } + _spinlock(&_mmi.lock); + rc = Munmap(p, n); + _spunlock(&_mmi.lock); STRACE("munmap(%.12p, %'zu) → %d% m", p, n, rc); return rc; } diff --git a/libc/runtime/paginate.c b/libc/runtime/paginate.c new file mode 100644 index 000000000..c37a00e0b --- /dev/null +++ b/libc/runtime/paginate.c @@ -0,0 +1,57 @@ +/*-*- 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/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/fmt/fmt.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/x/x.h" + +/** + * Displays wall of text in terminal with pagination. + */ +void __paginate(int fd, const char *s) { + int tfd, pid; + char *args[3] = {0}; + char tmppath[PATH_MAX]; + char progpath[PATH_MAX]; + if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && + ((args[0] = commandv("less", progpath, sizeof(progpath))) || + (args[0] = commandv("more", progpath, sizeof(progpath))))) { + snprintf(tmppath, sizeof(tmppath), "%s%s-%s-%d.txt", kTmpPath, + program_invocation_short_name, "paginate", getpid()); + if ((tfd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0644)) != -1) { + write(tfd, s, strlen(s)); + close(tfd); + args[1] = tmppath; + if ((pid = vfork()) != -1) { + if (!pid) { + execv(args[0], args); + _Exit(127); + } + waitpid(pid, 0, 0); + unlink(tmppath); + return; + } + unlink(tmppath); + } + } + write(fd, s, strlen(s)); +} diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 10e438f7a..8f18b5f02 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -44,6 +44,7 @@ #include "libc/runtime/stack.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" +#include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/poll.h" @@ -52,12 +53,13 @@ #include "tool/decode/lib/idname.h" #include "tool/decode/lib/x86idnames.h" +STATIC_YOINK("strerror"); // for kprintf() STATIC_YOINK("strsignal"); // for kprintf() #define PRINT(FMT, ...) \ do { \ kprintf(prologue); \ - kprintf(FMT "%n", ##__VA_ARGS__); \ + kprintf(FMT "\n", ##__VA_ARGS__); \ } while (0) static const struct AuxiliaryValue { @@ -137,11 +139,14 @@ textstartup void __printargs(const char *prologue) { unsigned i, n; uintptr_t *auxp; struct utsname uts; - char path[PATH_MAX]; struct termios termios; int e, x, st, ft, flags; - struct pollfd pfds[128]; struct AuxiliaryValue *auxinfo; + union { + char path[PATH_MAX]; + struct pollfd pfds[128]; + } u; + st = __strace, __strace = 0; ft = g_ftrace, g_ftrace = 0; e = errno; @@ -157,7 +162,7 @@ textstartup void __printargs(const char *prologue) { kprintf(" %s", uts.release); } } - kprintf("%n"); + kprintf("\n"); } else { PRINT(" uname() failed %m"); } @@ -177,7 +182,7 @@ textstartup void __printargs(const char *prologue) { FindNameById(kX86GradeNames, getx86processormodel(kX86ProcessorModelKey)->grade)); } - kprintf("%n"); + kprintf("\n"); if ((x = KCPUIDS(16H, EAX) & 0x7fff)) { kprintf(prologue); kprintf(" %dmhz %s", x, "freq"); @@ -187,7 +192,7 @@ textstartup void __printargs(const char *prologue) { if ((x = KCPUIDS(16H, ECX) & 0x7fff)) { kprintf(" / %dmhz %s", x, "bus"); } - kprintf("%n"); + kprintf("\n"); } if (X86_HAVE(HYPERVISOR)) { unsigned eax, ebx, ecx, edx; @@ -203,8 +208,9 @@ textstartup void __printargs(const char *prologue) { PRINT(" L%d%s%s %u-way %,u byte cache w/%s " "%,u sets of %,u byte lines shared across %u threads%s", CPUID4_CACHE_LEVEL, - CPUID4_CACHE_TYPE == 1 ? " data" - : CPUID4_CACHE_TYPE == 2 ? " code" : "", + CPUID4_CACHE_TYPE == 1 ? " data" + : CPUID4_CACHE_TYPE == 2 ? " code" + : "", CPUID4_IS_FULLY_ASSOCIATIVE ? " fully-associative" : "", CPUID4_WAYS_OF_ASSOCIATIVITY, CPUID4_CACHE_SIZE_IN_BYTES, CPUID4_PHYSICAL_LINE_PARTITIONS > 1 ? " physically partitioned" : "", @@ -233,19 +239,19 @@ textstartup void __printargs(const char *prologue) { if (X86_HAVE(RDPID)) kprintf(" RDPID"); if (X86_HAVE(LA57)) kprintf(" LA57"); if (X86_HAVE(FSGSBASE)) kprintf(" FSGSBASE"); - kprintf("%n"); + kprintf("\n"); PRINT(""); PRINT("FILE DESCRIPTORS"); - for (i = 0; i < ARRAYLEN(pfds); ++i) { - pfds[i].fd = i; - pfds[i].events = POLLIN; + for (i = 0; i < ARRAYLEN(u.pfds); ++i) { + u.pfds[i].fd = i; + u.pfds[i].events = POLLIN; } - if ((n = poll(pfds, ARRAYLEN(pfds), 0)) != -1) { - for (i = 0; i < ARRAYLEN(pfds); ++i) { - if (i && (pfds[i].revents & POLLNVAL)) continue; + if ((n = poll(u.pfds, ARRAYLEN(u.pfds), 0)) != -1) { + for (i = 0; i < ARRAYLEN(u.pfds); ++i) { + if (i && (u.pfds[i].revents & POLLNVAL)) continue; PRINT(" ☼ %d (revents=%#hx fcntl(F_GETFL)=%#x isatty()=%hhhd)", i, - pfds[i].revents, fcntl(i, F_GETFL), isatty(i)); + u.pfds[i].revents, fcntl(i, F_GETFL), isatty(i)); } } else { PRINT(" poll() returned %d %m", n); @@ -295,8 +301,8 @@ textstartup void __printargs(const char *prologue) { if (*__auxv) { for (auxp = __auxv; *auxp; auxp += 2) { if ((auxinfo = DescribeAuxv(auxp[0]))) { - ksnprintf(path, sizeof(path), auxinfo->fmt, auxp[1]); - PRINT(" ☼ %16s[%4ld] = %s", auxinfo->name, auxp[0], path); + ksnprintf(u.path, sizeof(u.path), auxinfo->fmt, auxp[1]); + PRINT(" ☼ %16s[%4ld] = %s", auxinfo->name, auxp[0], u.path); } else { PRINT(" ☼ %16s[%4ld] = %014p", "unknown", auxp[0], auxp[1]); } @@ -321,9 +327,9 @@ textstartup void __printargs(const char *prologue) { PRINT(" ☼ %s = %#s", "kTmpPath", kTmpPath); PRINT(" ☼ %s = %#s", "kNtSystemDirectory", kNtSystemDirectory); PRINT(" ☼ %s = %#s", "kNtWindowsDirectory", kNtWindowsDirectory); - PRINT(" ☼ %s = %#s", "program_executable_name", GetProgramExecutableName()); - PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName()", - GetInterpreterExecutableName(path, sizeof(path))); + PRINT(" ☼ %s = %#s", "GetProgramExecutableName", GetProgramExecutableName()); + PRINT(" ☼ %s = %#s", "GetInterpreterExecutableName", + GetInterpreterExecutableName(u.path, sizeof(u.path))); PRINT(" ☼ %s = %p", "RSP", __builtin_frame_address(0)); PRINT(" ☼ %s = %p", "GetStackAddr()", GetStackAddr(0)); PRINT(" ☼ %s = %p", "GetStaticStackAddr(0)", GetStaticStackAddr(0)); @@ -355,7 +361,7 @@ textstartup void __printargs(const char *prologue) { if (termios.c_iflag & IMAXBEL) kprintf(" IMAXBEL"); if (termios.c_iflag & IUTF8) kprintf(" IUTF8"); if (termios.c_iflag & IUCLC) kprintf(" IUCLC"); - kprintf("%n"); + kprintf("\n"); kprintf(prologue); kprintf(" c_oflag ="); if (termios.c_oflag & OPOST) kprintf(" OPOST"); @@ -408,15 +414,15 @@ textstartup void __printargs(const char *prologue) { } else if ((termios.c_oflag & FFDLY) == FF1) { kprintf(" FF1"); } - kprintf("%n"); + kprintf("\n"); kprintf(prologue); kprintf(" c_cflag ="); - if (termios.c_cflag & ISIG) kprintf(" ISIG"); - if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB"); - if (termios.c_cflag & CREAD) kprintf(" CREAD"); if (termios.c_cflag & PARENB) kprintf(" PARENB"); if (termios.c_cflag & PARODD) kprintf(" PARODD"); + if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB"); + if (termios.c_cflag & PARODD) kprintf(" PARODD"); if (termios.c_cflag & HUPCL) kprintf(" HUPCL"); + if (termios.c_cflag & CREAD) kprintf(" CREAD"); if (termios.c_cflag & CLOCAL) kprintf(" CLOCAL"); if ((termios.c_cflag & CSIZE) == CS5) { kprintf(" CS5"); @@ -427,7 +433,7 @@ textstartup void __printargs(const char *prologue) { } else if ((termios.c_cflag & CSIZE) == CS8) { kprintf(" CS8"); } - kprintf("%n"); + kprintf("\n"); kprintf(prologue); kprintf(" c_lflag ="); if (termios.c_lflag & ISIG) kprintf(" ISIG"); @@ -445,7 +451,7 @@ textstartup void __printargs(const char *prologue) { if (termios.c_lflag & FLUSHO) kprintf(" FLUSHO"); if (termios.c_lflag & PENDIN) kprintf(" PENDIN"); if (termios.c_lflag & XCASE) kprintf(" XCASE"); - kprintf("%n"); + kprintf("\n"); PRINT(" c_ispeed = %u", termios.c_ispeed); PRINT(" c_ospeed = %u", termios.c_ospeed); PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR])); diff --git a/libc/runtime/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c index d3b5ae1db..2fdfebce4 100644 --- a/libc/runtime/printmemoryintervals.c +++ b/libc/runtime/printmemoryintervals.c @@ -54,7 +54,7 @@ void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) { if (mm->p[i].h != -1) { kprintf(" h=%ld", mm->p[i].h); } - kprintf("%n"); + kprintf("\n"); } - kprintf("# %ld frames mapped w/ %'ld frames gapped%n", maptally, gaptally); + kprintf("# %ld frames mapped w/ %'ld frames gapped\n", maptally, gaptally); } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 0604d98dd..989c6b2de 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -14,7 +14,6 @@ extern char **__argv; /* CRT */ extern char **__envp; /* CRT */ extern unsigned long *__auxv; /* CRT */ extern intptr_t __oldstack; /* CRT */ -extern char program_executable_name[]; /* RII */ extern char *program_invocation_name; /* RII */ extern char *program_invocation_short_name; /* RII */ extern int g_ftrace; /* CRT */ @@ -104,6 +103,8 @@ long GetMaxFd(void); char *GetProgramExecutableName(void); char *GetInterpreterExecutableName(char *, size_t); void __printargs(const char *); +void __paginate(int, const char *); +int __arg_max(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index db205c38b..1892b2408 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -69,10 +69,12 @@ o/$(MODE)/libc/runtime/ezmap.o \ o/$(MODE)/libc/runtime/getdosargv.o \ o/$(MODE)/libc/runtime/getdosenviron.o \ o/$(MODE)/libc/runtime/hook.greg.o \ +o/$(MODE)/libc/runtime/mprotect.greg.o \ +o/$(MODE)/libc/runtime/mprotect-nt.greg.o \ +o/$(MODE)/libc/runtime/ismemtracked.greg.o \ o/$(MODE)/libc/runtime/isheap.o \ o/$(MODE)/libc/runtime/memtracknt.o \ o/$(MODE)/libc/runtime/memtrack.greg.o \ -o/$(MODE)/libc/runtime/ismemtracked.greg.o \ o/$(MODE)/libc/runtime/metalprintf.greg.o \ o/$(MODE)/libc/runtime/printargs.greg.o \ o/$(MODE)/libc/runtime/mman.greg.o \ diff --git a/libc/runtime/sysconf.c b/libc/runtime/sysconf.c index cec42e6ad..6c685d528 100644 --- a/libc/runtime/sysconf.c +++ b/libc/runtime/sysconf.c @@ -22,6 +22,7 @@ #include "libc/macros.internal.h" #include "libc/runtime/clktck.h" #include "libc/runtime/sysconf.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/rlimit.h" @@ -42,7 +43,7 @@ long sysconf(int name) { int n; switch (name) { case _SC_ARG_MAX: - return ARG_MAX; + return _ARG_MAX; case _SC_CHILD_MAX: return GetResourceLimit(RLIMIT_NPROC); case _SC_CLK_TCK: diff --git a/libc/runtime/vfork.S b/libc/runtime/vfork.S index c10080021..b09a0ff36 100644 --- a/libc/runtime/vfork.S +++ b/libc/runtime/vfork.S @@ -94,7 +94,7 @@ vfork.bsd: #ifdef SYSDEBUG .rodata.str1.1 .Llog: .ascii STRACE_PROLOGUE - .asciz "vfork()%n" + .asciz "vfork()\n" .previous #endif /* DEBUGSYS */ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 6208d6d69..21a40d890 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -55,6 +55,7 @@ #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/internal.h" +#include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" @@ -83,8 +84,8 @@ struct WinArgs { char *argv[4096]; char *envp[4092]; intptr_t auxv[2][2]; - char argblock[ARG_MAX]; - char envblock[ARG_MAX]; + char argblock[ARG_MAX / 2]; + char envblock[ARG_MAX / 2]; }; extern uint32_t __winmainpid; @@ -120,6 +121,15 @@ forceinline void MakeLongDoubleLongAgain(void) { asm volatile("fldcw\t%0" : /* no outputs */ : "m"(x87cw)); } +static inline size_t StrLen16(const char16_t *s) { + size_t n; + for (n = 0;; ++n) { + if (!s[n]) { + return n; + } + } +} + __msabi static textwindows int OnEarlyWinCrash(struct NtExceptionPointers *ep) { uint32_t wrote; char buf[64], *p = buf; @@ -257,7 +267,6 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, extern uint64_t ts asm("kStartTsc"); os = WINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */ ts = rdtsc(); - __nomultics = true; __pid = GetCurrentProcessId(); __wincrashearly = AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash); cmdline = GetCommandLine(); diff --git a/libc/sock/describesocklevel.greg.c b/libc/sock/describesocklevel.greg.c new file mode 100644 index 000000000..7dd91118b --- /dev/null +++ b/libc/sock/describesocklevel.greg.c @@ -0,0 +1,33 @@ +/*-*- 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/sysv/consts/sol.h" + +/** + * Describes setsockopt() level arguments. + */ +char *DescribeSockLevel(int x) { + static char buf[12]; + if (x == SOL_IP) return "SOL_IP"; + if (x == SOL_TCP) return "SOL_TCP"; + if (x == SOL_UDP) return "SOL_UDP"; + if (x == SOL_SOCKET) return "SOL_SOCKET"; + FormatInt32(buf, x); + return buf; +} diff --git a/libc/sock/describesockoptname.greg.c b/libc/sock/describesockoptname.greg.c new file mode 100644 index 000000000..09213b2a0 --- /dev/null +++ b/libc/sock/describesockoptname.greg.c @@ -0,0 +1,51 @@ +/*-*- 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 setsockopt() optname arguments. + */ +char *DescribeSockOptname(int l, int x) { + int i; + 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))) { + 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 49ccc4ee7..d64f6c5f6 100644 --- a/libc/sock/getsockopt-nt.c +++ b/libc/sock/getsockopt-nt.c @@ -17,20 +17,62 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#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" +#include "libc/sysv/consts/sol.h" #include "libc/sysv/errfuns.h" textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname, - void *out_opt_optval, uint32_t *out_optlen) { - /* TODO(jart): Use WSAIoctl? */ + void *out_opt_optval, + uint32_t *inout_optlen) { + uint64_t ms; + uint32_t in_optlen; + struct linger_nt linger; assert(fd->kind == kFdSocket); - if (__sys_getsockopt_nt(fd->handle, level, optname, out_opt_optval, - out_optlen) != -1) { - return 0; + + if (out_opt_optval && inout_optlen) { + in_optlen = *inout_optlen; } else { + in_optlen = 0; + } + + // TODO(jart): Use WSAIoctl? + if (__sys_getsockopt_nt(fd->handle, level, optname, out_opt_optval, + inout_optlen) == -1) { return __winsockerr(); } + + if (level == SOL_SOCKET) { + if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && + in_optlen == sizeof(struct timeval) && + *inout_optlen == sizeof(uint32_t)) { + ms = *(uint32_t *)out_opt_optval; + ((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); + } + } + + if (in_optlen == 4 && *inout_optlen == 1) { + // handle cases like this + // getsockopt(8, SOL_TCP, TCP_FASTOPEN, [u"☺"], [1]) → 0 + int32_t wut = *(signed char *)out_opt_optval; + memcpy(out_opt_optval, &wut, 4); + *inout_optlen = 4; + } + + return 0; } diff --git a/libc/sock/getsockopt.c b/libc/sock/getsockopt.c index 067c05267..d60d8d601 100644 --- a/libc/sock/getsockopt.c +++ b/libc/sock/getsockopt.c @@ -19,6 +19,8 @@ #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/sock/internal.h" #include "libc/sock/sock.h" #include "libc/sysv/errfuns.h" @@ -36,10 +38,15 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval, uint32_t *out_optlen) { int rc; + if (!level || !optname) { rc = enoprotoopt(); /* our sysvconsts definition */ } else if (optname == -1) { rc = 0; /* our sysvconsts definition */ + } else if (IsAsan() && (out_opt_optval && out_optlen && + (!__asan_is_valid(out_optlen, sizeof(uint32_t)) || + !__asan_is_valid(out_opt_optval, *out_optlen)))) { + rc = efault(); } else if (!IsWindows()) { rc = sys_getsockopt(fd, level, optname, out_opt_optval, out_optlen); } else if (__isfdkind(fd, kFdSocket)) { @@ -48,7 +55,18 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval, } else { rc = ebadf(); } - STRACE("getsockopt(%d, %#x, %#x, %p, %p) → %d% lm", fd, level, optname, - out_opt_optval, out_optlen, rc); + +#ifdef SYSDEBUG + if (out_opt_optval && out_optlen && rc != -1) { + STRACE("getsockopt(%d, %s, %s, [%#.*hhs], [%d]) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + *out_optlen, out_opt_optval, *out_optlen, rc); + } else { + STRACE("getsockopt(%d, %s, %s, %p, %p) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + out_opt_optval, out_optlen, rc); + } +#endif + return rc; } diff --git a/libc/calls/nowl.S b/libc/sock/kipoptnames.S similarity index 79% rename from libc/calls/nowl.S rename to libc/sock/kipoptnames.S index 9c2d8bad0..cee04e63c 100644 --- a/libc/calls/nowl.S +++ b/libc/sock/kipoptnames.S @@ -1,7 +1,7 @@ /*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,22 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/nexgen32e/x86feature.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" -// Returns timestamp without needing system calls. -// -// @return seconds since unix epoch in %st0 -// @note uses microsecond scale fallback on k8 or vm - .initbss 202,_init_nowl -nowl: .quad 0 - .endobj nowl,globl + .macro .e e s + .long \e - kIpOptnames + .long 1f - kIpOptnames + .rodata.str1.1 +1: .string "\s" .previous + .endm - .init.start 202,_init_nowl - ezlea nowl_sys,ax - ezlea nowl_art,cx - testb X86_HAVE(INVTSC)+kCpuids(%rip) - cmovnz %rcx,%rax - stosq - .init.end 202,_init_nowl + .section .rodata + .align 4 + .underrun +kIpOptnames: + .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 new file mode 100644 index 000000000..8b29764a0 --- /dev/null +++ b/libc/sock/ksockoptnames.S @@ -0,0 +1,52 @@ +/*-*- 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 - kSockOptnames + .long 1f - kSockOptnames + .rodata.str1.1 +1: .string "\s" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kSockOptnames: + .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 new file mode 100644 index 000000000..444b35922 --- /dev/null +++ b/libc/sock/ktcpoptnames.S @@ -0,0 +1,49 @@ +/*-*- 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 - kTcpOptnames + .long 1f - kTcpOptnames + .rodata.str1.1 +1: .string "\s" + .previous + .endm + + .section .rodata + .align 4 + .underrun +kTcpOptnames: + .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/recvmsg.c b/libc/sock/recvmsg.c index 137b6e74f..c688f808f 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -88,7 +88,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags); kprintf(".iov=", fd); __strace_iov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); - kprintf("}], %#x) → %'ld% m%n", flags, rc); + kprintf("}], %#x) → %'ld% m\n", flags, rc); } } #endif diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c index d338ded3e..3c8545229 100644 --- a/libc/sock/sendmsg.c +++ b/libc/sock/sendmsg.c @@ -90,7 +90,7 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags); kprintf(".iov=", fd); __strace_iov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0); - kprintf("}, %#x) → %'ld% m%n", flags, rc); + kprintf("}, %#x) → %'ld% m\n", flags, rc); } } #endif diff --git a/libc/sock/setsockopt-nt.c b/libc/sock/setsockopt-nt.c index ed31ffda5..4f637d8cd 100644 --- a/libc/sock/setsockopt-nt.c +++ b/libc/sock/setsockopt-nt.c @@ -17,39 +17,44 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #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; struct timeval *tv; struct linger *linger; union { uint32_t millis; struct linger_nt linger; - } nt; + } u; - if (optname == SO_LINGER && optval && optlen == sizeof(struct linger)) { - linger = optval; - nt.linger.l_onoff = linger->l_onoff; - nt.linger.l_linger = MIN(0xFFFF, MAX(0, linger->l_linger)); - optval = &nt.linger; - optlen = sizeof(nt.linger); - } - - if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && optval && - optlen == sizeof(struct timeval)) { - tv = optval; - nt.millis = MIN(0xFFFFFFFF, MAX(0, tv->tv_sec * 1000 + tv->tv_usec / 1000)); - optval = &nt.millis; - optlen = sizeof(nt.millis); + if (level == SOL_SOCKET) { + if (optname == SO_LINGER && optval && optlen == sizeof(struct linger)) { + linger = optval; + u.linger.l_onoff = linger->l_onoff; + u.linger.l_linger = MIN(0xFFFF, MAX(0, linger->l_linger)); + optval = &u.linger; + optlen = sizeof(u.linger); + } else if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && optval && + optlen == sizeof(struct timeval)) { + tv = optval; + if (__builtin_mul_overflow(tv->tv_sec, 1000, &ms) || + __builtin_add_overflow(ms, tv->tv_usec / 1000, &ms) || + (ms < 0 || ms > 0xffffffff)) { + u.millis = 0xffffffff; + } else { + u.millis = ms; + } + optval = &u.millis; + optlen = sizeof(u.millis); + } } if (__sys_setsockopt_nt(fd->handle, level, optname, optval, optlen) != -1) { diff --git a/libc/sock/setsockopt.c b/libc/sock/setsockopt.c index a9e831b48..c66b078eb 100644 --- a/libc/sock/setsockopt.c +++ b/libc/sock/setsockopt.c @@ -20,6 +20,8 @@ #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/asan.internal.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" @@ -52,12 +54,12 @@ static bool setsockopt_polyfill(int *optname) { int setsockopt(int fd, int level, int optname, const void *optval, uint32_t optlen) { int e, rc; - if (!optval) { + + if (!optname) { + rc = enosys(); /* see libc/sysv/consts.sh */ + } else if ((!optval && optlen) || + (IsAsan() && !__asan_is_valid(optval, optlen))) { rc = efault(); - } else if (!level || !optname) { - rc = enoprotoopt(); /* our sysvconsts definition */ - } else if (optname == -1) { - rc = 0; /* our sysvconsts definition */ } else if (!IsWindows()) { rc = -1; e = errno; @@ -73,7 +75,18 @@ int setsockopt(int fd, int level, int optname, const void *optval, } else { rc = ebadf(); } - STRACE("setsockopt(%d, %#x, %#x, %p, %'u) → %d% lm", fd, level, optname, - optval, optlen, rc); + +#ifdef SYSDEBUG + if (!(rc == -1 && errno == EFAULT)) { + STRACE("setsockopt(%d, %s, %s, %#.*hhs, %'u) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + optlen, optval, optlen, rc); + } else { + STRACE("setsockopt(%d, %s, %s, %p, %'u) → %d% lm", fd, + DescribeSockLevel(level), DescribeSockOptname(level, optname), + optval, optlen, rc); + } +#endif + return rc; } diff --git a/libc/sock/sock.mk b/libc/sock/sock.mk index e8d40fa58..4ffe02852 100644 --- a/libc/sock/sock.mk +++ b/libc/sock/sock.mk @@ -9,10 +9,16 @@ LIBC_SOCK_A = o/$(MODE)/libc/sock/sock.a LIBC_SOCK_A_FILES := $(wildcard libc/sock/*) LIBC_SOCK_A_HDRS = $(filter %.h,$(LIBC_SOCK_A_FILES)) LIBC_SOCK_A_INCS = $(filter %.inc,$(LIBC_SOCK_A_FILES)) -LIBC_SOCK_A_SRCS = $(filter %.c,$(LIBC_SOCK_A_FILES)) +LIBC_SOCK_A_SRCS_C = $(filter %.c,$(LIBC_SOCK_A_FILES)) +LIBC_SOCK_A_SRCS_S = $(filter %.S,$(LIBC_SOCK_A_FILES)) + +LIBC_SOCK_A_SRCS = \ + $(LIBC_SOCK_A_SRCS_C) \ + $(LIBC_SOCK_A_SRCS_S) LIBC_SOCK_A_OBJS = \ - $(LIBC_SOCK_A_SRCS:%.c=o/$(MODE)/%.o) + $(LIBC_SOCK_A_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(LIBC_SOCK_A_SRCS_S:%.S=o/$(MODE)/%.o) LIBC_SOCK_A_CHECKS = \ $(LIBC_SOCK_A).pkg \ diff --git a/libc/sock/sockdebug.c b/libc/sock/sockdebug.c index aa9351f53..ff52cf07b 100644 --- a/libc/sock/sockdebug.c +++ b/libc/sock/sockdebug.c @@ -48,6 +48,10 @@ const char *__describe_socket_type(int type) { p = stpcpy(p, "SOCK_DGRAM"); } else if (x == SOCK_RAW) { p = stpcpy(p, "SOCK_RAW"); + } else if (x == SOCK_RDM) { + p = stpcpy(p, "SOCK_RDM"); + } else if (x == SOCK_SEQPACKET) { + p = stpcpy(p, "SOCK_SEQPACKET"); } else { p = FormatInt32(p, x); } diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index 54e3a2cf3..5dcffa13a 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -32,10 +32,6 @@ * - `%m` inserts strerror(errno) into the formatted output. This is * consistent with glibc, musl, and uclibc. * - * - `%n` inserts "\n" on non-Windows and "\r\n" on Windows. This is the - * same behavior as Java. It's incompatible with glibc's behavior, - * since that's problematic according to Android's security team. - * * - `%hs` converts UTF-16/UCS-2 → UTF-8, which can be helpful on Windows. * Formatting (e.g. %-10hs) will use monospace display width rather * than string length or codepoint count. diff --git a/libc/stdio/spawn.c b/libc/stdio/spawn.c index 2ec52e244..de6cfa921 100644 --- a/libc/stdio/spawn.c +++ b/libc/stdio/spawn.c @@ -77,7 +77,7 @@ int posix_spawn(int *pid, const char *path, if (sscanf(p + 5, "%d,", &fd) != 1) _Exit(127); p = strchr(p, ',') + 1; q = strchr(p, '*'); - if (!q || q - p + 1 > PATH_MAX) _Exit(127); + if (!q || q - p >= PATH_MAX) _Exit(127); strncpy(opath, p, q - p); opath[q - p] = '\0'; if (sscanf(q + 1, "%o,%o)", &oflag, &mode) != 2) _Exit(127); diff --git a/libc/stdio/spawnp.c b/libc/stdio/spawnp.c index 2191b3c99..67b9d0d67 100644 --- a/libc/stdio/spawnp.c +++ b/libc/stdio/spawnp.c @@ -32,6 +32,6 @@ int posix_spawnp(int *pid, const char *path, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { char pathbuf[PATH_MAX]; - if (!(path = commandv(path, pathbuf))) return errno; + if (!(path = commandv(path, pathbuf, sizeof(pathbuf)))) return errno; return posix_spawn(pid, path, file_actions, attrp, argv, envp); } 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/stdio/systemexec.c b/libc/stdio/systemexec.c index 70134991a..5bda2ef06 100644 --- a/libc/stdio/systemexec.c +++ b/libc/stdio/systemexec.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/macros.internal.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -29,14 +30,16 @@ */ int systemexec(const char *cmdline) { size_t n, m; - char *a, *b, *argv[4], comspec[PATH_MAX + 1]; + char *a, *b, *argv[4], comspec[PATH_MAX]; if (!IsWindows()) { argv[0] = _PATH_BSHELL; argv[1] = "-c"; } else { b = "cmd.exe"; a = kNtSystemDirectory; - if ((n = strlen(a)) + (m = strlen(b)) > PATH_MAX) return enametoolong(); + if ((n = strlen(a)) + (m = strlen(b)) >= ARRAYLEN(comspec)) { + return enametoolong(); + } memcpy(mempcpy(comspec, a, n), b, m + 1); argv[0] = comspec; argv[1] = "/C"; diff --git a/libc/stdio/tmpfile.c b/libc/stdio/tmpfile.c index 67e54721a..8ec1a985a 100644 --- a/libc/stdio/tmpfile.c +++ b/libc/stdio/tmpfile.c @@ -36,7 +36,7 @@ FILE *tmpfile(void) { tmp = firstnonnull(getenv("TMPDIR"), kTmpPath); sep = !isempty(tmp) && !endswith(tmp, "/") ? "/" : ""; if ((snprintf)(tpl, PATH_MAX, "%s%stmp.%s.XXXXXX", tmp, sep, - program_invocation_short_name) < PATH_MAX) { + program_invocation_short_name) <= PATH_MAX) { if ((fd = mkostemps(tpl, 0, 0)) != -1) { return fdopen(fd, "w+"); } diff --git a/libc/str/kstrsignal.S b/libc/str/ksignalnames.S similarity index 94% rename from libc/str/kstrsignal.S rename to libc/str/ksignalnames.S index 838354752..0e58a1b70 100644 --- a/libc/str/kstrsignal.S +++ b/libc/str/ksignalnames.S @@ -16,11 +16,12 @@ │ 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 - kStrSignal - .long 1f - kStrSignal + .long \e - kSignalNames + .long 1f - kSignalNames .rodata.str1.1 1: .string "\s" .previous @@ -29,7 +30,7 @@ .section .rodata .align 4 .underrun -kStrSignal: +kSignalNames: .e SIGHUP,"HUP" .e SIGINT,"INT" .e SIGQUIT,"QUIT" @@ -65,6 +66,6 @@ kStrSignal: .e SIGRTMIN,"RTMIN" .e SIGEMT,"EMT" .e SIGPWR,"PWR" - .long 0 - .endobj kStrSignal,globl,hidden + .long MAGNUM_TERMINATOR + .endobj kSignalNames,globl,hidden .overrun diff --git a/libc/str/longsort.c b/libc/str/longsort.c index bdf420b81..fcbb9e366 100644 --- a/libc/str/longsort.c +++ b/libc/str/longsort.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/macros.internal.h" @@ -80,4 +81,7 @@ void longsort(long *x, size_t n) { longsort_pure(x, n, t); } } + if (n > 1000) { + STRACE("longsort(%p, %'zu)", x, n); + } } diff --git a/libc/str/qsort.c b/libc/str/qsort.c index d280a394b..d0f112e07 100644 --- a/libc/str/qsort.c +++ b/libc/str/qsort.c @@ -53,7 +53,7 @@ static inline int pntz(size_t p[2]) { return 0; } -/* smoothsort_shl() and smoothsort_shr() need n > 0 */ +// smoothsort_shl() and smoothsort_shr() need n > 0 static inline void smoothsort_shl(size_t p[2], int n) { if (n >= CHAR_BIT * sizeof(size_t)) { n -= CHAR_BIT * sizeof(size_t); @@ -163,7 +163,7 @@ static void smoothsort(struct SmoothSort *s, void *base, size_t nel, if (!size) return; head = base; high = head + size - width; - /* Precompute Leonardo numbers, scaled by element width */ + // precompute Leonardo numbers, scaled by element width for (s->lp[0] = s->lp[1] = width, i = 2; (s->lp[i] = s->lp[i - 2] + s->lp[i - 1] + width) < size; i++) { } diff --git a/libc/str/str.h b/libc/str/str.h index 3b06b430b..fbf461e9a 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -192,7 +192,6 @@ wchar_t *wmempcpy(wchar_t *, const wchar_t *, size_t) memcpyesque; wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t) memcpyesque; void *tinymemccpy(void *, const void *, int, size_t) memcpyesque; void *memmem(const void *, size_t, const void *, size_t) libcesque nosideeffect; -char *strerror(int) returnsnonnull dontthrow nocallback; long a64l(const char *); char *l64a(long); @@ -262,14 +261,12 @@ wint_t towctrans(wint_t, wctrans_t); ╚────────────────────────────────────────────────────────────────────────────│*/ char *strsignal(int) returnsnonnull libcesque; +char *strerror(int) returnsnonnull dontthrow nocallback; +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; -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) -/* gcc rewrites to memset otherwise :'( */ -void __bzero(void *, size_t) asm("bzero") memcpyesque; -#define bzero(DEST, SIZE) \ - ((void)((__builtin_constant_p(SIZE)) ? memset(DEST, 0, SIZE) \ - : __bzero(DEST, SIZE))) -#endif /* __GNUC__ && !__STRICT_ANSI__ */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STR_STR_H_ */ diff --git a/libc/str/strchrnul.c b/libc/str/strchrnul.c index 981b9aaa6..3a3125442 100644 --- a/libc/str/strchrnul.c +++ b/libc/str/strchrnul.c @@ -53,6 +53,8 @@ noasan static inline const char *strchrnul_sse(const char *s, unsigned char c) { /** * Returns pointer to first instance of character. * + * If c is not found then a pointer to the nul byte is returned. + * * @param s is a NUL-terminated string * @param c is masked with 255 as byte to search for * @return pointer to first instance of c, or pointer to diff --git a/libc/str/strsignal.c b/libc/str/strsignal.c index 7b4283462..ea6b0868f 100644 --- a/libc/str/strsignal.c +++ b/libc/str/strsignal.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/macros.internal.h" #include "libc/str/str.h" @@ -35,15 +35,12 @@ static char g_strsignal[12]; * @see sigaction() */ char *strsignal(int sig) { - int i; + const char *s; strcpy(g_strsignal, "SIG"); if (sig) { - for (i = 0; kStrSignal[i].x; ++i) { - if (sig == *(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x)) { - strcpy(g_strsignal + 3, - (const char *)((uintptr_t)kStrSignal + kStrSignal[i].s)); - return g_strsignal; - } + if ((s = GetMagnumStr(kSignalNames, sig))) { + strcpy(g_strsignal + 3, s); + return g_strsignal; } } if (!sig) { diff --git a/libc/sysv/calls/getresuid.s b/libc/sysv/calls/getresuid.s deleted file mode 100644 index e50c6a170..000000000 --- a/libc/sysv/calls/getresuid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall getresuid,0xfff119168ffff076,globl diff --git a/libc/sysv/calls/pledge.s b/libc/sysv/calls/pledge.s deleted file mode 100644 index 8015bf37e..000000000 --- a/libc/sysv/calls/pledge.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall pledge,0xfff06cffffffffff,globl diff --git a/libc/sysv/calls/setregid.s b/libc/sysv/calls/setregid.s deleted file mode 100644 index 7daa7a16a..000000000 --- a/libc/sysv/calls/setregid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall setregid,0x07f07f07f207f072,globl diff --git a/libc/sysv/calls/setreuid.s b/libc/sysv/calls/setreuid.s deleted file mode 100644 index e0b88ab32..000000000 --- a/libc/sysv/calls/setreuid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall setreuid,0x07e07e07e207e071,globl diff --git a/libc/sysv/calls/sys_getresgid.s b/libc/sysv/calls/sys_getresgid.s new file mode 100644 index 000000000..f70e7c99d --- /dev/null +++ b/libc/sysv/calls/sys_getresgid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_getresgid,0xfff11b169ffff078,globl diff --git a/libc/sysv/calls/sys_getresuid.s b/libc/sysv/calls/sys_getresuid.s new file mode 100644 index 000000000..4d61c6a0c --- /dev/null +++ b/libc/sysv/calls/sys_getresuid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_getresuid,0xfff119168ffff076,globl diff --git a/libc/sysv/calls/getresgid.s b/libc/sysv/calls/sys_pledge.s similarity index 50% rename from libc/sysv/calls/getresgid.s rename to libc/sysv/calls/sys_pledge.s index 9ce4dee92..18fc40da1 100644 --- a/libc/sysv/calls/getresgid.s +++ b/libc/sysv/calls/sys_pledge.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall getresgid,0xfff11b169ffff078,globl +.scall sys_pledge,0xfff06cffffffffff,globl diff --git a/libc/sysv/calls/sys_setregid.s b/libc/sysv/calls/sys_setregid.s new file mode 100644 index 000000000..8b42b154a --- /dev/null +++ b/libc/sysv/calls/sys_setregid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_setregid,0x07f07f07f207f072,globl,hidden diff --git a/libc/sysv/calls/sys_setreuid.s b/libc/sysv/calls/sys_setreuid.s new file mode 100644 index 000000000..a3f3fc5b3 --- /dev/null +++ b/libc/sysv/calls/sys_setreuid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_setreuid,0x07e07e07e207e071,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index ddace70e8..b52068ef5 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -436,10 +436,10 @@ syscon ioctl TIOCINQ 0x541b 0x4004667f 0x4004667f 0x4004667f 0x4004667f # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon at AT_FDCWD -100 -2 -100 -100 -100 -100 # faked nt syscon at AT_SYMLINK_NOFOLLOW 0x0100 0x20 0x0200 2 0x200 0x0100 # faked nt +syscon at AT_SYMLINK_FOLLOW 0x0400 0x40 0x0400 4 0x400 0 # see linkat(2) syscon at AT_REMOVEDIR 0x0200 0x80 0x0800 8 0x800 0x0200 # faked nt syscon at AT_EACCESS 0x0200 0x10 0x0100 1 0x100 0 syscon at AT_EMPTY_PATH 0x1000 0 0 0 0 0 # linux 2.6.39+; see unlink, O_TMPFILE, etc. -syscon at AT_SYMLINK_FOLLOW 0x0400 0x40 0x0400 4 0x400 0 # uhhh wut # memfd_create() flags # @@ -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,30 +669,32 @@ 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 syscon so SO_RCVTIMEO 20 0x1006 0x1006 0x1006 0x100c 0x1006 # recv timeout; takes struct timeval (overrides SA_RESTART restoring EINTR behavior on recv/send/connect/accept/etc.; bsd consensus) 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_EXCLUSIVEADDRUSE 0 0 0 0 0 0xfffffffb # hoo boy 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 syscon so SO_MAX_PACING_RATE 47 0 0x1018 0 0 0 syscon so SO_PEERCRED 17 0 0 0x1022 0 0 +syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 0xfffffffb # hoo boy syscon so LOCAL_PEERCRED 0 1 1 0 0 0 syscon so SO_PROTOCOL 38 0 0x1016 0x1025 0 0 syscon so SO_ATTACH_BPF 50 0 0 0 0 0 @@ -730,9 +732,10 @@ syscon so SO_TIMESTAMPNS 35 0 0 0 0 0 syscon so SO_WIFI_STATUS 41 0 0 0 0 0 syscon sol SOL_IP 0 0 0 0 0 0 # consensus -syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff 0xffff # bsd+nt consensus (todo: what's up with ipproto_icmp overlap) +syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff 0xffff # yes it's actually 0xffff; bsd+nt consensus (todo: what's up with ipproto_icmp overlap) syscon sol SOL_TCP 6 6 6 6 6 6 # consensus syscon sol SOL_UDP 17 17 17 17 17 17 # consensus +syscon sol SOL_RAW 255 0 0 0 0 0 syscon sol SOL_IPV6 41 41 41 41 41 41 syscon sol SOL_ICMPV6 58 58 58 58 58 0 syscon sol SOL_AAL 265 0 0 0 0 0 @@ -752,7 +755,6 @@ syscon sol SOL_NFC 280 0 0 0 0 0 syscon sol SOL_PACKET 263 0 0 0 0 0 syscon sol SOL_PNPIPE 275 0 0 0 0 0 syscon sol SOL_PPPOL2TP 273 0 0 0 0 0 -syscon sol SOL_RAW 255 0 0 0 0 0 syscon sol SOL_RDS 276 0 0 0 0 0 syscon sol SOL_RXRPC 272 0 0 0 0 0 syscon sol SOL_TIPC 271 0 0 0 0 0 @@ -808,13 +810,14 @@ syscon tcp TCP_REPAIR_QUEUE 20 0 0 0 0 0 # what is it syscon tcp TCP_THIN_LINEAR_TIMEOUTS 16 0 0 0 0 0 # what is it # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon ip IP_TOS 1 3 3 3 3 8 # bsd consensus +syscon ip IP_TTL 2 4 4 4 4 7 # bsd consensus +syscon ip IP_HDRINCL 3 2 2 2 2 2 # bsd consensus syscon ip IP_DEFAULT_MULTICAST_LOOP 1 1 1 1 1 1 # consensus syscon ip IP_DEFAULT_MULTICAST_TTL 1 1 1 1 1 1 # consensus syscon ip IP_PMTUDISC_DONT 0 0 0 0 0 0 # consensus -syscon ip IP_HDRINCL 3 2 2 2 2 2 # bsd consensus syscon ip IP_MAX_MEMBERSHIPS 20 0x0fff 0x0fff 0x0fff 0x0fff 20 # bsd consensus syscon ip IP_OPTIONS 4 1 1 1 1 1 # bsd consensus -syscon ip IP_TOS 1 3 3 3 3 8 # bsd consensus syscon ip IP_RECVTTL 12 24 65 31 23 21 syscon ip IP_ADD_MEMBERSHIP 35 12 12 12 12 5 # bsd consensus syscon ip IP_DROP_MEMBERSHIP 36 13 13 13 13 6 # bsd consensus @@ -825,7 +828,6 @@ syscon ip IP_RECVOPTS 6 5 5 5 5 0 # bsd consensus syscon ip IP_RECVRETOPTS 7 6 6 6 6 0 # bsd consensus syscon ip IP_RECVDSTADDR 0 7 7 7 7 0 # bsd consensus syscon ip IP_RETOPTS 7 8 8 8 8 0 # bsd consensus -syscon ip IP_TTL 2 4 4 4 4 7 # bsd consensus syscon ip IP_ADD_SOURCE_MEMBERSHIP 39 70 70 0 0 15 syscon ip IP_BLOCK_SOURCE 38 72 72 0 0 17 syscon ip IP_DROP_SOURCE_MEMBERSHIP 40 71 71 0 0 0x10 @@ -1108,9 +1110,6 @@ syscon pf PF_VSOCK 40 0 0 0 0 0 syscon pf PF_WANPIPE 25 0 0 0 0 0 syscon pf PF_X25 9 0 0 0 0 0 -syscon exit EXIT_SUCCESS 0 0 0 0 0 0 # consensus -syscon exit EXIT_FAILURE 1 1 1 1 1 1 # consensus - # Eric Allman's exit() codes # # - Broadly supported style guideline; @@ -1227,6 +1226,15 @@ syscon mount MNT_NOCLUSTERR 0 0 0x40000000 0 0 0 # disable cluster syscon mount MNT_NOCLUSTERW 0 0 0x80000000 0 0 0 # disable cluster write syscon mount MNT_SNAPSHOT 0 0x40000000 0x01000000 0 0 0 # confusing +# limits +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon limits PIPE_BUF 4096 512 512 512 512 4096 # bsd consensus +syscon limits _ARG_MAX 128*1024 1024*1024 512*1024 512*1024 256*1024 32767*2 # bsd consensus +syscon limits _NAME_MAX 255 255 255 255 511 255 # probably higher on windows? +syscon limits _PATH_MAX 4096 1024 1024 1024 1024 512 # cosmopolitan libc imposes a lower 512 limit; nt theoretically goes up to 32767 +syscon limits _NSIG 64 32 128 32 64 32 # _SIG_MAXSIG on FreeBSD + # unmount() flags # a.k.a. umount2() on linux # @@ -1295,6 +1303,14 @@ syscon prio PRIO_MIN -20 -20 -20 -20 -20 -20 # unix consensus / p syscon prio PRIO_MAX 20 20 20 20 20 20 # unix consensus / poly nt syscon prio NZERO 20 20 20 20 20 20 # unix consensus / polyfilled nt +# getrusage() who +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon rusage RUSAGE_SELF 0 0 0 0 0 0 # unix consensus & faked nt +syscon rusage RUSAGE_THREAD 1 99 1 1 1 1 # faked nt & unavailable on xnu +syscon rusage RUSAGE_CHILDREN -1 -1 -1 -1 -1 99 # unix consensus & unavailable on nt +syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop + # Teletypewriter Control, e.g. # # TCSETS → About 70,800 results (0.31 seconds) @@ -1367,6 +1383,25 @@ syscon termios ENDRUNDISC 0 0 0 0x9 0x9 0 # boop syscon termios TIOCPTMASTER 0 0 0x2000741c 0 0 0 # boop syscon termios NETGRAPHDISC 0 0 0x6 0 0 0 # boop syscon termios H4DISC 0 0 0x7 0 0 0 # boop + +# Teletypewriter Control Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon termios CS5 0b0000000000000000 0b000000000000000000 0b000000000000000000 0b0000000000000000 0b0000000000000000 0b0000000000000000 # termios.c_cflag; consensus +syscon termios CS6 0b0000000000010000 0b000000000100000000 0b000000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000010000 # termios.c_cflag; 6-bit characters +syscon termios CS7 0b0000000000100000 0b000000001000000000 0b000000001000000000 0b0000001000000000 0b0000001000000000 0b0000000000100000 # termios.c_cflag; 7-bit characters +syscon termios CS8 0b0000000000110000 0b000000001100000000 0b000000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # termios.c_cflag; 8-bit characters +syscon termios CSIZE 0b0000000000110000 0b000000001100000000 0b000000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # termios.c_cflag; mask for CS𝑥 flags +syscon termios CSTOPB 0b0000000001000000 0b000000010000000000 0b000000010000000000 0b0000010000000000 0b0000010000000000 0b0000000001000000 # termios.c_cflag; bsd consensus +syscon termios CREAD 0b0000000010000000 0b000000100000000000 0b000000100000000000 0b0000100000000000 0b0000100000000000 0b0000000010000000 # termios.c_cflag; bsd consensus +syscon termios PARENB 0b0000000100000000 0b000001000000000000 0b000001000000000000 0b0001000000000000 0b0001000000000000 0b0000000100000000 # termios.c_cflag +syscon termios PARODD 0b0000001000000000 0b000010000000000000 0b000010000000000000 0b0010000000000000 0b0010000000000000 0b0000001000000000 # termios.c_cflag +syscon termios HUPCL 0b0000010000000000 0b000100000000000000 0b000100000000000000 0b0100000000000000 0b0100000000000000 0b0000010000000000 # termios.c_cflag; bsd consensus +syscon termios CLOCAL 0b0000100000000000 0b1000000000000000 0b1000000000000000 0b1000000000000000 0b1000000000000000 0b0000100000000000 # termios.c_cflag; consensus + +# Teletypewriter Local Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios ISIG 0b0000000000000001 0b0000000010000000 0b0000000010000000 0b0000000010000000 0b0000000010000000 0b0000000000000001 # termios.c_lflag|=ISIG makes Ctrl-C, Ctrl-\, etc. generate signals syscon termios ICANON 0b0000000000000010 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000000010 # termios.c_lflag&=~ICANON disables 1960's version of gnu readline (see also VMIN) syscon termios XCASE 0b0000000000000100 0 0 16777216 0 0b0000000000000100 # termios.c_lflag @@ -1383,6 +1418,10 @@ syscon termios FLUSHO 0b0001000000000000 8388608 8388608 8388608 83886 syscon termios PENDIN 0b0100000000000000 536870912 536870912 536870912 536870912 0b0100000000000000 # termios.c_lflag syscon termios IEXTEN 0b1000000000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b1000000000000000 # termios.c_lflag&=~IEXTEN disables platform input processing magic syscon termios EXTPROC 65536 0b0000100000000000 0b0000100000000000 0b0000100000000000 0b0000100000000000 65536 # termios.c_lflag + +# Teletypewriter Input Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios IGNBRK 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS syscon termios BRKINT 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS syscon termios IGNPAR 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 # termios.c_iflag|=IGNPAR ignores parity and framing errors; see PARMRK UNIXCONSENSUS @@ -1398,9 +1437,13 @@ syscon termios IXANY 0b0000100000000000 0b0000100000000000 0b000010000000000 syscon termios IXOFF 0b0001000000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b0001000000000000 # termios.c_iflag|=IXOFF disables annoying display freeze keys syscon termios IMAXBEL 0b0010000000000000 0b0010000000000000 0b0010000000000000 0b0010000000000000 0b0010000000000000 0b0010000000000000 # termios.c_iflag|=IMAXBEL rings when queue full UNIXCONSENSUS syscon termios IUTF8 0b0100000000000000 0b0100000000000000 0 0 0 0b0100000000000000 # termios.c_iflag|=IUTF8 helps w/ rubout on UTF-8 input + +# Teletypewriter Output Modes +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios OPOST 0b0000000000000001 0b000000000000000001 0b000000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_oflag&=~OPOST disables output processing magic, e.g. MULTICS newlines syscon termios OLCUC 0b0000000000000010 0 0 0b0000000000100000 0 0b0000000000000010 # termios.c_oflag|=OLCUC maps a-z → A-Z output -syscon termios ONLCR 0b0000000000000100 0b000000000000000010 0b000000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000100 # termios.c_oflag|=ONLCR claims to map \n → \r\n output +syscon termios ONLCR 0b0000000000000100 0b000000000000000010 0b000000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000100 # termios.c_oflag|=ONLCR map \n → \r\n output (MULTICS newline) and requires OPOST syscon termios OCRNL 0b0000000000001000 0b000000000000010000 0b000000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000001000 # termios.c_oflag|=OCRNL maps \r → \n output syscon termios ONOCR 0b0000000000010000 0b000000000000100000 0b000000000000100000 0b0000000001000000 0b0000000001000000 0b0000000000010000 # termios.c_oflag|=ONOCR maps \r → ∅ output iff column 0 syscon termios ONLRET 0b0000000000100000 0b000000000001000000 0b000000000001000000 0b0000000010000000 0b0000000010000000 0b0000000000100000 # termios.c_oflag|=ONLRET maps \r → ∅ output @@ -1431,11 +1474,10 @@ syscon termios VT1 0b0100000000000000 0b010000000000000000 0b0100000000000 syscon termios FFDLY 0b1000000000000000 0b000100000000000000 0b000100000000000000 0 0 0b1000000000000000 # termios.c_oflag syscon termios FF0 0b0000000000000000 0b000000000000000000 0b000000000000000000 0 0 0b0000000000000000 # termios.c_oflag syscon termios FF1 0b1000000000000000 0b000100000000000000 0b000100000000000000 0 0 0b1000000000000000 # termios.c_oflag -syscon termios CS5 0 0 0 0 0 0 # consensus -syscon termios CS6 0b0000000000010000 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000010000 # termios.c_cflag flag for 6-bit characters -syscon termios CS7 0b0000000000100000 0b0000001000000000 0b0000001000000000 0b0000001000000000 0b0000001000000000 0b0000000000100000 # termios.c_cflag flag for 7-bit characters -syscon termios CS8 0b0000000000110000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # termios.c_cflag flag for 8-bit characters -syscon termios CSIZE 0b0000000000110000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000001100000000 0b0000000000110000 # mask for CS𝑥 flags + +# Teletypewriter Special Control Character Assignments +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon termios NCCS 20 20 20 20 20 20 # ARRAYLEN(termios.c_cc); we schlep c_line into c_cc on linux syscon termios VINTR 0+1 8 8 8 8 0 # termios.c_cc[VINTR]=𝑥 syscon termios VQUIT 1+1 9 9 9 9 1 # termios.c_cc[VQUIT]=𝑥 @@ -1454,15 +1496,13 @@ syscon termios VDISCARD 13+1 15 15 15 15 13 # termios.c_cc[VDISCA syscon termios VWERASE 14+1 4 4 4 4 14 # termios.c_cc[VWERASE]=𝑥 syscon termios VLNEXT 15+1 14 14 14 14 15 # termios.c_cc[VLNEXT]=𝑥 syscon termios VEOL2 16+1 2 2 2 2 16 # termios.c_cc[VEOL2]=𝑥 + syscon termios TIOCSERGETLSR 0x5459 0 0 0 0 0 # syscon termios TIOCSERGETMULTI 0x545a 0 0 0 0 0 # syscon termios TIOCSERSETMULTI 0x545b 0 0 0 0 0 # syscon termios TIOCSER_TEMT 1 0 0 0 0 0 # syscon termios VERIFY 47 0 0 0 0 0 -syscon termios PARENB 0x0100 0x1000 0x1000 0x1000 0x1000 0 # -syscon termios PARODD 0x0200 0x2000 0x2000 0x2000 0x2000 0 # syscon termios CIBAUD 0x100f0000 0 0 0 0 0 -syscon termios CLOCAL 0x0800 0x8000 0x8000 0x8000 0x8000 0 # syscon termios CMSPAR 0x40000000 0 0 0 0 0 syscon termios BUSY 4 0 0 0 0 0 syscon termios CANBSIZ 255 0 0 0 0 0 @@ -1488,9 +1528,6 @@ syscon termios TCOON 1 2 2 2 2 1 # see tcflow; bsd consensus syscon termios TCIOFF 2 3 3 3 3 2 # see tcflow; bsd consensus syscon termios TCION 3 4 4 4 4 3 # see tcflow; bsd consensus -syscon termios CREAD 0x80 0x0800 0x0800 0x0800 0x0800 0 # bsd consensus -syscon termios CSTOPB 0x40 0x0400 0x0400 0x0400 0x0400 0 # bsd consensus -syscon termios HUPCL 0x0400 0x4000 0x4000 0x4000 0x4000 0 # bsd consensus syscon termios CSTART 17 17 17 17 17 0 # unix consensus syscon termios CSTOP 19 19 19 19 19 0 # unix consensus @@ -1708,11 +1745,6 @@ syscon misc NL_TEXTMAX 0x7fffffff 0x0800 0x0800 255 255 0 syscon misc NL_NMAX 0x7fffffff 1 1 0 0 0 syscon misc NL_SETD 1 1 0 1 1 0 -syscon rusage RUSAGE_SELF 0 0 0 0 0 0 # unix consensus & faked nt -syscon rusage RUSAGE_THREAD 1 99 1 1 1 1 # faked nt & unavailable on xnu -syscon rusage RUSAGE_CHILDREN -1 -1 -1 -1 -1 99 # unix consensus & unavailable on nt -syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop - syscon misc FSETLOCKING_QUERY 0 0 0 0 0 0 # consensus syscon misc FSETLOCKING_BYCALLER 2 0 0 0 0 0 syscon misc FSETLOCKING_INTERNAL 1 0 0 0 0 0 @@ -3032,7 +3064,6 @@ syscon misc NGREG 23 0 0 0 0 0 syscon misc NOGROUP -1 0xffff 0xffff 0xffff 0xffff 0 # bsd consensus syscon misc ORDERED_QUEUE_TAG 34 0 0 0 0 0 syscon misc ORIG_RAX 15 0 0 0 0 0 -syscon misc PIPE_BUF 0x1000 0x0200 0x0200 0x0200 0x0200 0 # bsd consensus syscon misc PRE_FETCH 52 0 0 0 0 0 syscon misc QUEUE_FULL 20 0 0 0 0 0 syscon misc REASSIGN_BLOCKS 7 0 0 0 0 0 @@ -3160,84 +3191,6 @@ syscon in IN_OPEN 0x20 0 0 0 0 0 syscon in IN_Q_OVERFLOW 0x4000 0 0 0 0 0 syscon in IN_UNMOUNT 0x2000 0 0 0 0 0 -syscon posix _POSIX_ARG_MAX 0x1000 0x1000 0x1000 0x1000 0x1000 0 # unix consensus -syscon posix _POSIX_CHILD_MAX 25 25 25 25 25 0 # unix consensus -syscon posix _POSIX_HOST_NAME_MAX 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_LINK_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_LOGIN_NAME_MAX 9 9 9 9 9 0 # unix consensus -syscon posix _POSIX_MAX_CANON 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_MAX_INPUT 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_NAME_MAX 14 14 14 14 14 14 # forced consensus -syscon posix _POSIX_NGROUPS_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_OPEN_MAX 20 20 20 20 20 20 # forced consensus -syscon posix _POSIX_PATH_MAX 255 255 255 255 255 255 # forced consensus -syscon posix _POSIX_PIPE_BUF 0x0200 0x0200 0x0200 0x0200 0x0200 0 # unix consensus -syscon posix _POSIX_RE_DUP_MAX 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_SEM_NSEMS_MAX 0x0100 0x0100 0x0100 0x0100 0x0100 0 # unix consensus -syscon posix _POSIX_SEM_VALUE_MAX 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0 # unix consensus -syscon posix _POSIX_SSIZE_MAX 0x7fff 0x7fff 0x7fff 0x7fff 0x7fff 0 # unix consensus -syscon posix _POSIX_STREAM_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_SYMLINK_MAX 255 255 255 255 255 0 # unix consensus -syscon posix _POSIX_SYMLOOP_MAX 8 8 8 8 8 0 # unix consensus -syscon posix _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 4 4 4 4 0 # unix consensus -syscon posix _POSIX_THREAD_KEYS_MAX 0x80 0x80 0x80 0x80 0x80 0 # unix consensus -syscon posix _POSIX_TTY_NAME_MAX 9 9 9 9 9 0 # unix consensus -syscon posix _POSIX_TZNAME_MAX 6 6 6 6 6 0 # unix consensus -syscon posix _POSIX_CLOCK_SELECTION 0x031069 -1 -1 -1 -1 0 # bsd consensus -syscon posix _POSIX_FSYNC 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_MAPPED_FILES 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_MEMORY_PROTECTION 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_READER_WRITER_LOCKS 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_THREADS 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_THREAD_ATTR_STACKADDR 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_THREAD_ATTR_STACKSIZE 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon posix _POSIX_ADVISORY_INFO 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_ASYNCHRONOUS_IO 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_BARRIERS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_JOB_CONTROL 1 0x030db0 1 1 1 0 -syscon posix _POSIX_MEMLOCK 0x031069 -1 -1 0x030db0 0x030db0 0 -syscon posix _POSIX_MEMLOCK_RANGE 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_MESSAGE_PASSING 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_NO_TRUNC 1 0x030db0 1 1 1 0 -syscon posix _POSIX_RAW_SOCKETS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_REALTIME_SIGNALS 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_REGEXP 1 0x030db0 1 1 1 0 -syscon posix _POSIX_SEMAPHORES 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_SHARED_MEMORY_OBJECTS 0x031069 -1 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_SHELL 1 0x030db0 1 1 1 0 -syscon posix _POSIX_SPAWN 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_SPIN_LOCKS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_THREAD_PRIORITY_SCHEDULING 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_THREAD_PROCESS_SHARED 0x031069 0x030db0 0x030db0 -1 -1 0 -syscon posix _POSIX_THREAD_SAFE_FUNCTIONS 0x031069 0x030db0 -1 0x030db0 0x030db0 0 -syscon posix _POSIX_THREAD_THREADS_MAX 0x40 0x40 0x40 4 4 0 -syscon posix _POSIX_TIMEOUTS 0x031069 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_TIMERS 0x031069 -1 0x030db0 -1 -1 0 -syscon posix _POSIX_VERSION 0x031069 0x030db0 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_VDISABLE 0 255 255 255 255 0 # bsd consensus -syscon posix _POSIX_AIO_LISTIO_MAX 2 2 2 0 0 0 -syscon posix _POSIX_AIO_MAX 1 1 1 0 0 0 -syscon posix _POSIX_CHOWN_RESTRICTED 0 0x030db0 1 1 1 0 -syscon posix _POSIX_CLOCKRES_MIN 0x01312d00 0 0x01312d00 0x01312d00 0x01312d00 0 -syscon posix _POSIX_CPUTIME 0 -1 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_DELAYTIMER_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_MONOTONIC_CLOCK 0 -1 0x030db0 0x030db0 0x030db0 0 -syscon posix _POSIX_MQ_OPEN_MAX 8 8 8 0 0 0 -syscon posix _POSIX_MQ_PRIO_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_RTSIG_MAX 8 8 8 0 0 0 -syscon posix _POSIX_SAVED_IDS 1 0x030db0 0 1 1 0 -syscon posix _POSIX_SIGQUEUE_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_THREAD_CPUTIME 0 -1 0x030db0 0x031069 0x031069 0 -syscon posix _POSIX_TIMER_MAX 0x20 0x20 0x20 0 0 0 -syscon posix _POSIX_IPV6 0x031069 0x030db0 0 0 0 0 -syscon posix _POSIX_SS_REPL_MAX 0 4 4 0 0 0 -syscon posix _POSIX_TRACE_EVENT_NAME_MAX 0 30 30 0 0 0 -syscon posix _POSIX_TRACE_NAME_MAX 0 8 8 0 0 0 -syscon posix _POSIX_TRACE_SYS_MAX 0 8 8 0 0 0 -syscon posix _POSIX_TRACE_USER_EVENT_MAX 0 0x20 0x20 0 0 0 -syscon posix _POSIX_V6_LP64_OFF64 1 1 0 0 0 0 -syscon posix _POSIX_V7_LP64_OFF64 1 1 0 0 0 0 - syscon misc TYPE_DISK 0 0 0 0 0 0 # consensus syscon misc TYPE_A 1 1 1 1 1 0 # unix consensus syscon misc TYPE_E 2 2 2 2 2 0 # unix consensus @@ -3253,18 +3206,6 @@ syscon misc TYPE_SCANNER 6 0 0 0 0 0 syscon misc TYPE_TAPE 1 0 0 0 0 0 syscon misc TYPE_WORM 4 0 0 0 0 0 -syscon misc _POSIX2_BC_BASE_MAX 99 99 99 99 99 0 # unix consensus -syscon misc _POSIX2_BC_DIM_MAX 0x0800 0x0800 0x0800 0x0800 0x0800 0 # unix consensus -syscon misc _POSIX2_BC_SCALE_MAX 99 99 99 99 99 0 # unix consensus -syscon misc _POSIX2_BC_STRING_MAX 0x03e8 0x03e8 0x03e8 0x03e8 0x03e8 0 # unix consensus -syscon misc _POSIX2_CHARCLASS_NAME_MAX 14 14 14 14 14 0 # unix consensus -syscon misc _POSIX2_COLL_WEIGHTS_MAX 2 2 2 2 2 0 # unix consensus -syscon misc _POSIX2_EXPR_NEST_MAX 0x20 0x20 0x20 0x20 0x20 0 # unix consensus -syscon misc _POSIX2_LINE_MAX 0x0800 0x0800 0x0800 0x0800 0x0800 0 # unix consensus -syscon misc _POSIX2_RE_DUP_MAX 255 255 255 255 255 0 # unix consensus -syscon misc _POSIX2_C_BIND 0x031069 0x030db0 0x030db0 0x030db0 0x030db0 0 # bsd consensus -syscon misc _POSIX2_VERSION 0x031069 0x030db0 0x030a2c 0x031069 0x031069 0 - syscon nd ND_RA_FLAG_MANAGED 0x80 0x80 0x80 0x80 0x80 0x80 # consensus syscon nd ND_RA_FLAG_OTHER 0x40 0x40 0x40 0x40 0x40 0x40 # consensus syscon nd ND_NA_FLAG_OVERRIDE 0x20 0x20 0x20 0x20 0x20 0x20000000 # unix consensus diff --git a/libc/sysv/consts/CLOCAL.S b/libc/sysv/consts/CLOCAL.S index 73bdc0a62..d9bcd3c33 100644 --- a/libc/sysv/consts/CLOCAL.S +++ b/libc/sysv/consts/CLOCAL.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CLOCAL,0x0800,0x8000,0x8000,0x8000,0x8000,0 +.syscon termios,CLOCAL,0b0000100000000000,0b1000000000000000,0b1000000000000000,0b1000000000000000,0b1000000000000000,0b0000100000000000 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/CREAD.S b/libc/sysv/consts/CREAD.S index 1eac14cd8..e18ada92d 100644 --- a/libc/sysv/consts/CREAD.S +++ b/libc/sysv/consts/CREAD.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CREAD,0x80,0x0800,0x0800,0x0800,0x0800,0 +.syscon termios,CREAD,0b0000000010000000,0b000000100000000000,0b000000100000000000,0b0000100000000000,0b0000100000000000,0b0000000010000000 diff --git a/libc/sysv/consts/CS5.S b/libc/sysv/consts/CS5.S index 01a9b5895..ac9704d4c 100644 --- a/libc/sysv/consts/CS5.S +++ b/libc/sysv/consts/CS5.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS5,0,0,0,0,0,0 +.syscon termios,CS5,0b0000000000000000,0b000000000000000000,0b000000000000000000,0b0000000000000000,0b0000000000000000,0b0000000000000000 diff --git a/libc/sysv/consts/CS6.S b/libc/sysv/consts/CS6.S index f5031010e..4b5329830 100644 --- a/libc/sysv/consts/CS6.S +++ b/libc/sysv/consts/CS6.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS6,0b0000000000010000,0b0000000100000000,0b0000000100000000,0b0000000100000000,0b0000000100000000,0b0000000000010000 +.syscon termios,CS6,0b0000000000010000,0b000000000100000000,0b000000000100000000,0b0000000100000000,0b0000000100000000,0b0000000000010000 diff --git a/libc/sysv/consts/CS7.S b/libc/sysv/consts/CS7.S index d4fb534a7..923e5982c 100644 --- a/libc/sysv/consts/CS7.S +++ b/libc/sysv/consts/CS7.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS7,0b0000000000100000,0b0000001000000000,0b0000001000000000,0b0000001000000000,0b0000001000000000,0b0000000000100000 +.syscon termios,CS7,0b0000000000100000,0b000000001000000000,0b000000001000000000,0b0000001000000000,0b0000001000000000,0b0000000000100000 diff --git a/libc/sysv/consts/CS8.S b/libc/sysv/consts/CS8.S index 08ad827a8..21784475b 100644 --- a/libc/sysv/consts/CS8.S +++ b/libc/sysv/consts/CS8.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CS8,0b0000000000110000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 +.syscon termios,CS8,0b0000000000110000,0b000000001100000000,0b000000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 diff --git a/libc/sysv/consts/CSIZE.S b/libc/sysv/consts/CSIZE.S index 90372e69c..59abbeb63 100644 --- a/libc/sysv/consts/CSIZE.S +++ b/libc/sysv/consts/CSIZE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CSIZE,0b0000000000110000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 +.syscon termios,CSIZE,0b0000000000110000,0b000000001100000000,0b000000001100000000,0b0000001100000000,0b0000001100000000,0b0000000000110000 diff --git a/libc/sysv/consts/CSTOPB.S b/libc/sysv/consts/CSTOPB.S index 0e8700592..5374da0e4 100644 --- a/libc/sysv/consts/CSTOPB.S +++ b/libc/sysv/consts/CSTOPB.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,CSTOPB,0x40,0x0400,0x0400,0x0400,0x0400,0 +.syscon termios,CSTOPB,0b0000000001000000,0b000000010000000000,0b000000010000000000,0b0000010000000000,0b0000010000000000,0b0000000001000000 diff --git a/libc/sysv/consts/HUPCL.S b/libc/sysv/consts/HUPCL.S index dd03f7ab0..6d27a2fb8 100644 --- a/libc/sysv/consts/HUPCL.S +++ b/libc/sysv/consts/HUPCL.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,HUPCL,0x0400,0x4000,0x4000,0x4000,0x4000,0 +.syscon termios,HUPCL,0b0000010000000000,0b000100000000000000,0b000100000000000000,0b0100000000000000,0b0100000000000000,0b0000010000000000 diff --git a/libc/sysv/consts/PARENB.S b/libc/sysv/consts/PARENB.S index 2a40e7dc4..9900cf43b 100644 --- a/libc/sysv/consts/PARENB.S +++ b/libc/sysv/consts/PARENB.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,PARENB,0x0100,0x1000,0x1000,0x1000,0x1000,0 +.syscon termios,PARENB,0b0000000100000000,0b000001000000000000,0b000001000000000000,0b0001000000000000,0b0001000000000000,0b0000000100000000 diff --git a/libc/sysv/consts/PARODD.S b/libc/sysv/consts/PARODD.S index 0ef8fff7a..1cfe04b15 100644 --- a/libc/sysv/consts/PARODD.S +++ b/libc/sysv/consts/PARODD.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon termios,PARODD,0x0200,0x2000,0x2000,0x2000,0x2000,0 +.syscon termios,PARODD,0b0000001000000000,0b000010000000000000,0b000010000000000000,0b0010000000000000,0b0010000000000000,0b0000001000000000 diff --git a/libc/sysv/consts/PIPE_BUF.S b/libc/sysv/consts/PIPE_BUF.S index 8a0b5b01c..34d0d247e 100644 --- a/libc/sysv/consts/PIPE_BUF.S +++ b/libc/sysv/consts/PIPE_BUF.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,PIPE_BUF,0x1000,0x0200,0x0200,0x0200,0x0200,0 +.syscon limits,PIPE_BUF,4096,512,512,512,512,4096 diff --git a/libc/sysv/consts/EXIT_SUCCESS.S b/libc/sysv/consts/SO_DONTLINGER.S similarity index 52% rename from libc/sysv/consts/EXIT_SUCCESS.S rename to libc/sysv/consts/SO_DONTLINGER.S index 8f41a65ed..6d61ff314 100644 --- a/libc/sysv/consts/EXIT_SUCCESS.S +++ b/libc/sysv/consts/SO_DONTLINGER.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon exit,EXIT_SUCCESS,0,0,0,0,0,0 +.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/sysv/consts/_ARG_MAX.S b/libc/sysv/consts/_ARG_MAX.S new file mode 100644 index 000000000..1071d0042 --- /dev/null +++ b/libc/sysv/consts/_ARG_MAX.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_ARG_MAX,128*1024,1024*1024,512*1024,512*1024,256*1024,32767*2 diff --git a/libc/sysv/consts/_NAME_MAX.S b/libc/sysv/consts/_NAME_MAX.S new file mode 100644 index 000000000..42d20d0bb --- /dev/null +++ b/libc/sysv/consts/_NAME_MAX.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_NAME_MAX,255,255,255,255,511,255 diff --git a/libc/sysv/consts/EXIT_FAILURE.S b/libc/sysv/consts/_NSIG.S similarity index 53% rename from libc/sysv/consts/EXIT_FAILURE.S rename to libc/sysv/consts/_NSIG.S index 21df576a8..12571eff7 100644 --- a/libc/sysv/consts/EXIT_FAILURE.S +++ b/libc/sysv/consts/_NSIG.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon exit,EXIT_FAILURE,1,1,1,1,1,1 +.syscon limits,_NSIG,64,32,128,32,64,32 diff --git a/libc/sysv/consts/_PATH_MAX.S b/libc/sysv/consts/_PATH_MAX.S new file mode 100644 index 000000000..af56d92f4 --- /dev/null +++ b/libc/sysv/consts/_PATH_MAX.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon limits,_PATH_MAX,4096,1024,1024,1024,1024,512 diff --git a/libc/sysv/consts/_POSIX2_BC_BASE_MAX.S b/libc/sysv/consts/_POSIX2_BC_BASE_MAX.S deleted file mode 100644 index d9def8b3a..000000000 --- a/libc/sysv/consts/_POSIX2_BC_BASE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_BASE_MAX,99,99,99,99,99,0 diff --git a/libc/sysv/consts/_POSIX2_BC_DIM_MAX.S b/libc/sysv/consts/_POSIX2_BC_DIM_MAX.S deleted file mode 100644 index c998bf8ee..000000000 --- a/libc/sysv/consts/_POSIX2_BC_DIM_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_DIM_MAX,0x0800,0x0800,0x0800,0x0800,0x0800,0 diff --git a/libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S b/libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S deleted file mode 100644 index b72e2963d..000000000 --- a/libc/sysv/consts/_POSIX2_BC_SCALE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_SCALE_MAX,99,99,99,99,99,0 diff --git a/libc/sysv/consts/_POSIX2_BC_STRING_MAX.S b/libc/sysv/consts/_POSIX2_BC_STRING_MAX.S deleted file mode 100644 index f4ea24f4e..000000000 --- a/libc/sysv/consts/_POSIX2_BC_STRING_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_BC_STRING_MAX,0x03e8,0x03e8,0x03e8,0x03e8,0x03e8,0 diff --git a/libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S b/libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S deleted file mode 100644 index 596985ba5..000000000 --- a/libc/sysv/consts/_POSIX2_CHARCLASS_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_CHARCLASS_NAME_MAX,14,14,14,14,14,0 diff --git a/libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S b/libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S deleted file mode 100644 index b6f3057e5..000000000 --- a/libc/sysv/consts/_POSIX2_COLL_WEIGHTS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_COLL_WEIGHTS_MAX,2,2,2,2,2,0 diff --git a/libc/sysv/consts/_POSIX2_C_BIND.S b/libc/sysv/consts/_POSIX2_C_BIND.S deleted file mode 100644 index 93adee85b..000000000 --- a/libc/sysv/consts/_POSIX2_C_BIND.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_C_BIND,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S b/libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S deleted file mode 100644 index 238faf31c..000000000 --- a/libc/sysv/consts/_POSIX2_EXPR_NEST_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_EXPR_NEST_MAX,0x20,0x20,0x20,0x20,0x20,0 diff --git a/libc/sysv/consts/_POSIX2_LINE_MAX.S b/libc/sysv/consts/_POSIX2_LINE_MAX.S deleted file mode 100644 index 53324180d..000000000 --- a/libc/sysv/consts/_POSIX2_LINE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_LINE_MAX,0x0800,0x0800,0x0800,0x0800,0x0800,0 diff --git a/libc/sysv/consts/_POSIX2_RE_DUP_MAX.S b/libc/sysv/consts/_POSIX2_RE_DUP_MAX.S deleted file mode 100644 index c03fc5b6d..000000000 --- a/libc/sysv/consts/_POSIX2_RE_DUP_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_RE_DUP_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX2_VERSION.S b/libc/sysv/consts/_POSIX2_VERSION.S deleted file mode 100644 index ef440fb36..000000000 --- a/libc/sysv/consts/_POSIX2_VERSION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon misc,_POSIX2_VERSION,0x031069,0x030db0,0x030a2c,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_ADVISORY_INFO.S b/libc/sysv/consts/_POSIX_ADVISORY_INFO.S deleted file mode 100644 index c2737a52b..000000000 --- a/libc/sysv/consts/_POSIX_ADVISORY_INFO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_ADVISORY_INFO,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S b/libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S deleted file mode 100644 index 38fc0e031..000000000 --- a/libc/sysv/consts/_POSIX_AIO_LISTIO_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_AIO_LISTIO_MAX,2,2,2,0,0,0 diff --git a/libc/sysv/consts/_POSIX_AIO_MAX.S b/libc/sysv/consts/_POSIX_AIO_MAX.S deleted file mode 100644 index e342f6a7c..000000000 --- a/libc/sysv/consts/_POSIX_AIO_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_AIO_MAX,1,1,1,0,0,0 diff --git a/libc/sysv/consts/_POSIX_ARG_MAX.S b/libc/sysv/consts/_POSIX_ARG_MAX.S deleted file mode 100644 index 315cf621c..000000000 --- a/libc/sysv/consts/_POSIX_ARG_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_ARG_MAX,0x1000,0x1000,0x1000,0x1000,0x1000,0 diff --git a/libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S b/libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S deleted file mode 100644 index 4de9f3bbd..000000000 --- a/libc/sysv/consts/_POSIX_ASYNCHRONOUS_IO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_ASYNCHRONOUS_IO,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_BARRIERS.S b/libc/sysv/consts/_POSIX_BARRIERS.S deleted file mode 100644 index 8fbeb0fac..000000000 --- a/libc/sysv/consts/_POSIX_BARRIERS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_BARRIERS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_CHILD_MAX.S b/libc/sysv/consts/_POSIX_CHILD_MAX.S deleted file mode 100644 index 3495a2ca0..000000000 --- a/libc/sysv/consts/_POSIX_CHILD_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CHILD_MAX,25,25,25,25,25,0 diff --git a/libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S b/libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S deleted file mode 100644 index 4ff35272a..000000000 --- a/libc/sysv/consts/_POSIX_CHOWN_RESTRICTED.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CHOWN_RESTRICTED,0,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_CLOCKRES_MIN.S b/libc/sysv/consts/_POSIX_CLOCKRES_MIN.S deleted file mode 100644 index 3bd0857d5..000000000 --- a/libc/sysv/consts/_POSIX_CLOCKRES_MIN.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CLOCKRES_MIN,0x01312d00,0,0x01312d00,0x01312d00,0x01312d00,0 diff --git a/libc/sysv/consts/_POSIX_CLOCK_SELECTION.S b/libc/sysv/consts/_POSIX_CLOCK_SELECTION.S deleted file mode 100644 index c6894a953..000000000 --- a/libc/sysv/consts/_POSIX_CLOCK_SELECTION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CLOCK_SELECTION,0x031069,-1,-1,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_CPUTIME.S b/libc/sysv/consts/_POSIX_CPUTIME.S deleted file mode 100644 index 8be5a812d..000000000 --- a/libc/sysv/consts/_POSIX_CPUTIME.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_CPUTIME,0,-1,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S b/libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S deleted file mode 100644 index 847687eea..000000000 --- a/libc/sysv/consts/_POSIX_DELAYTIMER_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_DELAYTIMER_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_FSYNC.S b/libc/sysv/consts/_POSIX_FSYNC.S deleted file mode 100644 index 98734a460..000000000 --- a/libc/sysv/consts/_POSIX_FSYNC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_FSYNC,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_HOST_NAME_MAX.S b/libc/sysv/consts/_POSIX_HOST_NAME_MAX.S deleted file mode 100644 index cdd32150d..000000000 --- a/libc/sysv/consts/_POSIX_HOST_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_HOST_NAME_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_IPV6.S b/libc/sysv/consts/_POSIX_IPV6.S deleted file mode 100644 index b77770aab..000000000 --- a/libc/sysv/consts/_POSIX_IPV6.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_IPV6,0x031069,0x030db0,0,0,0,0 diff --git a/libc/sysv/consts/_POSIX_JOB_CONTROL.S b/libc/sysv/consts/_POSIX_JOB_CONTROL.S deleted file mode 100644 index 6075f5486..000000000 --- a/libc/sysv/consts/_POSIX_JOB_CONTROL.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_JOB_CONTROL,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_LINK_MAX.S b/libc/sysv/consts/_POSIX_LINK_MAX.S deleted file mode 100644 index b4e062ba0..000000000 --- a/libc/sysv/consts/_POSIX_LINK_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_LINK_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S b/libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S deleted file mode 100644 index deb4123ba..000000000 --- a/libc/sysv/consts/_POSIX_LOGIN_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_LOGIN_NAME_MAX,9,9,9,9,9,0 diff --git a/libc/sysv/consts/_POSIX_MAPPED_FILES.S b/libc/sysv/consts/_POSIX_MAPPED_FILES.S deleted file mode 100644 index 20e7b821c..000000000 --- a/libc/sysv/consts/_POSIX_MAPPED_FILES.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MAPPED_FILES,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MAX_CANON.S b/libc/sysv/consts/_POSIX_MAX_CANON.S deleted file mode 100644 index 1abaa3203..000000000 --- a/libc/sysv/consts/_POSIX_MAX_CANON.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MAX_CANON,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_MAX_INPUT.S b/libc/sysv/consts/_POSIX_MAX_INPUT.S deleted file mode 100644 index cecc9f5a7..000000000 --- a/libc/sysv/consts/_POSIX_MAX_INPUT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MAX_INPUT,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_MEMLOCK.S b/libc/sysv/consts/_POSIX_MEMLOCK.S deleted file mode 100644 index 351263a05..000000000 --- a/libc/sysv/consts/_POSIX_MEMLOCK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MEMLOCK,0x031069,-1,-1,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S b/libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S deleted file mode 100644 index 5dbf7e4e6..000000000 --- a/libc/sysv/consts/_POSIX_MEMLOCK_RANGE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MEMLOCK_RANGE,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S b/libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S deleted file mode 100644 index 12a14fdbd..000000000 --- a/libc/sysv/consts/_POSIX_MEMORY_PROTECTION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MEMORY_PROTECTION,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MESSAGE_PASSING.S b/libc/sysv/consts/_POSIX_MESSAGE_PASSING.S deleted file mode 100644 index d5d784672..000000000 --- a/libc/sysv/consts/_POSIX_MESSAGE_PASSING.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MESSAGE_PASSING,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S b/libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S deleted file mode 100644 index 06db4eb35..000000000 --- a/libc/sysv/consts/_POSIX_MONOTONIC_CLOCK.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MONOTONIC_CLOCK,0,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S b/libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S deleted file mode 100644 index 0ea3ac38e..000000000 --- a/libc/sysv/consts/_POSIX_MQ_OPEN_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MQ_OPEN_MAX,8,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S b/libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S deleted file mode 100644 index 36272de45..000000000 --- a/libc/sysv/consts/_POSIX_MQ_PRIO_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_MQ_PRIO_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_NAME_MAX.S b/libc/sysv/consts/_POSIX_NAME_MAX.S deleted file mode 100644 index a7a828bb7..000000000 --- a/libc/sysv/consts/_POSIX_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_NAME_MAX,14,14,14,14,14,14 diff --git a/libc/sysv/consts/_POSIX_NGROUPS_MAX.S b/libc/sysv/consts/_POSIX_NGROUPS_MAX.S deleted file mode 100644 index 7ba077af6..000000000 --- a/libc/sysv/consts/_POSIX_NGROUPS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_NGROUPS_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_NO_TRUNC.S b/libc/sysv/consts/_POSIX_NO_TRUNC.S deleted file mode 100644 index 9971eb2d7..000000000 --- a/libc/sysv/consts/_POSIX_NO_TRUNC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_NO_TRUNC,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_OPEN_MAX.S b/libc/sysv/consts/_POSIX_OPEN_MAX.S deleted file mode 100644 index 6c9468640..000000000 --- a/libc/sysv/consts/_POSIX_OPEN_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_OPEN_MAX,20,20,20,20,20,20 diff --git a/libc/sysv/consts/_POSIX_PATH_MAX.S b/libc/sysv/consts/_POSIX_PATH_MAX.S deleted file mode 100644 index 909d3c2c7..000000000 --- a/libc/sysv/consts/_POSIX_PATH_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_PATH_MAX,255,255,255,255,255,255 diff --git a/libc/sysv/consts/_POSIX_PIPE_BUF.S b/libc/sysv/consts/_POSIX_PIPE_BUF.S deleted file mode 100644 index c831974fe..000000000 --- a/libc/sysv/consts/_POSIX_PIPE_BUF.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_PIPE_BUF,0x0200,0x0200,0x0200,0x0200,0x0200,0 diff --git a/libc/sysv/consts/_POSIX_RAW_SOCKETS.S b/libc/sysv/consts/_POSIX_RAW_SOCKETS.S deleted file mode 100644 index 9f4fc94b1..000000000 --- a/libc/sysv/consts/_POSIX_RAW_SOCKETS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_RAW_SOCKETS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S b/libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S deleted file mode 100644 index a1a4cc1c7..000000000 --- a/libc/sysv/consts/_POSIX_READER_WRITER_LOCKS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_READER_WRITER_LOCKS,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S b/libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S deleted file mode 100644 index be894edb5..000000000 --- a/libc/sysv/consts/_POSIX_REALTIME_SIGNALS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_REALTIME_SIGNALS,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_REGEXP.S b/libc/sysv/consts/_POSIX_REGEXP.S deleted file mode 100644 index ce4e54768..000000000 --- a/libc/sysv/consts/_POSIX_REGEXP.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_REGEXP,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_RE_DUP_MAX.S b/libc/sysv/consts/_POSIX_RE_DUP_MAX.S deleted file mode 100644 index bfb673a04..000000000 --- a/libc/sysv/consts/_POSIX_RE_DUP_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_RE_DUP_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_RTSIG_MAX.S b/libc/sysv/consts/_POSIX_RTSIG_MAX.S deleted file mode 100644 index 2241f715a..000000000 --- a/libc/sysv/consts/_POSIX_RTSIG_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_RTSIG_MAX,8,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_SAVED_IDS.S b/libc/sysv/consts/_POSIX_SAVED_IDS.S deleted file mode 100644 index 82fffc4ea..000000000 --- a/libc/sysv/consts/_POSIX_SAVED_IDS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SAVED_IDS,1,0x030db0,0,1,1,0 diff --git a/libc/sysv/consts/_POSIX_SEMAPHORES.S b/libc/sysv/consts/_POSIX_SEMAPHORES.S deleted file mode 100644 index de3e8eaad..000000000 --- a/libc/sysv/consts/_POSIX_SEMAPHORES.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SEMAPHORES,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S b/libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S deleted file mode 100644 index e9effe5ee..000000000 --- a/libc/sysv/consts/_POSIX_SEM_NSEMS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SEM_NSEMS_MAX,0x0100,0x0100,0x0100,0x0100,0x0100,0 diff --git a/libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S b/libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S deleted file mode 100644 index 763e10aff..000000000 --- a/libc/sysv/consts/_POSIX_SEM_VALUE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SEM_VALUE_MAX,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0 diff --git a/libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S b/libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S deleted file mode 100644 index a775360c0..000000000 --- a/libc/sysv/consts/_POSIX_SHARED_MEMORY_OBJECTS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SHARED_MEMORY_OBJECTS,0x031069,-1,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_SHELL.S b/libc/sysv/consts/_POSIX_SHELL.S deleted file mode 100644 index ea364b705..000000000 --- a/libc/sysv/consts/_POSIX_SHELL.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SHELL,1,0x030db0,1,1,1,0 diff --git a/libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S b/libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S deleted file mode 100644 index b6e88a5ee..000000000 --- a/libc/sysv/consts/_POSIX_SIGQUEUE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SIGQUEUE_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_SPAWN.S b/libc/sysv/consts/_POSIX_SPAWN.S deleted file mode 100644 index 5cc922cef..000000000 --- a/libc/sysv/consts/_POSIX_SPAWN.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SPAWN,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_SPIN_LOCKS.S b/libc/sysv/consts/_POSIX_SPIN_LOCKS.S deleted file mode 100644 index 3da50e838..000000000 --- a/libc/sysv/consts/_POSIX_SPIN_LOCKS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SPIN_LOCKS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_SSIZE_MAX.S b/libc/sysv/consts/_POSIX_SSIZE_MAX.S deleted file mode 100644 index e06159aff..000000000 --- a/libc/sysv/consts/_POSIX_SSIZE_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SSIZE_MAX,0x7fff,0x7fff,0x7fff,0x7fff,0x7fff,0 diff --git a/libc/sysv/consts/_POSIX_SS_REPL_MAX.S b/libc/sysv/consts/_POSIX_SS_REPL_MAX.S deleted file mode 100644 index 3776e47a7..000000000 --- a/libc/sysv/consts/_POSIX_SS_REPL_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SS_REPL_MAX,0,4,4,0,0,0 diff --git a/libc/sysv/consts/_POSIX_STREAM_MAX.S b/libc/sysv/consts/_POSIX_STREAM_MAX.S deleted file mode 100644 index 4067e637b..000000000 --- a/libc/sysv/consts/_POSIX_STREAM_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_STREAM_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_SYMLINK_MAX.S b/libc/sysv/consts/_POSIX_SYMLINK_MAX.S deleted file mode 100644 index ca36778c4..000000000 --- a/libc/sysv/consts/_POSIX_SYMLINK_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SYMLINK_MAX,255,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_SYMLOOP_MAX.S b/libc/sysv/consts/_POSIX_SYMLOOP_MAX.S deleted file mode 100644 index 981d87de7..000000000 --- a/libc/sysv/consts/_POSIX_SYMLOOP_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_SYMLOOP_MAX,8,8,8,8,8,0 diff --git a/libc/sysv/consts/_POSIX_THREADS.S b/libc/sysv/consts/_POSIX_THREADS.S deleted file mode 100644 index 8198becab..000000000 --- a/libc/sysv/consts/_POSIX_THREADS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREADS,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S b/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S deleted file mode 100644 index fc4fc4400..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKADDR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_ATTR_STACKADDR,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S b/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S deleted file mode 100644 index 7fec49e45..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_ATTR_STACKSIZE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_ATTR_STACKSIZE,0x031069,0x030db0,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_CPUTIME.S b/libc/sysv/consts/_POSIX_THREAD_CPUTIME.S deleted file mode 100644 index 1ceab7a7e..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_CPUTIME.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_CPUTIME,0,-1,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S b/libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S deleted file mode 100644 index cfb21f89a..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_DESTRUCTOR_ITERATIONS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_DESTRUCTOR_ITERATIONS,4,4,4,4,4,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S b/libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S deleted file mode 100644 index f63502d07..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_KEYS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_KEYS_MAX,0x80,0x80,0x80,0x80,0x80,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S b/libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S deleted file mode 100644 index ce2a15df8..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_PRIORITY_SCHEDULING.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_PRIORITY_SCHEDULING,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S b/libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S deleted file mode 100644 index f79252755..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_PROCESS_SHARED.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_PROCESS_SHARED,0x031069,0x030db0,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S b/libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S deleted file mode 100644 index e1d5d06fe..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_SAFE_FUNCTIONS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_SAFE_FUNCTIONS,0x031069,0x030db0,-1,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S b/libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S deleted file mode 100644 index 2fcdce6ed..000000000 --- a/libc/sysv/consts/_POSIX_THREAD_THREADS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_THREAD_THREADS_MAX,0x40,0x40,0x40,4,4,0 diff --git a/libc/sysv/consts/_POSIX_TIMEOUTS.S b/libc/sysv/consts/_POSIX_TIMEOUTS.S deleted file mode 100644 index dddedfedd..000000000 --- a/libc/sysv/consts/_POSIX_TIMEOUTS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TIMEOUTS,0x031069,-1,0x030db0,0x030db0,0x030db0,0 diff --git a/libc/sysv/consts/_POSIX_TIMERS.S b/libc/sysv/consts/_POSIX_TIMERS.S deleted file mode 100644 index 7f4c8ebed..000000000 --- a/libc/sysv/consts/_POSIX_TIMERS.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TIMERS,0x031069,-1,0x030db0,-1,-1,0 diff --git a/libc/sysv/consts/_POSIX_TIMER_MAX.S b/libc/sysv/consts/_POSIX_TIMER_MAX.S deleted file mode 100644 index d7d3b7111..000000000 --- a/libc/sysv/consts/_POSIX_TIMER_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TIMER_MAX,0x20,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S b/libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S deleted file mode 100644 index 56c6f2efb..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_EVENT_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_EVENT_NAME_MAX,0,30,30,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S b/libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S deleted file mode 100644 index c576681b8..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_NAME_MAX,0,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S b/libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S deleted file mode 100644 index 6a3a96249..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_SYS_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_SYS_MAX,0,8,8,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S b/libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S deleted file mode 100644 index 3beba770f..000000000 --- a/libc/sysv/consts/_POSIX_TRACE_USER_EVENT_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TRACE_USER_EVENT_MAX,0,0x20,0x20,0,0,0 diff --git a/libc/sysv/consts/_POSIX_TTY_NAME_MAX.S b/libc/sysv/consts/_POSIX_TTY_NAME_MAX.S deleted file mode 100644 index 900f1b86e..000000000 --- a/libc/sysv/consts/_POSIX_TTY_NAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TTY_NAME_MAX,9,9,9,9,9,0 diff --git a/libc/sysv/consts/_POSIX_TZNAME_MAX.S b/libc/sysv/consts/_POSIX_TZNAME_MAX.S deleted file mode 100644 index 0ce6a6993..000000000 --- a/libc/sysv/consts/_POSIX_TZNAME_MAX.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_TZNAME_MAX,6,6,6,6,6,0 diff --git a/libc/sysv/consts/_POSIX_V6_LP64_OFF64.S b/libc/sysv/consts/_POSIX_V6_LP64_OFF64.S deleted file mode 100644 index 3a3c260f4..000000000 --- a/libc/sysv/consts/_POSIX_V6_LP64_OFF64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_V6_LP64_OFF64,1,1,0,0,0,0 diff --git a/libc/sysv/consts/_POSIX_V7_LP64_OFF64.S b/libc/sysv/consts/_POSIX_V7_LP64_OFF64.S deleted file mode 100644 index 351dd64b4..000000000 --- a/libc/sysv/consts/_POSIX_V7_LP64_OFF64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_V7_LP64_OFF64,1,1,0,0,0,0 diff --git a/libc/sysv/consts/_POSIX_VDISABLE.S b/libc/sysv/consts/_POSIX_VDISABLE.S deleted file mode 100644 index b12b30623..000000000 --- a/libc/sysv/consts/_POSIX_VDISABLE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_VDISABLE,0,255,255,255,255,0 diff --git a/libc/sysv/consts/_POSIX_VERSION.S b/libc/sysv/consts/_POSIX_VERSION.S deleted file mode 100644 index 34fb98fd9..000000000 --- a/libc/sysv/consts/_POSIX_VERSION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon posix,_POSIX_VERSION,0x031069,0x030db0,0x030db0,0x031069,0x031069,0 diff --git a/libc/sysv/consts/_posix.h b/libc/sysv/consts/_posix.h index b96c0f051..66f9062b7 100644 --- a/libc/sysv/consts/_posix.h +++ b/libc/sysv/consts/_posix.h @@ -1,168 +1,59 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ -#include "libc/runtime/symbolic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ -extern const long _POSIX_ADVISORY_INFO; -extern const long _POSIX_AIO_LISTIO_MAX; -extern const long _POSIX_AIO_MAX; -extern const long _POSIX_ARG_MAX; -extern const long _POSIX_ASYNCHRONOUS_IO; -extern const long _POSIX_BARRIERS; -extern const long _POSIX_CHILD_MAX; -extern const long _POSIX_CHOWN_RESTRICTED; -extern const long _POSIX_CLOCKRES_MIN; -extern const long _POSIX_CLOCK_SELECTION; -extern const long _POSIX_CPUTIME; -extern const long _POSIX_DELAYTIMER_MAX; -extern const long _POSIX_FSYNC; -extern const long _POSIX_HOST_NAME_MAX; -extern const long _POSIX_IPV6; -extern const long _POSIX_JOB_CONTROL; -extern const long _POSIX_LINK_MAX; -extern const long _POSIX_LOGIN_NAME_MAX; -extern const long _POSIX_MAPPED_FILES; -extern const long _POSIX_MAX_CANON; -extern const long _POSIX_MAX_INPUT; -extern const long _POSIX_MEMLOCK; -extern const long _POSIX_MEMLOCK_RANGE; -extern const long _POSIX_MEMORY_PROTECTION; -extern const long _POSIX_MESSAGE_PASSING; -extern const long _POSIX_MONOTONIC_CLOCK; -extern const long _POSIX_MQ_OPEN_MAX; -extern const long _POSIX_MQ_PRIO_MAX; -extern const long _POSIX_NAME_MAX; -extern const long _POSIX_NGROUPS_MAX; -extern const long _POSIX_NO_TRUNC; -extern const long _POSIX_OPEN_MAX; -extern const long _POSIX_PATH_MAX; -extern const long _POSIX_PIPE_BUF; -extern const long _POSIX_RAW_SOCKETS; -extern const long _POSIX_READER_WRITER_LOCKS; -extern const long _POSIX_REALTIME_SIGNALS; -extern const long _POSIX_REGEXP; -extern const long _POSIX_RE_DUP_MAX; -extern const long _POSIX_RTSIG_MAX; -extern const long _POSIX_SAVED_IDS; -extern const long _POSIX_SEMAPHORES; -extern const long _POSIX_SEM_NSEMS_MAX; -extern const long _POSIX_SEM_VALUE_MAX; -extern const long _POSIX_SHARED_MEMORY_OBJECTS; -extern const long _POSIX_SHELL; -extern const long _POSIX_SIGQUEUE_MAX; -extern const long _POSIX_SPAWN; -extern const long _POSIX_SPIN_LOCKS; -extern const long _POSIX_SSIZE_MAX; -extern const long _POSIX_SS_REPL_MAX; -extern const long _POSIX_STREAM_MAX; -extern const long _POSIX_SYMLINK_MAX; -extern const long _POSIX_SYMLOOP_MAX; -extern const long _POSIX_THREADS; -extern const long _POSIX_THREAD_ATTR_STACKADDR; -extern const long _POSIX_THREAD_ATTR_STACKSIZE; -extern const long _POSIX_THREAD_CPUTIME; -extern const long _POSIX_THREAD_DESTRUCTOR_ITERATIONS; -extern const long _POSIX_THREAD_KEYS_MAX; -extern const long _POSIX_THREAD_PRIORITY_SCHEDULING; -extern const long _POSIX_THREAD_PROCESS_SHARED; -extern const long _POSIX_THREAD_SAFE_FUNCTIONS; -extern const long _POSIX_THREAD_THREADS_MAX; -extern const long _POSIX_TIMEOUTS; -extern const long _POSIX_TIMERS; -extern const long _POSIX_TIMER_MAX; -extern const long _POSIX_TRACE_EVENT_NAME_MAX; -extern const long _POSIX_TRACE_NAME_MAX; -extern const long _POSIX_TRACE_SYS_MAX; -extern const long _POSIX_TRACE_USER_EVENT_MAX; -extern const long _POSIX_TTY_NAME_MAX; -extern const long _POSIX_TZNAME_MAX; -extern const long _POSIX_V6_LP64_OFF64; -extern const long _POSIX_V7_LP64_OFF64; -extern const long _POSIX_VDISABLE; -extern const long _POSIX_VERSION; +/* The Open Group Base Specifications Issue 7, 2018 edition */ +/* IEEE Std 1003.1-2017 (Revision of IEEE Std 1003.1-2008) */ -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#define _POSIX_AIO_LISTIO_MAX 2 +#define _POSIX_AIO_MAX 1 +#define _POSIX_ARG_MAX 4096 +#define _POSIX_CHILD_MAX 25 +#define _POSIX_DELAYTIMER_MAX 32 +#define _POSIX_HOST_NAME_MAX 255 +#define _POSIX_LINK_MAX 8 +#define _POSIX_LOGIN_NAME_MAX 9 +#define _POSIX_MAX_CANON 255 +#define _POSIX_MAX_INPUT 255 +#define _POSIX_MQ_OPEN_MAX 8 +#define _POSIX_MQ_PRIO_MAX 32 +#define _POSIX_NAME_MAX 14 +#define _POSIX_NGROUPS_MAX 8 +#define _POSIX_OPEN_MAX 20 +#define _POSIX_PATH_MAX 256 +#define _POSIX_PIPE_BUF 512 +#define _POSIX_RE_DUP_MAX 255 +#define _POSIX_RTSIG_MAX 8 +#define _POSIX_SEM_NSEMS_MAX 256 +#define _POSIX_SEM_VALUE_MAX 32767 +#define _POSIX_SIGQUEUE_MAX 32 +#define _POSIX_SSIZE_MAX 32767 +#define _POSIX_SS_REPL_MAX 4 +#define _POSIX_STREAM_MAX 8 +#define _POSIX_SYMLINK_MAX 255 +#define _POSIX_SYMLOOP_MAX 8 +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 +#define _POSIX_THREAD_KEYS_MAX 128 +#define _POSIX_THREAD_THREADS_MAX 64 +#define _POSIX_TIMER_MAX 32 +#define _POSIX_TRACE_EVENT_NAME_MAX 30 +#define _POSIX_TRACE_NAME_MAX 8 +#define _POSIX_TRACE_SYS_MAX 8 +#define _POSIX_TRACE_USER_EVENT_MAX 32 +#define _POSIX_TTY_NAME_MAX 9 +#define _POSIX_TZNAME_MAX 6 -#define _POSIX_ADVISORY_INFO SYMBOLIC(_POSIX_ADVISORY_INFO) -#define _POSIX_AIO_LISTIO_MAX SYMBOLIC(_POSIX_AIO_LISTIO_MAX) -#define _POSIX_AIO_MAX SYMBOLIC(_POSIX_AIO_MAX) -#define _POSIX_ARG_MAX SYMBOLIC(_POSIX_ARG_MAX) -#define _POSIX_ASYNCHRONOUS_IO SYMBOLIC(_POSIX_ASYNCHRONOUS_IO) -#define _POSIX_BARRIERS SYMBOLIC(_POSIX_BARRIERS) -#define _POSIX_CHILD_MAX SYMBOLIC(_POSIX_CHILD_MAX) -#define _POSIX_CHOWN_RESTRICTED SYMBOLIC(_POSIX_CHOWN_RESTRICTED) -#define _POSIX_CLOCKRES_MIN SYMBOLIC(_POSIX_CLOCKRES_MIN) -#define _POSIX_CLOCK_SELECTION SYMBOLIC(_POSIX_CLOCK_SELECTION) -#define _POSIX_CPUTIME SYMBOLIC(_POSIX_CPUTIME) -#define _POSIX_DELAYTIMER_MAX SYMBOLIC(_POSIX_DELAYTIMER_MAX) -#define _POSIX_FSYNC SYMBOLIC(_POSIX_FSYNC) -#define _POSIX_HOST_NAME_MAX SYMBOLIC(_POSIX_HOST_NAME_MAX) -#define _POSIX_IPV6 SYMBOLIC(_POSIX_IPV6) -#define _POSIX_JOB_CONTROL SYMBOLIC(_POSIX_JOB_CONTROL) -#define _POSIX_LINK_MAX SYMBOLIC(_POSIX_LINK_MAX) -#define _POSIX_LOGIN_NAME_MAX SYMBOLIC(_POSIX_LOGIN_NAME_MAX) -#define _POSIX_MAPPED_FILES SYMBOLIC(_POSIX_MAPPED_FILES) -#define _POSIX_MAX_CANON SYMBOLIC(_POSIX_MAX_CANON) -#define _POSIX_MAX_INPUT SYMBOLIC(_POSIX_MAX_INPUT) -#define _POSIX_MEMLOCK SYMBOLIC(_POSIX_MEMLOCK) -#define _POSIX_MEMLOCK_RANGE SYMBOLIC(_POSIX_MEMLOCK_RANGE) -#define _POSIX_MEMORY_PROTECTION SYMBOLIC(_POSIX_MEMORY_PROTECTION) -#define _POSIX_MESSAGE_PASSING SYMBOLIC(_POSIX_MESSAGE_PASSING) -#define _POSIX_MONOTONIC_CLOCK SYMBOLIC(_POSIX_MONOTONIC_CLOCK) -#define _POSIX_MQ_OPEN_MAX SYMBOLIC(_POSIX_MQ_OPEN_MAX) -#define _POSIX_MQ_PRIO_MAX SYMBOLIC(_POSIX_MQ_PRIO_MAX) -#define _POSIX_NAME_MAX SYMBOLIC(_POSIX_NAME_MAX) -#define _POSIX_NGROUPS_MAX SYMBOLIC(_POSIX_NGROUPS_MAX) -#define _POSIX_NO_TRUNC SYMBOLIC(_POSIX_NO_TRUNC) -#define _POSIX_OPEN_MAX SYMBOLIC(_POSIX_OPEN_MAX) -#define _POSIX_PATH_MAX SYMBOLIC(_POSIX_PATH_MAX) -#define _POSIX_PIPE_BUF SYMBOLIC(_POSIX_PIPE_BUF) -#define _POSIX_RAW_SOCKETS SYMBOLIC(_POSIX_RAW_SOCKETS) -#define _POSIX_READER_WRITER_LOCKS SYMBOLIC(_POSIX_READER_WRITER_LOCKS) -#define _POSIX_REALTIME_SIGNALS SYMBOLIC(_POSIX_REALTIME_SIGNALS) -#define _POSIX_REGEXP SYMBOLIC(_POSIX_REGEXP) -#define _POSIX_RE_DUP_MAX SYMBOLIC(_POSIX_RE_DUP_MAX) -#define _POSIX_RTSIG_MAX SYMBOLIC(_POSIX_RTSIG_MAX) -#define _POSIX_SAVED_IDS SYMBOLIC(_POSIX_SAVED_IDS) -#define _POSIX_SEMAPHORES SYMBOLIC(_POSIX_SEMAPHORES) -#define _POSIX_SEM_NSEMS_MAX SYMBOLIC(_POSIX_SEM_NSEMS_MAX) -#define _POSIX_SEM_VALUE_MAX SYMBOLIC(_POSIX_SEM_VALUE_MAX) -#define _POSIX_SHARED_MEMORY_OBJECTS SYMBOLIC(_POSIX_SHARED_MEMORY_OBJECTS) -#define _POSIX_SHELL SYMBOLIC(_POSIX_SHELL) -#define _POSIX_SIGQUEUE_MAX SYMBOLIC(_POSIX_SIGQUEUE_MAX) -#define _POSIX_SPAWN SYMBOLIC(_POSIX_SPAWN) -#define _POSIX_SPIN_LOCKS SYMBOLIC(_POSIX_SPIN_LOCKS) -#define _POSIX_SSIZE_MAX SYMBOLIC(_POSIX_SSIZE_MAX) -#define _POSIX_SS_REPL_MAX SYMBOLIC(_POSIX_SS_REPL_MAX) -#define _POSIX_STREAM_MAX SYMBOLIC(_POSIX_STREAM_MAX) -#define _POSIX_SYMLINK_MAX SYMBOLIC(_POSIX_SYMLINK_MAX) -#define _POSIX_SYMLOOP_MAX SYMBOLIC(_POSIX_SYMLOOP_MAX) -#define _POSIX_THREADS SYMBOLIC(_POSIX_THREADS) -#define _POSIX_THREAD_ATTR_STACKADDR SYMBOLIC(_POSIX_THREAD_ATTR_STACKADDR) -#define _POSIX_THREAD_ATTR_STACKSIZE SYMBOLIC(_POSIX_THREAD_ATTR_STACKSIZE) -#define _POSIX_THREAD_CPUTIME SYMBOLIC(_POSIX_THREAD_CPUTIME) -#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS \ - SYMBOLIC(_POSIX_THREAD_DESTRUCTOR_ITERATIONS) -#define _POSIX_THREAD_KEYS_MAX SYMBOLIC(_POSIX_THREAD_KEYS_MAX) -#define _POSIX_THREAD_PRIORITY_SCHEDULING \ - SYMBOLIC(_POSIX_THREAD_PRIORITY_SCHEDULING) -#define _POSIX_THREAD_PROCESS_SHARED SYMBOLIC(_POSIX_THREAD_PROCESS_SHARED) -#define _POSIX_THREAD_SAFE_FUNCTIONS SYMBOLIC(_POSIX_THREAD_SAFE_FUNCTIONS) -#define _POSIX_THREAD_THREADS_MAX SYMBOLIC(_POSIX_THREAD_THREADS_MAX) -#define _POSIX_TIMEOUTS SYMBOLIC(_POSIX_TIMEOUTS) -#define _POSIX_TIMERS SYMBOLIC(_POSIX_TIMERS) -#define _POSIX_TIMER_MAX SYMBOLIC(_POSIX_TIMER_MAX) -#define _POSIX_TRACE_EVENT_NAME_MAX SYMBOLIC(_POSIX_TRACE_EVENT_NAME_MAX) -#define _POSIX_TRACE_NAME_MAX SYMBOLIC(_POSIX_TRACE_NAME_MAX) -#define _POSIX_TRACE_SYS_MAX SYMBOLIC(_POSIX_TRACE_SYS_MAX) -#define _POSIX_TRACE_USER_EVENT_MAX SYMBOLIC(_POSIX_TRACE_USER_EVENT_MAX) -#define _POSIX_TTY_NAME_MAX SYMBOLIC(_POSIX_TTY_NAME_MAX) -#define _POSIX_TZNAME_MAX SYMBOLIC(_POSIX_TZNAME_MAX) -#define _POSIX_V6_LP64_OFF64 SYMBOLIC(_POSIX_V6_LP64_OFF64) -#define _POSIX_V7_LP64_OFF64 SYMBOLIC(_POSIX_V7_LP64_OFF64) -#define _POSIX_VDISABLE SYMBOLIC(_POSIX_VDISABLE) -#define _POSIX_VERSION SYMBOLIC(_POSIX_VERSION) +#define _POSIX2_BC_BASE_MAX 99 +#define _POSIX2_BC_DIM_MAX 2048 +#define _POSIX2_BC_SCALE_MAX 99 +#define _POSIX2_BC_STRING_MAX 1000 +#define _POSIX2_CHARCLASS_NAME_MAX 14 +#define _POSIX2_COLL_WEIGHTS_MAX 2 +#define _POSIX2_EXPR_NEST_MAX 32 +#define _POSIX2_LINE_MAX 2048 +#define _POSIX2_RE_DUP_MAX 255 + +#define _XOPEN_IOV_MAX 16 +#define _XOPEN_NAME_MAX 255 +#define _XOPEN_PATH_MAX 1024 #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS__POSIX_H_ */ diff --git a/libc/sysv/consts/exit.h b/libc/sysv/consts/exit.h index b980539d0..70e67d83f 100644 --- a/libc/sysv/consts/exit.h +++ b/libc/sysv/consts/exit.h @@ -1,16 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ -#include "libc/runtime/symbolic.h" -#define EXIT_FAILURE SYMBOLIC(EXIT_FAILURE) -#define EXIT_SUCCESS SYMBOLIC(EXIT_SUCCESS) +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -extern const long EXIT_FAILURE; -extern const long EXIT_SUCCESS; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ */ diff --git a/libc/sysv/consts/limits.h b/libc/sysv/consts/limits.h index 4a5476963..b01418b22 100644 --- a/libc/sysv/consts/limits.h +++ b/libc/sysv/consts/limits.h @@ -4,11 +4,19 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern const long PIPE_BUF; +extern const int PIPE_BUF; +extern const int _ARG_MAX; +extern const int _NAME_MAX; +extern const int _PATH_MAX; +extern const int _NSIG; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define PIPE_BUF SYMBOLIC(PIPE_BUF) +#define PIPE_BUF SYMBOLIC(PIPE_BUF) +#define _ARG_MAX SYMBOLIC(_ARG_MAX) +#define _NAME_MAX SYMBOLIC(_NAME_MAX) +#define _PATH_MAX SYMBOLIC(_PATH_MAX) +#define _NSIG SYMBOLIC(_NSIG) #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_LIMITS_H_ */ diff --git a/libc/sysv/consts/nrlinux.h b/libc/sysv/consts/nrlinux.h new file mode 100644 index 000000000..59d6aa8e7 --- /dev/null +++ b/libc/sysv/consts/nrlinux.h @@ -0,0 +1,330 @@ +#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ +#define COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define __NR_linux_exit 0x003c +#define __NR_linux_exit_group 0x00e7 +#define __NR_linux_read 0x0000 +#define __NR_linux_write 0x0001 +#define __NR_linux_open 0x0002 +#define __NR_linux_close 0x0003 +#define __NR_linux_stat 0x0004 +#define __NR_linux_fstat 0x0005 +#define __NR_linux_lstat 0x0006 +#define __NR_linux_poll 0x0007 +#define __NR_linux_ppoll 0x010f +#define __NR_linux_brk 0x000c +#define __NR_linux_sigreturn 0x000f +#define __NR_linux_lseek 0x0008 +#define __NR_linux_mmap 0x0009 +#define __NR_linux_msync 0x001a +#define __NR_linux_mprotect 0x000a +#define __NR_linux_munmap 0x000b +#define __NR_linux_sigaction 0x000d +#define __NR_linux_sigprocmask 0x000e +#define __NR_linux_ioctl 0x0010 +#define __NR_linux_pread 0x0011 +#define __NR_linux_pwrite 0x0012 +#define __NR_linux_readv 0x0013 +#define __NR_linux_writev 0x0014 +#define __NR_linux_access 0x0015 +#define __NR_linux_pipe 0x0016 +#define __NR_linux_select 0x0017 +#define __NR_linux_pselect6 0x010e +#define __NR_linux_sched_yield 0x0018 +#define __NR_linux_mremap 0x0019 +#define __NR_linux_mincore 0x001b +#define __NR_linux_madvise 0x001c +#define __NR_linux_shmget 0x001d +#define __NR_linux_shmat 0x001e +#define __NR_linux_shmctl 0x001f +#define __NR_linux_dup 0x0020 +#define __NR_linux_dup2 0x0021 +#define __NR_linux_pause 0x0022 +#define __NR_linux_nanosleep 0x0023 +#define __NR_linux_getitimer 0x0024 +#define __NR_linux_setitimer 0x0026 +#define __NR_linux_alarm 0x0025 +#define __NR_linux_getpid 0x0027 +#define __NR_linux_sendfile 0x0028 +#define __NR_linux_socket 0x0029 +#define __NR_linux_connect 0x002a +#define __NR_linux_accept 0x002b +#define __NR_linux_sendto 0x002c +#define __NR_linux_recvfrom 0x002d +#define __NR_linux_sendmsg 0x002e +#define __NR_linux_recvmsg 0x002f +#define __NR_linux_shutdown 0x0030 +#define __NR_linux_bind 0x0031 +#define __NR_linux_listen 0x0032 +#define __NR_linux_getsockname 0x0033 +#define __NR_linux_getpeername 0x0034 +#define __NR_linux_socketpair 0x0035 +#define __NR_linux_setsockopt 0x0036 +#define __NR_linux_getsockopt 0x0037 +#define __NR_linux_fork 0x0039 +#define __NR_linux_vfork 0x003a +#define __NR_linux_execve 0x003b +#define __NR_linux_wait4 0x003d +#define __NR_linux_kill 0x003e +#define __NR_linux_clone 0x0038 +#define __NR_linux_tkill 0x00c8 +#define __NR_linux_futex 0x00ca +#define __NR_linux_set_robust_list 0x0111 +#define __NR_linux_get_robust_list 0x0112 +#define __NR_linux_uname 0x003f +#define __NR_linux_semget 0x0040 +#define __NR_linux_semop 0x0041 +#define __NR_linux_semctl 0x0042 +#define __NR_linux_shmdt 0x0043 +#define __NR_linux_msgget 0x0044 +#define __NR_linux_msgsnd 0x0045 +#define __NR_linux_msgrcv 0x0046 +#define __NR_linux_msgctl 0x0047 +#define __NR_linux_fcntl 0x0048 +#define __NR_linux_flock 0x0049 +#define __NR_linux_fsync 0x004a +#define __NR_linux_fdatasync 0x004b +#define __NR_linux_truncate 0x004c +#define __NR_linux_ftruncate 0x004d +#define __NR_linux_getcwd 0x004f +#define __NR_linux_chdir 0x0050 +#define __NR_linux_fchdir 0x0051 +#define __NR_linux_rename 0x0052 +#define __NR_linux_mkdir 0x0053 +#define __NR_linux_rmdir 0x0054 +#define __NR_linux_creat 0x0055 +#define __NR_linux_link 0x0056 +#define __NR_linux_unlink 0x0057 +#define __NR_linux_symlink 0x0058 +#define __NR_linux_readlink 0x0059 +#define __NR_linux_chmod 0x005a +#define __NR_linux_fchmod 0x005b +#define __NR_linux_chown 0x005c +#define __NR_linux_fchown 0x005d +#define __NR_linux_lchown 0x005e +#define __NR_linux_umask 0x005f +#define __NR_linux_gettimeofday 0x0060 +#define __NR_linux_getrlimit 0x0061 +#define __NR_linux_getrusage 0x0062 +#define __NR_linux_sysinfo 0x0063 +#define __NR_linux_times 0x0064 +#define __NR_linux_ptrace 0x0065 +#define __NR_linux_syslog 0x0067 +#define __NR_linux_getuid 0x0066 +#define __NR_linux_getgid 0x0068 +#define __NR_linux_getppid 0x006e +#define __NR_linux_getpgrp 0x006f +#define __NR_linux_setsid 0x0070 +#define __NR_linux_getsid 0x007c +#define __NR_linux_getpgid 0x0079 +#define __NR_linux_setpgid 0x006d +#define __NR_linux_geteuid 0x006b +#define __NR_linux_getegid 0x006c +#define __NR_linux_getgroups 0x0073 +#define __NR_linux_setgroups 0x0074 +#define __NR_linux_setreuid 0x0071 +#define __NR_linux_setregid 0x0072 +#define __NR_linux_setuid 0x0069 +#define __NR_linux_setgid 0x006a +#define __NR_linux_setresuid 0x0075 +#define __NR_linux_setresgid 0x0077 +#define __NR_linux_getresuid 0x0076 +#define __NR_linux_getresgid 0x0078 +#define __NR_linux_sigpending 0x007f +#define __NR_linux_sigsuspend 0x0082 +#define __NR_linux_sigaltstack 0x0083 +#define __NR_linux_mknod 0x0085 +#define __NR_linux_mknodat 0x0103 +#define __NR_linux_statfs 0x0089 +#define __NR_linux_fstatfs 0x008a +#define __NR_linux_getpriority 0x008c +#define __NR_linux_setpriority 0x008d +#define __NR_linux_mlock 0x0095 +#define __NR_linux_munlock 0x0096 +#define __NR_linux_mlockall 0x0097 +#define __NR_linux_munlockall 0x0098 +#define __NR_linux_setrlimit 0x00a0 +#define __NR_linux_chroot 0x00a1 +#define __NR_linux_sync 0x00a2 +#define __NR_linux_acct 0x00a3 +#define __NR_linux_settimeofday 0x00a4 +#define __NR_linux_mount 0x00a5 +#define __NR_linux_reboot 0x00a9 +#define __NR_linux_quotactl 0x00b3 +#define __NR_linux_setfsuid 0x007a +#define __NR_linux_setfsgid 0x007b +#define __NR_linux_capget 0x007d +#define __NR_linux_capset 0x007e +#define __NR_linux_sigtimedwait 0x0080 +#define __NR_linux_rt_sigqueueinfo 0x0081 +#define __NR_linux_personality 0x0087 +#define __NR_linux_ustat 0x0088 +#define __NR_linux_sysfs 0x008b +#define __NR_linux_sched_setparam 0x008e +#define __NR_linux_sched_getparam 0x008f +#define __NR_linux_sched_setscheduler 0x0090 +#define __NR_linux_sched_getscheduler 0x0091 +#define __NR_linux_sched_get_priority_max 0x0092 +#define __NR_linux_sched_get_priority_min 0x0093 +#define __NR_linux_sched_rr_get_interval 0x0094 +#define __NR_linux_vhangup 0x0099 +#define __NR_linux_modify_ldt 0x009a +#define __NR_linux_pivot_root 0x009b +#define __NR_linux__sysctl 0x009c +#define __NR_linux_prctl 0x009d +#define __NR_linux_arch_prctl 0x009e +#define __NR_linux_adjtimex 0x009f +#define __NR_linux_umount2 0x00a6 +#define __NR_linux_swapon 0x00a7 +#define __NR_linux_swapoff 0x00a8 +#define __NR_linux_sethostname 0x00aa +#define __NR_linux_setdomainname 0x00ab +#define __NR_linux_iopl 0x00ac +#define __NR_linux_ioperm 0x00ad +#define __NR_linux_init_module 0x00af +#define __NR_linux_delete_module 0x00b0 +#define __NR_linux_gettid 0x00ba +#define __NR_linux_readahead 0x00bb +#define __NR_linux_setxattr 0x00bc +#define __NR_linux_fsetxattr 0x00be +#define __NR_linux_getxattr 0x00bf +#define __NR_linux_fgetxattr 0x00c1 +#define __NR_linux_listxattr 0x00c2 +#define __NR_linux_flistxattr 0x00c4 +#define __NR_linux_removexattr 0x00c5 +#define __NR_linux_fremovexattr 0x00c7 +#define __NR_linux_lsetxattr 0x00bd +#define __NR_linux_lgetxattr 0x00c0 +#define __NR_linux_llistxattr 0x00c3 +#define __NR_linux_lremovexattr 0x00c6 +#define __NR_linux_sched_setaffinity 0x00cb +#define __NR_linux_sched_getaffinity 0x00cc +#define __NR_linux_io_setup 0x00ce +#define __NR_linux_io_destroy 0x00cf +#define __NR_linux_io_getevents 0x00d0 +#define __NR_linux_io_submit 0x00d1 +#define __NR_linux_io_cancel 0x00d2 +#define __NR_linux_lookup_dcookie 0x00d4 +#define __NR_linux_epoll_create 0x00d5 +#define __NR_linux_epoll_wait 0x00e8 +#define __NR_linux_epoll_ctl 0x00e9 +#define __NR_linux_getdents 0x00d9 +#define __NR_linux_set_tid_address 0x00da +#define __NR_linux_restart_syscall 0x00db +#define __NR_linux_semtimedop 0x00dc +#define __NR_linux_fadvise 0x00dd +#define __NR_linux_timer_create 0x00de +#define __NR_linux_timer_settime 0x00df +#define __NR_linux_timer_gettime 0x00e0 +#define __NR_linux_timer_getoverrun 0x00e1 +#define __NR_linux_timer_delete 0x00e2 +#define __NR_linux_clock_settime 0x00e3 +#define __NR_linux_clock_gettime 0x00e4 +#define __NR_linux_clock_getres 0x00e5 +#define __NR_linux_clock_nanosleep 0x00e6 +#define __NR_linux_tgkill 0x00ea +#define __NR_linux_mbind 0x00ed +#define __NR_linux_set_mempolicy 0x00ee +#define __NR_linux_get_mempolicy 0x00ef +#define __NR_linux_mq_open 0x00f0 +#define __NR_linux_mq_unlink 0x00f1 +#define __NR_linux_mq_timedsend 0x00f2 +#define __NR_linux_mq_timedreceive 0x00f3 +#define __NR_linux_mq_notify 0x00f4 +#define __NR_linux_mq_getsetattr 0x00f5 +#define __NR_linux_kexec_load 0x00f6 +#define __NR_linux_waitid 0x00f7 +#define __NR_linux_add_key 0x00f8 +#define __NR_linux_request_key 0x00f9 +#define __NR_linux_keyctl 0x00fa +#define __NR_linux_ioprio_set 0x00fb +#define __NR_linux_ioprio_get 0x00fc +#define __NR_linux_inotify_init 0x00fd +#define __NR_linux_inotify_add_watch 0x00fe +#define __NR_linux_inotify_rm_watch 0x00ff +#define __NR_linux_openat 0x0101 +#define __NR_linux_mkdirat 0x0102 +#define __NR_linux_fchownat 0x0104 +#define __NR_linux_utime 0x0084 +#define __NR_linux_utimes 0x00eb +#define __NR_linux_futimesat 0x0105 +#define __NR_linux_fstatat 0x0106 +#define __NR_linux_unlinkat 0x0107 +#define __NR_linux_renameat 0x0108 +#define __NR_linux_linkat 0x0109 +#define __NR_linux_symlinkat 0x010a +#define __NR_linux_readlinkat 0x010b +#define __NR_linux_fchmodat 0x010c +#define __NR_linux_faccessat 0x010d +#define __NR_linux_unshare 0x0110 +#define __NR_linux_splice 0x0113 +#define __NR_linux_tee 0x0114 +#define __NR_linux_sync_file_range 0x0115 +#define __NR_linux_vmsplice 0x0116 +#define __NR_linux_migrate_pages 0x0100 +#define __NR_linux_move_pages 0x0117 +#define __NR_linux_preadv 0x0127 +#define __NR_linux_pwritev 0x0128 +#define __NR_linux_utimensat 0x0118 +#define __NR_linux_fallocate 0x011d +#define __NR_linux_accept4 0x0120 +#define __NR_linux_dup3 0x0124 +#define __NR_linux_pipe2 0x0125 +#define __NR_linux_epoll_pwait 0x0119 +#define __NR_linux_epoll_create1 0x0123 +#define __NR_linux_perf_event_open 0x012a +#define __NR_linux_inotify_init1 0x0126 +#define __NR_linux_rt_tgsigqueueinfo 0x0129 +#define __NR_linux_signalfd 0x011a +#define __NR_linux_signalfd4 0x0121 +#define __NR_linux_eventfd 0x011c +#define __NR_linux_eventfd2 0x0122 +#define __NR_linux_timerfd_create 0x011b +#define __NR_linux_timerfd_settime 0x011e +#define __NR_linux_timerfd_gettime 0x011f +#define __NR_linux_recvmmsg 0x012b +#define __NR_linux_fanotify_init 0x012c +#define __NR_linux_fanotify_mark 0x012d +#define __NR_linux_prlimit 0x012e +#define __NR_linux_name_to_handle_at 0x012f +#define __NR_linux_open_by_handle_at 0x0130 +#define __NR_linux_clock_adjtime 0x0131 +#define __NR_linux_syncfs 0x0132 +#define __NR_linux_sendmmsg 0x0133 +#define __NR_linux_setns 0x0134 +#define __NR_linux_getcpu 0x0135 +#define __NR_linux_process_vm_readv 0x0136 +#define __NR_linux_process_vm_writev 0x0137 +#define __NR_linux_kcmp 0x0138 +#define __NR_linux_finit_module 0x0139 +#define __NR_linux_sched_setattr 0x013a +#define __NR_linux_sched_getattr 0x013b +#define __NR_linux_renameat2 0x013c +#define __NR_linux_seccomp 0x013d +#define __NR_linux_getrandom 0x013e +#define __NR_linux_memfd_create 0x013f +#define __NR_linux_kexec_file_load 0x0140 +#define __NR_linux_bpf 0x0141 +#define __NR_linux_execveat 0x0142 +#define __NR_linux_userfaultfd 0x0143 +#define __NR_linux_membarrier 0x0144 +#define __NR_linux_mlock2 0x0145 +#define __NR_linux_copy_file_range 0x0146 +#define __NR_linux_preadv2 0x0147 +#define __NR_linux_pwritev2 0x0148 +#define __NR_linux_pkey_mprotect 0x0149 +#define __NR_linux_pkey_alloc 0x014a +#define __NR_linux_pkey_free 0x014b +#define __NR_linux_statx 0x014c +#define __NR_linux_io_pgetevents 0x014d +#define __NR_linux_rseq 0x014e +#define __NR_linux_pidfd_send_signal 0x01a8 +#define __NR_linux_io_uring_setup 0x01a9 +#define __NR_linux_io_uring_enter 0x01aa +#define __NR_linux_io_uring_register 0x01ab + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_NRLINUX_H_ */ diff --git a/libc/sysv/consts/so.h b/libc/sysv/consts/so.h index e3ec79d74..3c61cf7a0 100644 --- a/libc/sysv/consts/so.h +++ b/libc/sysv/consts/so.h @@ -1,69 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_SO_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_SO_H_ #include "libc/runtime/symbolic.h" - -#define LOCAL_PEERCRED SYMBOLIC(LOCAL_PEERCRED) -#define SO_ACCEPTCONN SYMBOLIC(SO_ACCEPTCONN) -#define SO_ATTACH_BPF SYMBOLIC(SO_ATTACH_BPF) -#define SO_ATTACH_FILTER SYMBOLIC(SO_ATTACH_FILTER) -#define SO_ATTACH_REUSEPORT_CBPF SYMBOLIC(SO_ATTACH_REUSEPORT_CBPF) -#define SO_ATTACH_REUSEPORT_EBPF SYMBOLIC(SO_ATTACH_REUSEPORT_EBPF) -#define SO_BINDTODEVICE SYMBOLIC(SO_BINDTODEVICE) -#define SO_BPF_EXTENSIONS SYMBOLIC(SO_BPF_EXTENSIONS) -#define SO_BROADCAST SYMBOLIC(SO_BROADCAST) -#define SO_BSDCOMPAT SYMBOLIC(SO_BSDCOMPAT) -#define SO_BUSY_POLL SYMBOLIC(SO_BUSY_POLL) -#define SO_CNX_ADVICE SYMBOLIC(SO_CNX_ADVICE) -#define SO_DEBUG SYMBOLIC(SO_DEBUG) -#define SO_DETACH_BPF SYMBOLIC(SO_DETACH_BPF) -#define SO_DETACH_FILTER SYMBOLIC(SO_DETACH_FILTER) -#define SO_DOMAIN SYMBOLIC(SO_DOMAIN) -#define SO_DONTROUTE SYMBOLIC(SO_DONTROUTE) -#define SO_ERROR SYMBOLIC(SO_ERROR) -#define SO_EXCLUSIVEADDRUSE SYMBOLIC(SO_EXCLUSIVEADDRUSE) -#define SO_GET_FILTER SYMBOLIC(SO_GET_FILTER) -#define SO_INCOMING_CPU SYMBOLIC(SO_INCOMING_CPU) -#define SO_KEEPALIVE SYMBOLIC(SO_KEEPALIVE) -#define SO_LINGER SYMBOLIC(SO_LINGER) -#define SO_LOCK_FILTER SYMBOLIC(SO_LOCK_FILTER) -#define SO_MARK SYMBOLIC(SO_MARK) -#define SO_MAX_PACING_RATE SYMBOLIC(SO_MAX_PACING_RATE) -#define SO_NOFCS SYMBOLIC(SO_NOFCS) -#define SO_NO_CHECK SYMBOLIC(SO_NO_CHECK) -#define SO_OOBINLINE SYMBOLIC(SO_OOBINLINE) -#define SO_PASSCRED SYMBOLIC(SO_PASSCRED) -#define SO_PASSSEC SYMBOLIC(SO_PASSSEC) -#define SO_PEEK_OFF SYMBOLIC(SO_PEEK_OFF) -#define SO_PEERCRED SYMBOLIC(SO_PEERCRED) -#define SO_PEERNAME SYMBOLIC(SO_PEERNAME) -#define SO_PEERSEC SYMBOLIC(SO_PEERSEC) -#define SO_PRIORITY SYMBOLIC(SO_PRIORITY) -#define SO_PROTOCOL SYMBOLIC(SO_PROTOCOL) -#define SO_RCVBUF SYMBOLIC(SO_RCVBUF) -#define SO_RCVBUFFORCE SYMBOLIC(SO_RCVBUFFORCE) -#define SO_RCVLOWAT SYMBOLIC(SO_RCVLOWAT) -#define SO_RCVTIMEO SYMBOLIC(SO_RCVTIMEO) -#define SO_REUSEADDR SYMBOLIC(SO_REUSEADDR) -#define SO_REUSEPORT SYMBOLIC(SO_REUSEPORT) -#define SO_RXQ_OVFL SYMBOLIC(SO_RXQ_OVFL) -#define SO_SELECT_ERR_QUEUE SYMBOLIC(SO_SELECT_ERR_QUEUE) -#define SO_SETFIB SYMBOLIC(SO_SETFIB) -#define SO_SNDBUF SYMBOLIC(SO_SNDBUF) -#define SO_SNDBUFFORCE SYMBOLIC(SO_SNDBUFFORCE) -#define SO_SNDLOWAT SYMBOLIC(SO_SNDLOWAT) -#define SO_SNDTIMEO SYMBOLIC(SO_SNDTIMEO) -#define SO_TIMESTAMP SYMBOLIC(SO_TIMESTAMP) -#define SO_TIMESTAMPING SYMBOLIC(SO_TIMESTAMPING) -#define SO_TIMESTAMPNS SYMBOLIC(SO_TIMESTAMPNS) -#define SO_TYPE SYMBOLIC(SO_TYPE) -#define SO_USELOOPBACK SYMBOLIC(SO_USELOOPBACK) -#define SO_WIFI_STATUS SYMBOLIC(SO_WIFI_STATUS) - -#define SO_SECURITY_AUTHENTICATION SYMBOLIC(SO_SECURITY_AUTHENTICATION) -#define SO_SECURITY_ENCRYPTION_NETWORK SYMBOLIC(SO_SECURITY_ENCRYPTION_NETWORK) -#define SO_SECURITY_ENCRYPTION_TRANSPORT \ - SYMBOLIC(SO_SECURITY_ENCRYPTION_TRANSPORT) - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -129,4 +66,68 @@ extern const long SO_WIFI_STATUS; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define SO_DEBUG LITERALLY(1) + +#define LOCAL_PEERCRED SYMBOLIC(LOCAL_PEERCRED) +#define SO_ACCEPTCONN SYMBOLIC(SO_ACCEPTCONN) +#define SO_ATTACH_BPF SYMBOLIC(SO_ATTACH_BPF) +#define SO_ATTACH_FILTER SYMBOLIC(SO_ATTACH_FILTER) +#define SO_ATTACH_REUSEPORT_CBPF SYMBOLIC(SO_ATTACH_REUSEPORT_CBPF) +#define SO_ATTACH_REUSEPORT_EBPF SYMBOLIC(SO_ATTACH_REUSEPORT_EBPF) +#define SO_BINDTODEVICE SYMBOLIC(SO_BINDTODEVICE) +#define SO_BPF_EXTENSIONS SYMBOLIC(SO_BPF_EXTENSIONS) +#define SO_BROADCAST SYMBOLIC(SO_BROADCAST) +#define SO_BSDCOMPAT SYMBOLIC(SO_BSDCOMPAT) +#define SO_BUSY_POLL SYMBOLIC(SO_BUSY_POLL) +#define SO_CNX_ADVICE SYMBOLIC(SO_CNX_ADVICE) +#define SO_DETACH_BPF SYMBOLIC(SO_DETACH_BPF) +#define SO_DETACH_FILTER SYMBOLIC(SO_DETACH_FILTER) +#define SO_DOMAIN SYMBOLIC(SO_DOMAIN) +#define SO_DONTROUTE SYMBOLIC(SO_DONTROUTE) +#define SO_ERROR SYMBOLIC(SO_ERROR) +#define SO_EXCLUSIVEADDRUSE SYMBOLIC(SO_EXCLUSIVEADDRUSE) +#define SO_GET_FILTER SYMBOLIC(SO_GET_FILTER) +#define SO_INCOMING_CPU SYMBOLIC(SO_INCOMING_CPU) +#define SO_KEEPALIVE SYMBOLIC(SO_KEEPALIVE) +#define SO_LINGER SYMBOLIC(SO_LINGER) +#define SO_LOCK_FILTER SYMBOLIC(SO_LOCK_FILTER) +#define SO_MARK SYMBOLIC(SO_MARK) +#define SO_MAX_PACING_RATE SYMBOLIC(SO_MAX_PACING_RATE) +#define SO_NOFCS SYMBOLIC(SO_NOFCS) +#define SO_NO_CHECK SYMBOLIC(SO_NO_CHECK) +#define SO_OOBINLINE SYMBOLIC(SO_OOBINLINE) +#define SO_PASSCRED SYMBOLIC(SO_PASSCRED) +#define SO_PASSSEC SYMBOLIC(SO_PASSSEC) +#define SO_PEEK_OFF SYMBOLIC(SO_PEEK_OFF) +#define SO_PEERCRED SYMBOLIC(SO_PEERCRED) +#define SO_PEERNAME SYMBOLIC(SO_PEERNAME) +#define SO_PEERSEC SYMBOLIC(SO_PEERSEC) +#define SO_PRIORITY SYMBOLIC(SO_PRIORITY) +#define SO_PROTOCOL SYMBOLIC(SO_PROTOCOL) +#define SO_RCVBUF SYMBOLIC(SO_RCVBUF) +#define SO_RCVBUFFORCE SYMBOLIC(SO_RCVBUFFORCE) +#define SO_RCVLOWAT SYMBOLIC(SO_RCVLOWAT) +#define SO_RCVTIMEO SYMBOLIC(SO_RCVTIMEO) +#define SO_REUSEADDR SYMBOLIC(SO_REUSEADDR) +#define SO_REUSEPORT SYMBOLIC(SO_REUSEPORT) +#define SO_RXQ_OVFL SYMBOLIC(SO_RXQ_OVFL) +#define SO_SELECT_ERR_QUEUE SYMBOLIC(SO_SELECT_ERR_QUEUE) +#define SO_SETFIB SYMBOLIC(SO_SETFIB) +#define SO_SNDBUF SYMBOLIC(SO_SNDBUF) +#define SO_SNDBUFFORCE SYMBOLIC(SO_SNDBUFFORCE) +#define SO_SNDLOWAT SYMBOLIC(SO_SNDLOWAT) +#define SO_SNDTIMEO SYMBOLIC(SO_SNDTIMEO) +#define SO_TIMESTAMP SYMBOLIC(SO_TIMESTAMP) +#define SO_TIMESTAMPING SYMBOLIC(SO_TIMESTAMPING) +#define SO_TIMESTAMPNS SYMBOLIC(SO_TIMESTAMPNS) +#define SO_TYPE SYMBOLIC(SO_TYPE) +#define SO_USELOOPBACK SYMBOLIC(SO_USELOOPBACK) +#define SO_WIFI_STATUS SYMBOLIC(SO_WIFI_STATUS) + +#define SO_SECURITY_AUTHENTICATION SYMBOLIC(SO_SECURITY_AUTHENTICATION) +#define SO_SECURITY_ENCRYPTION_NETWORK SYMBOLIC(SO_SECURITY_ENCRYPTION_NETWORK) +#define SO_SECURITY_ENCRYPTION_TRANSPORT \ + SYMBOLIC(SO_SECURITY_ENCRYPTION_TRANSPORT) + #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SO_H_ */ diff --git a/libc/sysv/consts/sol.h b/libc/sysv/consts/sol.h index a24cb776b..f3a1d6f06 100644 --- a/libc/sysv/consts/sol.h +++ b/libc/sysv/consts/sol.h @@ -1,36 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_SOL_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_SOL_H_ #include "libc/runtime/symbolic.h" - -#define SOL_AAL SYMBOLIC(SOL_AAL) -#define SOL_ALG SYMBOLIC(SOL_ALG) -#define SOL_ATM SYMBOLIC(SOL_ATM) -#define SOL_BLUETOOTH SYMBOLIC(SOL_BLUETOOTH) -#define SOL_CAIF SYMBOLIC(SOL_CAIF) -#define SOL_DCCP SYMBOLIC(SOL_DCCP) -#define SOL_DECNET SYMBOLIC(SOL_DECNET) -#define SOL_ICMPV6 SYMBOLIC(SOL_ICMPV6) -#define SOL_IP SYMBOLIC(SOL_IP) -#define SOL_IPV6 SYMBOLIC(SOL_IPV6) -#define SOL_IRDA SYMBOLIC(SOL_IRDA) -#define SOL_IUCV SYMBOLIC(SOL_IUCV) -#define SOL_KCM SYMBOLIC(SOL_KCM) -#define SOL_LLC SYMBOLIC(SOL_LLC) -#define SOL_NETBEUI SYMBOLIC(SOL_NETBEUI) -#define SOL_NETLINK SYMBOLIC(SOL_NETLINK) -#define SOL_NFC SYMBOLIC(SOL_NFC) -#define SOL_PACKET SYMBOLIC(SOL_PACKET) -#define SOL_PNPIPE SYMBOLIC(SOL_PNPIPE) -#define SOL_PPPOL2TP SYMBOLIC(SOL_PPPOL2TP) -#define SOL_RAW SYMBOLIC(SOL_RAW) -#define SOL_RDS SYMBOLIC(SOL_RDS) -#define SOL_RXRPC SYMBOLIC(SOL_RXRPC) -#define SOL_SOCKET SYMBOLIC(SOL_SOCKET) -#define SOL_TCP SYMBOLIC(SOL_TCP) -#define SOL_TIPC SYMBOLIC(SOL_TIPC) -#define SOL_UDP SYMBOLIC(SOL_UDP) -#define SOL_X25 SYMBOLIC(SOL_X25) - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -65,4 +35,35 @@ extern const long SOL_X25; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define SOL_IP LITERALLY(0) +#define SOL_TCP LITERALLY(6) +#define SOL_UDP LITERALLY(17) + +#define SOL_AAL SYMBOLIC(SOL_AAL) +#define SOL_ALG SYMBOLIC(SOL_ALG) +#define SOL_ATM SYMBOLIC(SOL_ATM) +#define SOL_BLUETOOTH SYMBOLIC(SOL_BLUETOOTH) +#define SOL_CAIF SYMBOLIC(SOL_CAIF) +#define SOL_DCCP SYMBOLIC(SOL_DCCP) +#define SOL_DECNET SYMBOLIC(SOL_DECNET) +#define SOL_ICMPV6 SYMBOLIC(SOL_ICMPV6) +#define SOL_IPV6 SYMBOLIC(SOL_IPV6) +#define SOL_IRDA SYMBOLIC(SOL_IRDA) +#define SOL_IUCV SYMBOLIC(SOL_IUCV) +#define SOL_KCM SYMBOLIC(SOL_KCM) +#define SOL_LLC SYMBOLIC(SOL_LLC) +#define SOL_NETBEUI SYMBOLIC(SOL_NETBEUI) +#define SOL_NETLINK SYMBOLIC(SOL_NETLINK) +#define SOL_NFC SYMBOLIC(SOL_NFC) +#define SOL_PACKET SYMBOLIC(SOL_PACKET) +#define SOL_PNPIPE SYMBOLIC(SOL_PNPIPE) +#define SOL_PPPOL2TP SYMBOLIC(SOL_PPPOL2TP) +#define SOL_RAW SYMBOLIC(SOL_RAW) +#define SOL_RDS SYMBOLIC(SOL_RDS) +#define SOL_RXRPC SYMBOLIC(SOL_RXRPC) +#define SOL_SOCKET SYMBOLIC(SOL_SOCKET) +#define SOL_TIPC SYMBOLIC(SOL_TIPC) +#define SOL_X25 SYMBOLIC(SOL_X25) + #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SOL_H_ */ diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 20d6d2da4..bbd647557 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -154,14 +154,14 @@ scall sys_geteuid 0x019019019201906b globl hidden scall sys_getegid 0x02b02b02b202b06c globl hidden scall getgroups 0x04f04f04f204f073 globl scall setgroups 0x0500500502050074 globl -scall setreuid 0x07e07e07e207e071 globl -scall setregid 0x07f07f07f207f072 globl +scall sys_setreuid 0x07e07e07e207e071 globl hidden +scall sys_setregid 0x07f07f07f207f072 globl hidden scall sys_setuid 0x0170170172017069 globl hidden scall sys_setgid 0x0b50b50b520b506a globl hidden scall sys_setresuid 0xfff11a137ffff075 globl hidden # polyfilled for xnu scall sys_setresgid 0xfff11c138ffff077 globl hidden # polyfilled for xnu -scall getresuid 0xfff119168ffff076 globl # semantics aren't well-defined -scall getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined +scall sys_getresuid 0xfff119168ffff076 globl # semantics aren't well-defined +scall sys_getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined scall sigpending 0x124034034203407f globl # a.k.a. rt_sigpending on linux scall sys_sigsuspend 0x12606f155206f082 globl hidden # a.k.a. rt_sigsuspend on Linux; openbsd:byvalue, sigsuspend_nocancel on XNU scall sys_sigaltstack 0x1191200352035083 globl hidden @@ -373,7 +373,7 @@ scall io_uring_setup 0xfffffffffffff1a9 globl # └─ gnu founder richard sta scall io_uring_enter 0xfffffffffffff1aa globl scall io_uring_register 0xfffffffffffff1ab globl #────────────────────────RHEL CLOUD────────────────────────── # ←┬─ red hat terminates community release of enterprise linux circa 2020 -scall pledge 0xfff06cffffffffff globl # └─ online linux services ban the president of united states of america +scall sys_pledge 0xfff06cffffffffff globl # └─ online linux services ban the president of united states of america scall msyscall 0xfff025ffffffffff globl # The Fifth Bell System Interface, Community Edition diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 9540717f0..fd37a06f4 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -553,7 +553,7 @@ syscon_windows:/* .rodata.str1.1 .Llog: .ascii STRACE_PROLOGUE .ascii "bell system five system call support" - .asciz " %'u magnums loaded on %s%n" + .asciz " %'u magnums loaded on %s\n" .previous #endif /* DEBUGSYS */ 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/libc/testlib/testlib.h b/libc/testlib/testlib.h index a1e266856..f0252c9dc 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -114,11 +114,12 @@ void TearDownOnce(void); #define ASSERT_SYS(ERRNO, WANT, GOT, ...) \ do { \ - errno = 0; \ + int e = errno; \ __TEST_EQ(assert, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, \ GOT, __VA_ARGS__); \ __TEST_EQ(assert, __FILE__, __LINE__, __FUNCTION__, #ERRNO, \ strerror(errno), ERRNO, errno, __VA_ARGS__); \ + errno = e; \ } while (0) #define ASSERT_BETWEEN(BEG, END, GOT) \ diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index fa079f0b2..05fbb3cd9 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -142,6 +142,7 @@ noasan int main(int argc, char *argv[]) { const char *comdbg; __log_level = kLogInfo; GetOpts(argc, argv); + setenv("GDB", "", true); // normalize this process FixIrregularFds(); diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index d1ce3f6fb..b9fdfbfda 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -142,7 +142,7 @@ static void CheckForFileDescriptors(void) { for (i = 0; i < ARRAYLEN(pfds); ++i) { if (pfds[i].revents & POLLNVAL) continue; ++g_testlib_failed; - fprintf(stderr, "error: test failed to close() fd %d%n", pfds[i].fd); + fprintf(stderr, "error: test failed to close() fd %d\n", pfds[i].fd); } } #endif @@ -161,7 +161,7 @@ static void CheckForZombies(void) { break; } else { ++g_testlib_failed; - fprintf(stderr, "error: test failed to reap zombies %d%n", pid); + fprintf(stderr, "error: test failed to reap zombies %d\n", pid); } } #endif diff --git a/libc/time/asctime.c b/libc/time/asctime.c index b17b44e6f..c076e59f1 100644 --- a/libc/time/asctime.c +++ b/libc/time/asctime.c @@ -1,31 +1,114 @@ -/*-*- 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 2020 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/fmt.h" #include "libc/time/time.h" +#include "libc/time/tz.internal.h" +// clang-format off +/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */ -static char g_asctime_buf[64]; +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ -/** - * Converts date time to string. - * - * @return date time string in statically allocated buffer - * @see asctime_r for reentrant version - */ -char *asctime(const struct tm *date) { - return asctime_r(date, g_asctime_buf); +/* +** Avoid the temptation to punt entirely to strftime; +** the output of strftime is supposed to be locale specific +** whereas the output of asctime is supposed to be constant. +*/ + +/* +** Some systems only handle "%.2d"; others only handle "%02d"; +** "%02.2d" makes (most) everybody happy. +** At least some versions of gcc warn about the %02.2d; +** we conditionalize below to avoid the warning. +*/ +/* +** All years associated with 32-bit time_t values are exactly four digits long; +** some years associated with 64-bit time_t values are not. +** Vintage programs are coded for years that are always four digits long +** and may assume that the newline always lands in the same place. +** For years that are less than four digits, we pad the output with +** leading zeroes to get the newline in the traditional place. +** The -4 ensures that we get four characters of output even if +** we call a strftime variant that produces fewer characters for some years. +** The ISO C and POSIX standards prohibit padding the year, +** but many implementations pad anyway; most likely the standards are buggy. +*/ +#ifdef __GNUC__ +#define ASCTIME_FMT "%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n" +#else /* !defined __GNUC__ */ +#define ASCTIME_FMT "%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n" +#endif /* !defined __GNUC__ */ +/* +** For years that are more than four digits we put extra spaces before the year +** so that code trying to overwrite the newline won't end up overwriting +** a digit within a year and truncating the year (operating on the assumption +** that no output is better than wrong output). +*/ +#ifdef __GNUC__ +#define ASCTIME_FMT_B "%s %s%3d %2.2d:%2.2d:%2.2d %s\n" +#else /* !defined __GNUC__ */ +#define ASCTIME_FMT_B "%s %s%3d %02.2d:%02.2d:%02.2d %s\n" +#endif /* !defined __GNUC__ */ + +#define STD_ASCTIME_BUF_SIZE 26 +/* +** Big enough for something such as +** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n +** (two three-character abbreviations, five strings denoting integers, +** seven explicit spaces, two explicit colons, a newline, +** and a trailing NUL byte). +** The values above are for systems where an int is 32 bits and are provided +** as an example; the define below calculates the maximum for the system at +** hand. +*/ +#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1) + +static char buf_asctime[MAX_ASCTIME_BUF_SIZE]; + +char * +asctime_r(register const struct tm *timeptr, char *buf) +{ + register const char * wn; + register const char * mn; + char year[INT_STRLEN_MAXIMUM(int) + 2]; + char result[MAX_ASCTIME_BUF_SIZE]; + + if (timeptr == NULL) { + errno = EINVAL; + return strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); + } + if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) + wn = "???"; + else wn = kWeekdayNameShort[timeptr->tm_wday]; + if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) + mn = "???"; + else mn = kMonthNameShort[timeptr->tm_mon]; + /* + ** Use strftime's %Y to generate the year, to avoid overflow problems + ** when computing timeptr->tm_year + TM_YEAR_BASE. + ** Assume that strftime is unaffected by other out-of-range members + ** (e.g., timeptr->tm_mday) when processing "%Y". + */ + strftime(year, sizeof year, "%Y", timeptr); + /* + ** We avoid using snprintf since it's not available on all systems. + */ + (sprintf)(result, + ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), + wn, mn, + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + year); + if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) + return strcpy(buf, result); + else { + errno = EOVERFLOW; + return NULL; + } +} + +char * +asctime(register const struct tm *timeptr) +{ + return asctime_r(timeptr, buf_asctime); } diff --git a/libc/time/ctime.c b/libc/time/ctime.c index e0e34ebfb..feedb7676 100644 --- a/libc/time/ctime.c +++ b/libc/time/ctime.c @@ -1,21 +1,13 @@ -/*-*- 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 2020 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/calls/weirdtypes.h" #include "libc/time/time.h" -char *ctime(const int64_t *timep) { return asctime(localtime(timep)); } +char *ctime(const time_t *timep) { + /* + ** Section 4.12.3.2 of X3.159-1989 requires that + ** The ctime function converts the calendar time pointed to by timer + ** to local time in the form of a string. It is equivalent to + ** asctime(localtime(timer)) + */ + struct tm *tmp = localtime(timep); + return tmp ? asctime(tmp) : NULL; +} diff --git a/libc/time/ctime_r.c b/libc/time/ctime_r.c index ddfd57b52..c830a081c 100644 --- a/libc/time/ctime_r.c +++ b/libc/time/ctime_r.c @@ -1,25 +1,9 @@ -/*-*- 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 2020 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/calls/weirdtypes.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" -char *ctime_r(const int64_t *timep, char buf[hasatleast 64]) { - struct tm date[1]; - return asctime_r(localtime_r(timep, date), buf); +char *ctime_r(const time_t *timep, char *buf) { + struct tm mytm; + struct tm *tmp = localtime_r(timep, &mytm); + return tmp ? asctime_r(tmp, buf) : NULL; } diff --git a/libc/time/difftime.c b/libc/time/difftime.c index 435401775..00f1daa00 100644 --- a/libc/time/difftime.c +++ b/libc/time/difftime.c @@ -1,21 +1,61 @@ -/*-*- 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 2020 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. │ +/*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/time/time.h" +#include "libc/time/tz.internal.h" +// clang-format off +/* Return the difference between two timestamps. */ -double difftime(int64_t x, int64_t y) { return x - y; } +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ + +/* Return -X as a double. Using this avoids casting to 'double'. */ +static inline double +dminus(double x) +{ + return -x; +} + +double +difftime(time_t time1, time_t time0) +{ + /* + ** If double is large enough, simply convert and subtract + ** (assuming that the larger type has more precision). + */ + if (sizeof(time_t) < sizeof(double)) { + double t1 = time1, t0 = time0; + return t1 - t0; + } + + /* + ** The difference of two unsigned values can't overflow + ** if the minuend is greater than or equal to the subtrahend. + */ + if (!TYPE_SIGNED(time_t)) + return time0 <= time1 ? time1 - time0 : dminus(time0 - time1); + + /* Use uintmax_t if wide enough. */ + if (sizeof(time_t) <= sizeof(uintmax_t)) { + uintmax_t t1 = time1, t0 = time0; + return time0 <= time1 ? t1 - t0 : dminus(t0 - t1); + } + + /* + ** Handle cases where both time1 and time0 have the same sign + ** (meaning that their difference cannot overflow). + */ + if ((time1 < 0) == (time0 < 0)) + return time1 - time0; + + /* + ** The values have opposite signs and uintmax_t is too narrow. + ** This suffers from double rounding; attempt to lessen that + ** by using long double temporaries. + */ + { + long double t1 = time1, t0 = time0; + return t1 - t0; + } +} diff --git a/libc/time/localtime.c b/libc/time/localtime.c index 74bad0461..4fcca25b2 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -1,39 +1,23 @@ /*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ │vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/initializer.internal.h" +#define LOCALTIME_IMPLEMENTATION #include "libc/calls/calls.h" -#include "libc/macros.internal.h" -#include "libc/math.h" -#include "libc/mem/mem.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/runtime/runtime.h" +#include "libc/intrin/spinlock.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" -#include "libc/time/struct/tm.h" #include "libc/time/time.h" +#include "libc/time/tz.internal.h" #include "libc/time/tzfile.internal.h" -#define ALL_STATE - -#define time_t int64_t -#define int_fast64_t int64_t -#define int_fast32_t int32_t -#define GRANDPARENTED "local time zone must be set" -#define AVGSECSPERYEAR 31556952L -#define SECSPERREPEAT \ - ((int_fast64_t)YEARSPERREPEAT * (int_fast64_t)AVGSECSPERYEAR) -#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ -#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ -#define TM_ZONE tm_zone -#define INITIALIZE(x) x = 0 - STATIC_YOINK("zip_uri_support"); STATIC_YOINK("usr/share/zoneinfo/"); +STATIC_YOINK("usr/share/zoneinfo/Anchorage"); STATIC_YOINK("usr/share/zoneinfo/Beijing"); STATIC_YOINK("usr/share/zoneinfo/Berlin"); STATIC_YOINK("usr/share/zoneinfo/Boulder"); STATIC_YOINK("usr/share/zoneinfo/Chicago"); +STATIC_YOINK("usr/share/zoneinfo/GMT"); STATIC_YOINK("usr/share/zoneinfo/GST"); STATIC_YOINK("usr/share/zoneinfo/Honolulu"); STATIC_YOINK("usr/share/zoneinfo/Israel"); @@ -41,26 +25,31 @@ STATIC_YOINK("usr/share/zoneinfo/Japan"); STATIC_YOINK("usr/share/zoneinfo/London"); STATIC_YOINK("usr/share/zoneinfo/Melbourne"); STATIC_YOINK("usr/share/zoneinfo/New_York"); -STATIC_YOINK("usr/share/zoneinfo/Singapore"); STATIC_YOINK("usr/share/zoneinfo/UTC"); -/* clang-format off */ +// clang-format off +/* Convert timestamp from time_t to struct tm. */ /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ -/* #ifndef lint */ -/* #ifndef NOID */ -/* static char elsieid[] = "@(#)localtime.c 8.3"; */ -/* #endif /\* !defined NOID *\/ */ -/* #endif /\* !defined lint *\/ */ - /* ** Leap second handling from Bradley White. ** POSIX-style TZ environment variable handling from Guy Harris. */ +_Alignas(64) static char locallock; + +static int lock(void) { + _spinlock(&locallock); + return 0; +} + +static void unlock(void) { + _spunlock(&locallock); +} + #ifndef TZ_ABBR_MAX_LEN #define TZ_ABBR_MAX_LEN 16 #endif /* !defined TZ_ABBR_MAX_LEN */ @@ -98,36 +87,44 @@ STATIC_YOINK("usr/share/zoneinfo/UTC"); #endif /* !defined WILDABBR */ static const char wildabbr[] = WILDABBR; -static char wildabbr2[sizeof(WILDABBR)]; -static const char gmt[] = "UTC"; +static const char gmt[] = "GMT"; /* ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. -** We default to US rules as of 1999-08-17. -** POSIX 1003.1 section 8.1.1 says that the default DST rules are -** implementation dependent; for historical reasons, US rules are a -** common default. +** Default to US rules as of 2017-05-07. +** POSIX does not specify the default DST rules; +** for historical reasons, US rules are a common default. */ #ifndef TZDEFRULESTRING -#define TZDEFRULESTRING ",M4.1.0,M10.5.0" -#endif /* !defined TZDEFDST */ +#define TZDEFRULESTRING ",M3.2.0,M11.1.0" +#endif struct ttinfo { /* time type information */ - long tt_gmtoff; /* UTC offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is UTC */ + int_fast32_t tt_utoff; /* UT offset in seconds */ + bool tt_isdst; /* used to set tm_isdst */ + int tt_desigidx; /* abbreviation list index */ + bool tt_ttisstd; /* transition is std time */ + bool tt_ttisut; /* transition is UT */ }; struct lsinfo { /* leap second information */ time_t ls_trans; /* transition time */ - long ls_corr; /* correction to apply */ + int_fast32_t ls_corr; /* correction to apply */ }; +#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b)) #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) +/* This abbreviation means local time is unspecified. */ +static char const UNSPEC[] = "-00"; + +/* How many extra bytes are needed at the end of struct state's chars array. + This needs to be at least 1 for null termination in case the input + data isn't properly terminated, and it also needs to be big enough + for ttunspecified to work without crashing. */ +enum { CHARS_EXTRA = BIGGEST(sizeof UNSPEC, 2) - 1 }; + #ifdef TZNAME_MAX #define MY_TZNAME_MAX TZNAME_MAX #endif /* defined TZNAME_MAX */ @@ -140,63 +137,46 @@ struct state { int timecnt; int typecnt; int charcnt; - int goback; - int goahead; + bool goback; + bool goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), + char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + CHARS_EXTRA, + sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; + + /* The time type to use for early times or if no transitions. + It is always zero for recent tzdb releases. + It might be nonzero for data from tzdb 2018e or earlier. */ + int defaulttype; +}; + +enum r_type { + JULIAN_DAY, /* Jn = Julian day */ + DAY_OF_YEAR, /* n = day of year */ + MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */ }; struct rule { - int r_type; /* type of rule--see below */ + enum r_type r_type; /* type of rule */ int r_day; /* day number of rule */ int r_week; /* week number of rule */ int r_mon; /* month number of rule */ - int32_t r_time; /* transition time of rule */ + int_fast32_t r_time; /* transition time of rule */ }; -#define JULIAN_DAY 0 /* Jn - Julian day */ -#define DAY_OF_YEAR 1 /* n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ - -/* -** Prototypes for static functions. -*/ - -static int32_t detzcode(const char *); -static time_t detzcode64(const char *); -static int differ_by_repeat(time_t, time_t); -static const char * getzname(const char *); -static const char * getqzname(const char *, const int); -static const char * getnum(const char *, int *, int, int); -static const char * getsecs(const char *, int32_t *); -static const char * getoffset(const char *, int32_t *); -static const char * getrule(const char *, struct rule *); -static void gmtload(struct state *); -static struct tm * gmtsub(const time_t *, int32_t, struct tm *); -static struct tm * localsub(const time_t *, int32_t, struct tm *); -static int increment_overflow(int *, int); -static int leaps_thru_end_of(int); -static int normalize_overflow(int *, int *, int); -static void settzname(void); -static time_t time1(struct tm *, struct tm * (*)(const time_t *, - int32_t, struct tm *), - int32_t); -static time_t time2(struct tm *, struct tm *(*)(const time_t *, - int32_t, struct tm *), - int32_t, int *); -static time_t time2sub(struct tm *, struct tm *(*)(const time_t *, - int32_t, struct tm*), - int32_t, int *, int); -static struct tm * timesub(const time_t *, int32_t, - const struct state *, struct tm *); -static int tmcomp(const struct tm *, const struct tm *); -static time_t transtime(time_t, int, const struct rule *, int32_t); -static int tzload(const char *, struct state *, int); -static int tzparse(const char *, struct state *, int); +static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, + struct tm *); +static bool increment_overflow(int *, int); +static bool increment_overflow_time(time_t *, int_fast32_t); +static int_fast32_t leapcorr(struct state const *, time_t); +static bool normalize_overflow32(int_fast32_t *, int *, int); +static struct tm *localtime_timesub(time_t const *, int_fast32_t, + struct state const *, struct tm *); +static bool localtime_typesequiv(struct state const *, int, int); +static bool localtime_tzparse(char const *, struct state *, struct state *); #ifdef ALL_STATE static struct state * lclptr; @@ -216,18 +196,6 @@ static struct state gmtmem; static char lcl_TZname[TZ_STRLEN_MAX + 1]; static int lcl_is_set; -static int gmt_is_set; - -char * tzname[2] /* = { */ -/* wildabbr, */ -/* wildabbr */ -/* } */; - -INITIALIZER(400, _init_localtime, { - memcpy(wildabbr2, wildabbr, sizeof(WILDABBR)); - tzname[0] = wildabbr2; - tzname[1] = wildabbr2; -}) /* ** Section 4.12.3 of X3.159-1989 requires that @@ -239,87 +207,139 @@ INITIALIZER(400, _init_localtime, { static struct tm tm; -#ifdef USG_COMPAT -time_t timezone; +#if 2 <= HAVE_TZNAME + TZ_TIME_T +char * tzname[2] = { + (char *) wildabbr, + (char *) wildabbr +}; +#endif +#if 2 <= USG_COMPAT + TZ_TIME_T +long timezone; int daylight; -#endif /* defined USG_COMPAT */ +#endif +#if 2 <= ALTZONE + TZ_TIME_T +long altzone; +#endif -#ifdef ALTZONE -time_t altzone; -#endif /* defined ALTZONE */ +/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */ +static void +init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx) +{ + s->tt_utoff = utoff; + s->tt_isdst = isdst; + s->tt_desigidx = desigidx; + s->tt_ttisstd = false; + s->tt_ttisut = false; +} -static int32_t -detzcode( - const char * const codep -) { - register int32_t result; +/* Return true if SP's time type I does not specify local time. */ +static bool +ttunspecified(struct state const *sp, int i) +{ + char const *abbr = &sp->chars[sp->ttis[i].tt_desigidx]; + /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */ + return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0; +} + +static int_fast32_t +detzcode(const char *const codep) +{ + register int_fast32_t result; register int i; - result = (codep[0] & 0x80) ? ~0L : 0; - for (i = 0; i < 4; ++i) - result = ((unsigned)result << 8) | (codep[i] & 0xff); + int_fast32_t one = 1; + int_fast32_t halfmaxval = one << (32 - 2); + int_fast32_t maxval = halfmaxval - 1 + halfmaxval; + int_fast32_t minval = -1 - maxval; + + result = codep[0] & 0x7f; + for (i = 1; i < 4; ++i) + result = (result << 8) | (codep[i] & 0xff); + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0; + result += minval; + } return result; } -static time_t -detzcode64( - const char * const codep -) { - register time_t result; - register int i; - result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0; - for (i = 0; i < 8; ++i) - result = result * 256 + (codep[i] & 0xff); +static int_fast64_t +detzcode64(const char *const codep) +{ + register int_fast64_t result; + register int i; + int_fast64_t one = 1; + int_fast64_t halfmaxval = one << (64 - 2); + int_fast64_t maxval = halfmaxval - 1 + halfmaxval; + int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval; + + result = codep[0] & 0x7f; + for (i = 1; i < 8; ++i) + result = (result << 8) | (codep[i] & 0xff); + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0; + result += minval; + } return result; } +static void +update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp) +{ +#if HAVE_TZNAME + tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx]; +#endif + if (!ttisp->tt_isdst) + timezone = - ttisp->tt_utoff; +#if ALTZONE + if (ttisp->tt_isdst) + altzone = - ttisp->tt_utoff; +#endif +} + static void settzname(void) { - register struct state * sp; - register int i; - sp = lclptr; - tzname[0] = wildabbr2; - tzname[1] = wildabbr2; -#ifdef USG_COMPAT + register struct state * const sp = lclptr; + register int i; + +#if HAVE_TZNAME + tzname[0] = tzname[1] = (char *) (sp ? wildabbr : gmt); +#endif daylight = 0; timezone = 0; -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE +#if ALTZONE altzone = 0; -#endif /* defined ALTZONE */ -#ifdef ALL_STATE +#endif if (sp == NULL) { - tzname[0] = tzname[1] = gmt; return; } -#endif /* defined ALL_STATE */ + /* + ** And to get the latest time zone abbreviations into tzname. . . + */ for (i = 0; i < sp->typecnt; ++i) { register const struct ttinfo * const ttisp = &sp->ttis[i]; - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; -#ifdef USG_COMPAT - if (ttisp->tt_isdst) - daylight = 1; - if (i == 0 || !ttisp->tt_isdst) - timezone = -(ttisp->tt_gmtoff); -#endif /* defined USG_COMPAT */ -#ifdef ALTZONE - if (i == 0 || ttisp->tt_isdst) - altzone = -(ttisp->tt_gmtoff); -#endif /* defined ALTZONE */ + update_tzname_etc(sp, ttisp); } - /* - ** And to get the latest zone names into tzname. . . - */ for (i = 0; i < sp->timecnt; ++i) { register const struct ttinfo * const ttisp = &sp->ttis[ sp->types[i]]; - tzname[ttisp->tt_isdst] = - &sp->chars[ttisp->tt_abbrind]; + update_tzname_etc(sp, ttisp); + if (ttisp->tt_isdst) + daylight = 1; } +} + +static void +scrub_abbrs(struct state *sp) +{ + int i; /* - ** Finally, scrub the abbreviations. ** First, replace bogus characters. */ for (i = 0; i < sp->charcnt; ++i) @@ -330,330 +350,521 @@ settzname(void) */ for (i = 0; i < sp->typecnt; ++i) { register const struct ttinfo * const ttisp = &sp->ttis[i]; - register char * cp = &sp->chars[ttisp->tt_abbrind]; + char *cp = &sp->chars[ttisp->tt_desigidx]; + if (strlen(cp) > TZ_ABBR_MAX_LEN && strcmp(cp, GRANDPARENTED) != 0) *(cp + TZ_ABBR_MAX_LEN) = '\0'; } } -forceinline int -differ_by_repeat( - const time_t t1, - const time_t t0 -) { - if (TYPE_INTEGRAL(time_t) && - TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) - return 0; - return (t1 - t0) == SECSPERREPEAT; -} +/* Input buffer for data read from a compiled tz file. */ +union input_buffer { + /* The first part of the buffer, interpreted as a header. */ + struct tzhead tzhead; -forceinline int -cmpstr( - const char *l, - const char *r -) { - size_t i = 0; - while (l[i] == r[i] && r[i]) ++i; - return (l[i] & 0xff) - (r[i] & 0xff); -} + /* The entire buffer. */ + char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state) + + 4 * TZ_MAX_TIMES]; +}; +/* TZDIR with a trailing '/' rather than a trailing '\0'. */ +static char const tzdirslash[sizeof TZDIR] = TZDIR "/"; + +/* Local storage needed for 'tzloadbody'. */ +union local_storage { + /* The results of analyzing the file's contents after it is opened. */ + struct file_analysis { + /* The input buffer. */ + union input_buffer u; + + /* A temporary state used for parsing a TZ string in the file. */ + struct state st; + } u; + + /* The file name to be opened. */ + char fullname[BIGGEST(sizeof(struct file_analysis), + sizeof tzdirslash + 1024)]; +}; + +/* Load tz data from the file named NAME into *SP. Read extended + format if DOEXTEND. Use *LSP for temporary storage. Return 0 on + success, an errno value on failure. */ static int -typesequiv( - const struct state *sp, - int a, - int b -) { - int result; +localtime_tzloadbody(char const *name, struct state *sp, bool doextend, + union local_storage *lsp) +{ + register int i; + register int fid; + register int stored; + register ssize_t nread; + register bool doaccess; + register union input_buffer *up = &lsp->u.u; + register int tzheadsize = sizeof(struct tzhead); + + sp->goback = sp->goahead = false; + + if (! name) { + name = TZDEFAULT; + if (! name) + return EINVAL; + } + + if (name[0] == ':') + ++name; +#ifdef SUPPRESS_TZDIR + /* Do not prepend TZDIR. This is intended for specialized + applications only, due to its security implications. */ + doaccess = true; +#else + doaccess = name[0] == '/'; +#endif + if (!doaccess) { + char const *dot; + size_t namelen = strlen(name); + if (sizeof lsp->fullname - sizeof tzdirslash <= namelen) + return ENAMETOOLONG; + + /* Create a string "TZDIR/NAME". Using sprintf here + would pull in stdio (and would fail if the + resulting string length exceeded INT_MAX!). */ + memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash); + strcpy(lsp->fullname + sizeof tzdirslash, name); + + /* Set doaccess if NAME contains a ".." file name + component, as such a name could read a file outside + the TZDIR virtual subtree. */ + for (dot = name; (dot = strchr(dot, '.')); dot++) + if ((dot == name || dot[-1] == '/') && dot[1] == '.' + && (dot[2] == '/' || !dot[2])) { + doaccess = true; + break; + } + + name = lsp->fullname; + } + if (doaccess && access(name, R_OK) != 0) + return errno; + fid = open(name, O_RDONLY); + if (fid < 0) + return errno; + + nread = read(fid, up->buf, sizeof up->buf); + if (nread < tzheadsize) { + int err = nread < 0 ? errno : EINVAL; + close(fid); + return err; + } + if (close(fid) < 0) + return errno; + for (stored = 4; stored <= 8; stored *= 2) { + char version = up->tzhead.tzh_version[0]; + bool skip_datablock = stored == 4 && version; + int_fast32_t datablock_size; + int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt); + int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt); + int_fast64_t prevtr = -1; + int_fast32_t prevcorr = 0; + int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt); + int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt); + int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt); + int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt); + char const *p = up->buf + tzheadsize; + /* Although tzfile(5) currently requires typecnt to be nonzero, + support future formats that may allow zero typecnt + in files that have a TZ string and no transitions. */ + if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS + && 0 <= typecnt && typecnt < TZ_MAX_TYPES + && 0 <= timecnt && timecnt < TZ_MAX_TIMES + && 0 <= charcnt && charcnt < TZ_MAX_CHARS + && 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES + && 0 <= ttisutcnt && ttisutcnt < TZ_MAX_TYPES)) + return EINVAL; + datablock_size + = (timecnt * stored /* ats */ + + timecnt /* types */ + + typecnt * 6 /* ttinfos */ + + charcnt /* chars */ + + leapcnt * (stored + 4) /* lsinfos */ + + ttisstdcnt /* ttisstds */ + + ttisutcnt); /* ttisuts */ + if (nread < tzheadsize + datablock_size) + return EINVAL; + if (skip_datablock) + p += datablock_size; + else { + if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0) + && (ttisutcnt == typecnt || ttisutcnt == 0))) + return EINVAL; + + sp->leapcnt = leapcnt; + sp->timecnt = timecnt; + sp->typecnt = typecnt; + sp->charcnt = charcnt; + + /* Read transitions, discarding those out of time_t range. + But pretend the last transition before TIME_T_MIN + occurred at TIME_T_MIN. */ + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + int_fast64_t at + = stored == 4 ? detzcode(p) : detzcode64(p); + sp->types[i] = at <= TIME_T_MAX; + if (sp->types[i]) { + time_t attime + = ((TYPE_SIGNED(time_t) ? at < TIME_T_MIN : at < 0) + ? TIME_T_MIN : at); + if (timecnt && attime <= sp->ats[timecnt - 1]) { + if (attime < sp->ats[timecnt - 1]) + return EINVAL; + sp->types[i - 1] = 0; + timecnt--; + } + sp->ats[timecnt++] = attime; + } + p += stored; + } + + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + unsigned char typ = *p++; + if (sp->typecnt <= typ) + return EINVAL; + if (sp->types[i]) + sp->types[timecnt++] = typ; + } + sp->timecnt = timecnt; + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + unsigned char isdst, desigidx; + + ttisp = &sp->ttis[i]; + ttisp->tt_utoff = detzcode(p); + p += 4; + isdst = *p++; + if (! (isdst < 2)) + return EINVAL; + ttisp->tt_isdst = isdst; + desigidx = *p++; + if (! (desigidx < sp->charcnt)) + return EINVAL; + ttisp->tt_desigidx = desigidx; + } + for (i = 0; i < sp->charcnt; ++i) + sp->chars[i] = *p++; + /* Ensure '\0'-terminated, and make it safe to call + ttunspecified later. */ + memset(&sp->chars[i], 0, CHARS_EXTRA); + + /* Read leap seconds, discarding those out of time_t range. */ + leapcnt = 0; + for (i = 0; i < sp->leapcnt; ++i) { + int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p); + int_fast32_t corr = detzcode(p + stored); + p += stored + 4; + + /* Leap seconds cannot occur before the Epoch, + or out of order. */ + if (tr <= prevtr) + return EINVAL; + + /* To avoid other botches in this code, each leap second's + correction must differ from the previous one's by 1 + second or less, except that the first correction can be + any value; these requirements are more generous than + RFC 8536, to allow future RFC extensions. */ + if (! (i == 0 + || (prevcorr < corr + ? corr == prevcorr + 1 + : (corr == prevcorr + || corr == prevcorr - 1)))) + return EINVAL; + prevtr = tr; + prevcorr = corr; + + if (tr <= TIME_T_MAX) { + sp->lsis[leapcnt].ls_trans = tr; + sp->lsis[leapcnt].ls_corr = corr; + leapcnt++; + } + } + sp->leapcnt = leapcnt; + + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisstdcnt == 0) + ttisp->tt_ttisstd = false; + else { + if (*p != true && *p != false) + return EINVAL; + ttisp->tt_ttisstd = *p++; + } + } + for (i = 0; i < sp->typecnt; ++i) { + register struct ttinfo * ttisp; + + ttisp = &sp->ttis[i]; + if (ttisutcnt == 0) + ttisp->tt_ttisut = false; + else { + if (*p != true && *p != false) + return EINVAL; + ttisp->tt_ttisut = *p++; + } + } + } + + nread -= p - up->buf; + memmove(up->buf, p, nread); + + /* If this is an old file, we're done. */ + if (!version) + break; + } + if (doextend && nread > 2 && + up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && + sp->typecnt + 2 <= TZ_MAX_TYPES) { + struct state *ts = &lsp->u.st; + + up->buf[nread - 1] = '\0'; + if (localtime_tzparse(&up->buf[1], ts, sp)) { + + /* Attempt to reuse existing abbreviations. + Without this, America/Anchorage would be right on + the edge after 2037 when TZ_MAX_CHARS is 50, as + sp->charcnt equals 40 (for LMT AST AWT APT AHST + AHDT YST AKDT AKST) and ts->charcnt equals 10 + (for AKST AKDT). Reusing means sp->charcnt can + stay 40 in this example. */ + int gotabbr = 0; + int charcnt = sp->charcnt; + for (i = 0; i < ts->typecnt; i++) { + char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx; + int j; + for (j = 0; j < charcnt; j++) + if (strcmp(sp->chars + j, tsabbr) == 0) { + ts->ttis[i].tt_desigidx = j; + gotabbr++; + break; + } + if (! (j < charcnt)) { + int tsabbrlen = strlen(tsabbr); + if (j + tsabbrlen < TZ_MAX_CHARS) { + strcpy(sp->chars + j, tsabbr); + charcnt = j + tsabbrlen + 1; + ts->ttis[i].tt_desigidx = j; + gotabbr++; + } + } + } + if (gotabbr == ts->typecnt) { + sp->charcnt = charcnt; + + /* Ignore any trailing, no-op transitions generated + by zic as they don't help here and can run afoul + of bugs in zic 2016j or earlier. */ + while (1 < sp->timecnt + && (sp->types[sp->timecnt - 1] + == sp->types[sp->timecnt - 2])) + sp->timecnt--; + + for (i = 0; + i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; + i++) { + time_t t = ts->ats[i]; + if (increment_overflow_time(&t, leapcorr(sp, t)) + || (0 < sp->timecnt + && t <= sp->ats[sp->timecnt - 1])) + continue; + sp->ats[sp->timecnt] = t; + sp->types[sp->timecnt] = (sp->typecnt + + ts->types[i]); + sp->timecnt++; + } + for (i = 0; i < ts->typecnt; i++) + sp->ttis[sp->typecnt++] = ts->ttis[i]; + } + } + } + if (sp->typecnt == 0) + return EINVAL; + if (sp->timecnt > 1) { + if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) { + time_t repeatat = sp->ats[0] + SECSPERREPEAT; + int repeattype = sp->types[0]; + for (i = 1; i < sp->timecnt; ++i) + if (sp->ats[i] == repeatat + && localtime_typesequiv(sp, sp->types[i], repeattype)) { + sp->goback = true; + break; + } + } + if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) { + time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT; + int repeattype = sp->types[sp->timecnt - 1]; + for (i = sp->timecnt - 2; i >= 0; --i) + if (sp->ats[i] == repeatat + && localtime_typesequiv(sp, sp->types[i], repeattype)) { + sp->goahead = true; + break; + } + } + } + + /* Infer sp->defaulttype from the data. Although this default + type is always zero for data from recent tzdb releases, + things are trickier for data from tzdb 2018e or earlier. + + The first set of heuristics work around bugs in 32-bit data + generated by tzdb 2013c or earlier. The workaround is for + zones like Australia/Macquarie where timestamps before the + first transition have a time type that is not the earliest + standard-time type. See: + https://mm.icann.org/pipermail/tz/2013-May/019368.html */ + /* + ** If type 0 does not specify local time, or is unused in transitions, + ** it's the type to use for early times. + */ + for (i = 0; i < sp->timecnt; ++i) + if (sp->types[i] == 0) + break; + i = i < sp->timecnt && ! ttunspecified(sp, 0) ? -1 : 0; + /* + ** Absent the above, + ** if there are transition times + ** and the first transition is to a daylight time + ** find the standard type less than and closest to + ** the type of the first transition. + */ + if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) { + i = sp->types[0]; + while (--i >= 0) + if (!sp->ttis[i].tt_isdst) + break; + } + /* The next heuristics are for data generated by tzdb 2018e or + earlier, for zones like EST5EDT where the first transition + is to DST. */ + /* + ** If no result yet, find the first standard type. + ** If there is none, punt to type zero. + */ + if (i < 0) { + i = 0; + while (sp->ttis[i].tt_isdst) + if (++i >= sp->typecnt) { + i = 0; + break; + } + } + /* A simple 'sp->defaulttype = 0;' would suffice here if we + didn't have to worry about 2018e-or-earlier data. Even + simpler would be to remove the defaulttype member and just + use 0 in its place. */ + sp->defaulttype = i; + + return 0; +} + +/* Load tz data from the file named NAME into *SP. Read extended + format if DOEXTEND. Return 0 on success, an errno value on failure. */ +static int +localtime_tzload(char const *name, struct state *sp, bool doextend) +{ +#ifdef ALL_STATE + union local_storage *lsp = malloc(sizeof *lsp); + if (!lsp) { + return HAVE_MALLOC_ERRNO ? errno : ENOMEM; + } else { + int err = localtime_tzloadbody(name, sp, doextend, lsp); + free(lsp); + return err; + } +#else + int i; + volatile char *p; + volatile unsigned x; + union local_storage ls; /* 70+ kilobytes */ + p = (char *)&ls; + for (x = i = 0; i < sizeof(ls); i += 4096) { + x += p[i]; /* make sure tzdata doesn't smash the stack */ + } + return localtime_tzloadbody(name, sp, doextend, &ls); +#endif +} + +static bool +localtime_typesequiv(const struct state *sp, int a, int b) +{ + register bool result; + if (sp == NULL || - a < 0 || a >= sp->typecnt || - b < 0 || b >= sp->typecnt) - result = FALSE; + a < 0 || a >= sp->typecnt || + b < 0 || b >= sp->typecnt) + result = false; else { - const struct ttinfo * ap = &sp->ttis[a]; - const struct ttinfo * bp = &sp->ttis[b]; - result = ap->tt_gmtoff == bp->tt_gmtoff && - ap->tt_isdst == bp->tt_isdst && - ap->tt_ttisstd == bp->tt_ttisstd && - ap->tt_ttisgmt == bp->tt_ttisgmt && - cmpstr(&sp->chars[ap->tt_abbrind], - &sp->chars[bp->tt_abbrind]) == 0; + register const struct ttinfo * ap = &sp->ttis[a]; + register const struct ttinfo * bp = &sp->ttis[b]; + result = (ap->tt_utoff == bp->tt_utoff + && ap->tt_isdst == bp->tt_isdst + && ap->tt_ttisstd == bp->tt_ttisstd + && ap->tt_ttisut == bp->tt_ttisut + && (strcmp(&sp->chars[ap->tt_desigidx], + &sp->chars[bp->tt_desigidx]) + == 0)); } return result; } -static int -tzload( - const char * name, - struct state * const sp, - const int doextend -) { - register const char * p; - register int i; - register int fid; - register int stored; - register int nread; - union { - struct tzhead tzhead; - char buf[2 * sizeof(struct tzhead) + - 2 * sizeof *sp + - 4 * TZ_MAX_TIMES]; - } * up; - char fullname[PATH_MAX]; - - up = calloc(1, sizeof *up); - if (up == NULL) - return -1; - - sp->goback = sp->goahead = FALSE; - if (name != NULL /* && issetugid() != 0 */) { - if ((name[0] == ':' && (strchr(name, '/'))) || - name[0] == '/' || strchr(name, '.')) - name = NULL; - } - if (name == NULL && (name = TZDEFAULT) == NULL) - goto oops; - - if (name[0] == ':') - ++name; - if (name[0] != '/') { - if ((p = TZDIR) == NULL) - goto oops; - if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) - goto oops; - strlcpy(fullname, p, sizeof fullname); - strlcat(fullname, "/", sizeof fullname); - strlcat(fullname, name, sizeof fullname); - name = fullname; - } - if ((fid = open(name, O_RDONLY)) == -1) - goto oops; - - nread = read(fid, up->buf, sizeof up->buf); - if (close(fid) < 0 || nread <= 0) - goto oops; - for (stored = 4; stored <= 8; stored *= 2) { - int ttisstdcnt; - int ttisgmtcnt; - - ttisstdcnt = (int) detzcode(up->tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(up->tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(up->tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(up->tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(up->tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(up->tzhead.tzh_charcnt); - p = up->tzhead.tzh_charcnt + sizeof up->tzhead.tzh_charcnt; - if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || - sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || - sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || - sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || - (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || - (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) - goto oops; - if (nread - (p - up->buf) < - sp->timecnt * stored + /* ats */ - sp->timecnt + /* types */ - sp->typecnt * 6 + /* ttinfos */ - sp->charcnt + /* chars */ - sp->leapcnt * (stored + 4) + /* lsinfos */ - ttisstdcnt + /* ttisstds */ - ttisgmtcnt) /* ttisgmts */ - goto oops; - for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - } - for (i = 0; i < sp->timecnt; ++i) { - sp->types[i] = (unsigned char) *p++; - if (sp->types[i] >= sp->typecnt) - goto oops; - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - ttisp->tt_gmtoff = detzcode(p); - p += 4; - ttisp->tt_isdst = (unsigned char) *p++; - if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - goto oops; - ttisp->tt_abbrind = (unsigned char) *p++; - if (ttisp->tt_abbrind < 0 || - ttisp->tt_abbrind > sp->charcnt) - goto oops; - } - for (i = 0; i < sp->charcnt; ++i) - sp->chars[i] = *p++; - sp->chars[i] = '\0'; /* ensure '\0' at end */ - for (i = 0; i < sp->leapcnt; ++i) { - struct lsinfo * lsisp; - - lsisp = &sp->lsis[i]; - lsisp->ls_trans = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - lsisp->ls_corr = detzcode(p); - p += 4; - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = FALSE; - else { - ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != TRUE && - ttisp->tt_ttisstd != FALSE) - goto oops; - } - } - for (i = 0; i < sp->typecnt; ++i) { - struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = FALSE; - else { - ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != TRUE && - ttisp->tt_ttisgmt != FALSE) - goto oops; - } - } - /* - ** Out-of-sort ats should mean we're running on a - ** signed time_t system but using a data file with - ** unsigned values (or vice versa). - */ - for (i = 0; i < sp->timecnt - 2; ++i) - if (sp->ats[i] > sp->ats[i + 1]) { - ++i; - /* - ** Ignore the end (easy). - */ - sp->timecnt = i; - break; - } - /* - ** If this is an old file, we're done. - */ - if (up->tzhead.tzh_version[0] == '\0') - break; - nread -= p - up->buf; - for (i = 0; i < nread; ++i) - up->buf[i] = p[i]; - /* - ** If this is a narrow integer time_t system, we're done. - */ - if (stored >= sizeof(time_t)) - break; - } - if (doextend && nread > 2 && - up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && - sp->typecnt + 2 <= TZ_MAX_TYPES) { - struct state *ts; - int result; - ts = calloc(1, sizeof(struct state)); - if (!ts) abort(); - up->buf[nread - 1] = '\0'; - result = tzparse(&up->buf[1], ts, FALSE); - if (result == 0 && ts->typecnt == 2 && - sp->charcnt + ts->charcnt <= TZ_MAX_CHARS) { - for (i = 0; i < 2; ++i) - ts->ttis[i].tt_abbrind += - sp->charcnt; - for (i = 0; i < ts->charcnt; ++i) - sp->chars[sp->charcnt++] = - ts->chars[i]; - i = 0; - while (i < ts->timecnt && - ts->ats[i] <= - sp->ats[sp->timecnt - 1]) - ++i; - while (i < ts->timecnt && - sp->timecnt < TZ_MAX_TIMES) { - sp->ats[sp->timecnt] = - ts->ats[i]; - sp->types[sp->timecnt] = - sp->typecnt + - ts->types[i]; - ++sp->timecnt; - ++i; - } - sp->ttis[sp->typecnt++] = ts->ttis[0]; - sp->ttis[sp->typecnt++] = ts->ttis[1]; - } - free(ts); - } - if (sp->timecnt > 1) { - for (i = 1; i < sp->timecnt; ++i) { - if (typesequiv(sp, sp->types[i], sp->types[0]) && - differ_by_repeat(sp->ats[i], sp->ats[0])) { - sp->goback = TRUE; - break; - } - } - for (i = sp->timecnt - 2; i >= 0; --i) { - if (typesequiv(sp, sp->types[sp->timecnt - 1], - sp->types[i]) && - differ_by_repeat(sp->ats[sp->timecnt - 1], - sp->ats[i])) { - sp->goahead = TRUE; - break; - } - } - } - free(up); - return 0; -oops: - free(up); - return -1; -} - -static const unsigned char kMonthLengths[2][MONSPERYEAR] = { +static const int mon_lengths[2][MONSPERYEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; -static const int kYearLengths[2] = { +static const int year_lengths[2] = { DAYSPERNYEAR, DAYSPERLYEAR }; +/* Is C an ASCII digit? */ +static inline bool +is_digit(char c) +{ + return '0' <= c && c <= '9'; +} + /* -** Given a pointer into a time zone string, scan until a character that is not -** a valid character in a zone name is found. Return a pointer to that -** character. +** Given a pointer into a timezone string, scan until a character that is not +** a valid character in a time zone abbreviation is found. +** Return a pointer to that character. */ -static const char * -getzname( - const char * strp -) { - char c; - while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' && - c != '+') { - ++strp; - } +static nosideeffect const char * +getzname(register const char *strp) +{ + register char c; + + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && + c != '+') + ++strp; return strp; } /* -** Given a pointer into an extended time zone string, scan until the ending -** delimiter of the zone name is located. Return a pointer to the delimiter. +** Given a pointer into an extended timezone string, scan until the ending +** delimiter of the time zone abbreviation is located. +** Return a pointer to the delimiter. ** ** As with getzname above, the legal character set is actually quite ** restricted, with other characters producing undefined results. ** We don't do any checking here; checking is done later in common-case code. */ -static const char * -getqzname( - const char * strp, - const int delim -) { - register int c; +static nosideeffect const char * +getqzname(register const char *strp, const int delim) +{ + register int c; while ((c = *strp) != '\0' && c != delim) ++strp; @@ -661,23 +872,19 @@ getqzname( } /* -** Given a pointer into a time zone string, extract a number from that string. +** Given a pointer into a timezone string, extract a number from that string. ** Check that the number is within a specified range; if it is not, return ** NULL. ** Otherwise, return a pointer to the first character not part of the number. */ static const char * -getnum( - const char * strp, - int * const nump, - const int min, - const int max -) { - register char c; - register int num; +getnum(register const char *strp, int *const nump, const int min, const int max) +{ + register char c; + register int num; - if (strp == NULL || !isdigit(c = *strp)) + if (strp == NULL || !is_digit(c = *strp)) return NULL; num = 0; do { @@ -685,7 +892,7 @@ getnum( if (num > max) return NULL; /* illegal value */ c = *++strp; - } while (isdigit(c)); + } while (is_digit(c)); if (num < min) return NULL; /* illegal value */ *nump = num; @@ -693,7 +900,7 @@ getnum( } /* -** Given a pointer into a time zone string, extract a number of seconds, +** Given a pointer into a timezone string, extract a number of seconds, ** in hh[:mm[:ss]] form, from the string. ** If any error occurs, return NULL. ** Otherwise, return a pointer to the first character not part of the number @@ -701,21 +908,21 @@ getnum( */ static const char * -getsecs( - const char * strp, - int32_t * const secsp -) { - int num; +getsecs(register const char *strp, int_fast32_t *const secsp) +{ + int num; + int_fast32_t secsperhour = SECSPERHOUR; + /* - ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like + ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like ** "M10.4.6/26", which does not conform to Posix, ** but which specifies the equivalent of - ** ``02:00 on the first Sunday on or after 23 Oct''. + ** "02:00 on the first Sunday on or after 23 Oct". */ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); if (strp == NULL) return NULL; - *secsp = num * (int32_t) SECSPERHOUR; + *secsp = num * secsperhour; if (*strp == ':') { ++strp; strp = getnum(strp, &num, 0, MINSPERHOUR - 1); @@ -724,7 +931,7 @@ getsecs( *secsp += num * SECSPERMIN; if (*strp == ':') { ++strp; - /* `SECSPERMIN' allows for leap seconds. */ + /* 'SECSPERMIN' allows for leap seconds. */ strp = getnum(strp, &num, 0, SECSPERMIN); if (strp == NULL) return NULL; @@ -735,20 +942,19 @@ getsecs( } /* -** Given a pointer into a time zone string, extract an offset, in +** Given a pointer into a timezone string, extract an offset, in ** [+-]hh[:mm[:ss]] form, from the string. ** If any error occurs, return NULL. ** Otherwise, return a pointer to the first character not part of the time. */ static const char * -getoffset( - const char * strp, - int32_t * const offsetp -) { - register int neg = 0; +getoffset(register const char *strp, int_fast32_t *const offsetp) +{ + register bool neg = false; + if (*strp == '-') { - neg = 1; + neg = true; ++strp; } else if (*strp == '+') ++strp; @@ -761,17 +967,15 @@ getoffset( } /* -** Given a pointer into a time zone string, extract a rule in the form +** Given a pointer into a timezone string, extract a rule in the form ** date[/time]. See POSIX section 8 for the format of "date" and "time". ** If a valid rule is not found, return NULL. ** Otherwise, return a pointer to the first character not part of the rule. */ static const char * -getrule( - const char * strp, - struct rule * const rulep -) { +getrule(const char *strp, register struct rule *const rulep) +{ if (*strp == 'J') { /* ** Julian day. @@ -796,7 +1000,7 @@ getrule( if (*strp++ != '.') return NULL; strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); - } else if (isdigit(*strp)) { + } else if (is_digit(*strp)) { /* ** Day of year. */ @@ -810,30 +1014,25 @@ getrule( ** Time specified. */ ++strp; - strp = getsecs(strp, &rulep->r_time); + strp = getoffset(strp, &rulep->r_time); } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } /* -** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the -** year, a rule, and the offset from UTC at the time that rule takes effect, -** calculate the Epoch-relative time that rule takes effect. +** Given a year, a rule, and the offset from UT at the time that rule takes +** effect, calculate the year-relative time that rule takes effect. */ -static time_t -transtime( - const time_t janfirst, - const int year, - const struct rule * const rulep, - const int32_t offset -) { - register int leapyear; - register time_t value; - register int i; - int d, m1, yy0, yy1, yy2, dow; +static int_fast32_t +transtime(const int year, register const struct rule *const rulep, + const int_fast32_t offset) +{ + register bool leapyear; + register int_fast32_t value; + register int i; + int d, m1, yy0, yy1, yy2, dow; - INITIALIZE(value); leapyear = isleap(year); switch (rulep->r_type) { @@ -845,7 +1044,7 @@ transtime( ** add SECSPERDAY times the day number-1 to the time of ** January 1, midnight, to get the day. */ - value = janfirst + (rulep->r_day - 1) * SECSPERDAY; + value = (rulep->r_day - 1) * SECSPERDAY; if (leapyear && rulep->r_day >= 60) value += SECSPERDAY; break; @@ -856,16 +1055,13 @@ transtime( ** Just add SECSPERDAY times the day number to the time of ** January 1, midnight, to get the day. */ - value = janfirst + rulep->r_day * SECSPERDAY; + value = rulep->r_day * SECSPERDAY; break; case MONTH_NTH_DAY_OF_WEEK: /* ** Mm.n.d - nth "dth day" of month m. */ - value = janfirst; - for (i = 0; i < rulep->r_mon - 1; ++i) - value += kMonthLengths[leapyear][i] * SECSPERDAY; /* ** Use Zeller's Congruence to get day-of-week of first day of @@ -890,7 +1086,7 @@ transtime( d += DAYSPERWEEK; for (i = 1; i < rulep->r_week; ++i) { if (d + DAYSPERWEEK >= - kMonthLengths[leapyear][rulep->r_mon - 1]) + mon_lengths[leapyear][rulep->r_mon - 1]) break; d += DAYSPERWEEK; } @@ -898,15 +1094,19 @@ transtime( /* ** "d" is the day-of-month (zero-origin) of the day we want. */ - value += d * SECSPERDAY; + value = d * SECSPERDAY; + for (i = 0; i < rulep->r_mon - 1; ++i) + value += mon_lengths[leapyear][i] * SECSPERDAY; break; + + default: UNREACHABLE(); } /* - ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in - ** question. To get the Epoch-relative time of the specified local + ** "value" is the year-relative time of 00:00:00 UT on the day in + ** question. To get the year-relative time of the specified local ** time on that day, add the transition time and the current offset - ** from UTC. + ** from UT. */ return value + rulep->r_time + offset; } @@ -916,142 +1116,194 @@ transtime( ** appropriate. */ -static int -tzparse( - const char * name, - struct state * const sp, - const int lastditch -) { - const char * stdname; - const char * dstname; - size_t stdlen; - size_t dstlen; - int32_t stdoffset; - int32_t dstoffset; - register time_t * atp; - register unsigned char *typep; - register char * cp; - register int load_result; +static bool +localtime_tzparse(const char *name, struct state *sp, struct state *basep) +{ + const char * stdname; + const char * dstname; + size_t stdlen; + size_t dstlen; + size_t charcnt; + int_fast32_t stdoffset; + int_fast32_t dstoffset; + register char * cp; + register bool load_ok; + time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN; - INITIALIZE(dstname); stdname = name; - if (lastditch) { - stdlen = strlen(name); /* length of standard zone name */ - name += stdlen; - if (stdlen >= sizeof sp->chars) - stdlen = (sizeof sp->chars) - 1; - stdoffset = 0; + if (*name == '<') { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') + return false; + stdlen = name - stdname; + name++; } else { - if (*name == '<') { - name++; - stdname = name; - name = getqzname(name, '>'); - if (*name != '>') - return (-1); - stdlen = name - stdname; - name++; - } else { - name = getzname(name); - stdlen = name - stdname; - } - if (*name == '\0') - return -1; - name = getoffset(name, &stdoffset); - if (name == NULL) - return -1; + name = getzname(name); + stdlen = name - stdname; } - load_result = tzload(TZDEFRULES, sp, FALSE); - if (load_result != 0) - sp->leapcnt = 0; /* so, we're off a little */ - sp->timecnt = 0; + if (!stdlen) + return false; + name = getoffset(name, &stdoffset); + if (name == NULL) + return false; + charcnt = stdlen + 1; + if (sizeof sp->chars < charcnt) + return false; + if (basep) { + if (0 < basep->timecnt) + atlo = basep->ats[basep->timecnt - 1]; + load_ok = false; + sp->leapcnt = basep->leapcnt; + memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis); + } else { + load_ok = localtime_tzload(TZDEFRULES, sp, false) == 0; + if (!load_ok) + sp->leapcnt = 0; /* So, we're off a little. */ + } + if (0 < sp->leapcnt) + leaplo = sp->lsis[sp->leapcnt - 1].ls_trans; if (*name != '\0') { if (*name == '<') { dstname = ++name; name = getqzname(name, '>'); if (*name != '>') - return -1; + return false; dstlen = name - dstname; name++; } else { dstname = name; name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ + dstlen = name - dstname; /* length of DST abbr. */ } + if (!dstlen) + return false; + charcnt += dstlen + 1; + if (sizeof sp->chars < charcnt) + return false; if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); if (name == NULL) - return -1; + return false; } else dstoffset = stdoffset - SECSPERHOUR; - if (*name == '\0' && load_result != 0) + if (*name == '\0' && !load_ok) name = TZDEFRULESTRING; if (*name == ',' || *name == ';') { struct rule start; struct rule end; register int year; - register time_t janfirst; - time_t starttime; - time_t endtime; + register int timecnt; + time_t janfirst; + int_fast32_t janoffset = 0; + int yearbeg, yearlim; ++name; if ((name = getrule(name, &start)) == NULL) - return -1; + return false; if (*name++ != ',') - return -1; + return false; if ((name = getrule(name, &end)) == NULL) - return -1; + return false; if (*name != '\0') - return -1; + return false; sp->typecnt = 2; /* standard time and DST */ /* ** Two transitions per year, from EPOCH_YEAR forward. */ - sp->ttis[0].tt_gmtoff = -dstoffset; - sp->ttis[0].tt_isdst = 1; - sp->ttis[0].tt_abbrind = stdlen + 1; - sp->ttis[1].tt_gmtoff = -stdoffset; - sp->ttis[1].tt_isdst = 0; - sp->ttis[1].tt_abbrind = 0; - atp = sp->ats; - typep = sp->types; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); + sp->defaulttype = 0; + timecnt = 0; janfirst = 0; - for (year = EPOCH_YEAR; - sp->timecnt + 2 <= TZ_MAX_TIMES; - ++year) { - time_t newfirst; + yearbeg = EPOCH_YEAR; - starttime = transtime(janfirst, year, &start, - stdoffset); - endtime = transtime(janfirst, year, &end, - dstoffset); - if (starttime > endtime) { - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - } else { - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - } - sp->timecnt += 2; - newfirst = janfirst; - newfirst += kYearLengths[isleap(year)] * - SECSPERDAY; - if (newfirst <= janfirst) - break; - janfirst = newfirst; + do { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + yearbeg--; + if (increment_overflow_time(&janfirst, -yearsecs)) { + janoffset = -yearsecs; + break; + } + } while (atlo < janfirst + && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + + while (true) { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg)] * SECSPERDAY; + int yearbeg1 = yearbeg; + time_t janfirst1 = janfirst; + if (increment_overflow_time(&janfirst1, yearsecs) + || increment_overflow(&yearbeg1, 1) + || atlo <= janfirst1) + break; + yearbeg = yearbeg1; + janfirst = janfirst1; } + + yearlim = yearbeg; + if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) + yearlim = INT_MAX; + for (year = yearbeg; year < yearlim; year++) { + int_fast32_t + starttime = transtime(year, &start, stdoffset), + endtime = transtime(year, &end, dstoffset); + int_fast32_t + yearsecs = (year_lengths[isleap(year)] + * SECSPERDAY); + bool reversed = endtime < starttime; + if (reversed) { + int_fast32_t swap = starttime; + starttime = endtime; + endtime = swap; + } + if (reversed + || (starttime < endtime + && endtime - starttime < yearsecs)) { + if (TZ_MAX_TIMES - 2 < timecnt) + break; + sp->ats[timecnt] = janfirst; + if (! increment_overflow_time + (&sp->ats[timecnt], + janoffset + starttime) + && atlo <= sp->ats[timecnt]) + sp->types[timecnt++] = !reversed; + sp->ats[timecnt] = janfirst; + if (! increment_overflow_time + (&sp->ats[timecnt], + janoffset + endtime) + && atlo <= sp->ats[timecnt]) { + sp->types[timecnt++] = reversed; + } + } + if (endtime < leaplo) { + yearlim = year; + if (increment_overflow(&yearlim, + YEARSPERREPEAT + 1)) + yearlim = INT_MAX; + } + if (increment_overflow_time + (&janfirst, janoffset + yearsecs)) + break; + janoffset = 0; + } + sp->timecnt = timecnt; + if (! timecnt) { + sp->ttis[0] = sp->ttis[1]; + sp->typecnt = 1; /* Perpetual DST. */ + } else if (YEARSPERREPEAT < year - yearbeg) + sp->goback = sp->goahead = true; } else { - register int32_t theirstdoffset; - register int32_t theirdstoffset; - register int32_t theiroffset; - register int isdst; + register int_fast32_t theirstdoffset; + register int_fast32_t theirdstoffset; + register int_fast32_t theiroffset; + register bool isdst; register int i; register int j; if (*name != '\0') - return -1; + return false; /* ** Initial values of theirstdoffset and theirdstoffset. */ @@ -1060,7 +1312,7 @@ tzparse( j = sp->types[i]; if (!sp->ttis[j].tt_isdst) { theirstdoffset = - -sp->ttis[j].tt_gmtoff; + - sp->ttis[j].tt_utoff; break; } } @@ -1069,15 +1321,14 @@ tzparse( j = sp->types[i]; if (sp->ttis[j].tt_isdst) { theirdstoffset = - -sp->ttis[j].tt_gmtoff; + - sp->ttis[j].tt_utoff; break; } } /* ** Initially we're assumed to be in standard time. */ - isdst = FALSE; - theiroffset = theirstdoffset; + isdst = false; /* ** Now juggle transition times and types ** tracking offsets as you do. @@ -1085,16 +1336,17 @@ tzparse( for (i = 0; i < sp->timecnt; ++i) { j = sp->types[i]; sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisgmt) { + if (sp->ttis[j].tt_ttisut) { /* No adjustment to transition time */ } else { /* - ** If summer time is in effect, and the - ** transition time was not specified as - ** standard time, add the summer time - ** offset to the transition time; - ** otherwise, add the standard time - ** offset to the transition time. + ** If daylight saving time is in + ** effect, and the transition time was + ** not specified as standard time, add + ** the daylight saving time offset to + ** the transition time; otherwise, add + ** the standard time offset to the + ** transition time. */ /* ** Transitions from DST to DDST @@ -1110,206 +1362,192 @@ tzparse( theirstdoffset; } } - theiroffset = -sp->ttis[j].tt_gmtoff; + theiroffset = -sp->ttis[j].tt_utoff; if (sp->ttis[j].tt_isdst) theirdstoffset = theiroffset; else theirstdoffset = theiroffset; } /* ** Finally, fill in ttis. - ** ttisstd and ttisgmt need not be handled. */ - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = FALSE; - sp->ttis[0].tt_abbrind = 0; - sp->ttis[1].tt_gmtoff = -dstoffset; - sp->ttis[1].tt_isdst = TRUE; - sp->ttis[1].tt_abbrind = stdlen + 1; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); sp->typecnt = 2; + sp->defaulttype = 0; } } else { dstlen = 0; sp->typecnt = 1; /* only standard time */ sp->timecnt = 0; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_abbrind = 0; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + sp->defaulttype = 0; } - sp->charcnt = stdlen + 1; - if (dstlen != 0) - sp->charcnt += dstlen + 1; - if ((size_t) sp->charcnt > sizeof sp->chars) - return -1; + sp->charcnt = charcnt; cp = sp->chars; - (void) strncpy(cp, stdname, stdlen); + memcpy(cp, stdname, stdlen); cp += stdlen; *cp++ = '\0'; if (dstlen != 0) { - (void) strncpy(cp, dstname, dstlen); + memcpy(cp, dstname, dstlen); *(cp + dstlen) = '\0'; } - return 0; + return true; } static void -gmtload( - struct state * const sp -) { - if (tzload(gmt, sp, TRUE) != 0) - (void) tzparse(gmt, sp, TRUE); +gmtload(struct state *const sp) +{ + if (localtime_tzload(gmt, sp, true) != 0) + localtime_tzparse("GMT0", sp, NULL); } -#ifndef STD_INSPIRED -/* -** A non-static declaration of tzsetwall in a system header file -** may cause a warning about this upcoming static declaration... -*/ -static -#endif /* !defined STD_INSPIRED */ -void -tzsetwall(void) +/* Initialize *SP to a value appropriate for the TZ setting NAME. + Return 0 on success, an errno value on failure. */ +static int +zoneinit(struct state *sp, char const *name) { - if (lcl_is_set < 0) - return; - lcl_is_set = -1; -#ifdef ALL_STATE - if (lclptr == NULL) { - if ((lclptr = malloc(sizeof(*lclptr)))) { - __cxa_atexit(free, lclptr, 0); - } else { - settzname(); /* all we can do */ - return; - } + if (name && ! name[0]) { + /* + ** User wants it fast rather than right. + */ + sp->leapcnt = 0; /* so, we're off a little */ + sp->timecnt = 0; + sp->typecnt = 0; + sp->charcnt = 0; + sp->goback = sp->goahead = false; + init_ttinfo(&sp->ttis[0], 0, false, 0); + strcpy(sp->chars, gmt); + sp->defaulttype = 0; + return 0; + } else { + int err = localtime_tzload(name, sp, true); + if (err != 0 && name && name[0] != ':' && + localtime_tzparse(name, sp, NULL)) + err = 0; + if (err == 0) + scrub_abbrs(sp); + return err; } +} + +static void +localtime_tzset_unlocked(void) +{ + char const *name = getenv("TZ"); + struct state *sp = lclptr; + int lcl = name ? strlen(name) < sizeof lcl_TZname : -1; + if (lcl < 0 + ? lcl_is_set < 0 + : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0) + return; +#ifdef ALL_STATE + if (! sp) + lclptr = sp = malloc(sizeof *lclptr); #endif /* defined ALL_STATE */ - if (tzload((char *) NULL, lclptr, TRUE) != 0) - gmtload(lclptr); + if (sp) { + if (zoneinit(sp, name) != 0) + zoneinit(sp, ""); + if (0 < lcl) + strcpy(lcl_TZname, name); + } settzname(); + lcl_is_set = lcl; } void tzset(void) { - register const char * name = NULL; - /* static char buf[PROP_VALUE_MAX]; */ - - name = getenv("TZ"); - - /* // try the "persist.sys.timezone" system property first */ - /* if (name == NULL && __system_property_get("persist.sys.timezone", buf) > 0) */ - /* name = buf; */ - - if (name == NULL) { - tzsetwall(); + if (lock() != 0) return; - } + localtime_tzset_unlocked(); + unlock(); +} - if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) +static void +localtime_gmtcheck(void) +{ + static bool gmt_is_set; + if (lock() != 0) return; - lcl_is_set = strlen(name) < sizeof lcl_TZname; - if (lcl_is_set) - (void) strcpy(lcl_TZname, name); - + if (! gmt_is_set) { #ifdef ALL_STATE - if (lclptr == NULL) { - if ((lclptr = malloc(sizeof(*lclptr)))) { - __cxa_atexit(free, lclptr, 0); - } else { - settzname(); /* all we can do */ - return; - } + gmtptr = malloc(sizeof *gmtptr); +#endif + if (gmtptr) + gmtload(gmtptr); + gmt_is_set = true; } -#endif /* defined ALL_STATE */ - if (*name == '\0') { - /* - ** User wants it fast rather than right. - */ - lclptr->leapcnt = 0; /* so, we're off a little */ - lclptr->timecnt = 0; - lclptr->typecnt = 0; - lclptr->ttis[0].tt_isdst = 0; - lclptr->ttis[0].tt_gmtoff = 0; - lclptr->ttis[0].tt_abbrind = 0; - (void) strcpy(lclptr->chars, gmt); - } else if (tzload(name, lclptr, TRUE) != 0) - if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0) - (void) gmtload(lclptr); - settzname(); + unlock(); } /* ** The easy way to behave "as if no library function calls" localtime -** is to not call it--so we drop its guts into "localsub", which can be -** freely called. (And no, the PANS doesn't require the above behavior-- +** is to not call it, so we drop its guts into "localsub", which can be +** freely called. (And no, the PANS doesn't require the above behavior, ** but it *is* desirable.) ** -** The unused offset argument is for the benefit of mktime variants. +** If successful and SETNAME is nonzero, +** set the applicable parts of tzname, timezone and altzone; +** however, it's OK to omit this step if the timezone is POSIX-compatible, +** since in that case tzset should have already done this step correctly. +** SETNAME's type is int_fast32_t for compatibility with gmtsub, +** but it is actually a boolean and its value should be 0 or 1. */ /*ARGSUSED*/ static struct tm * -localsub( - const time_t * const timep, - const int32_t offset, - struct tm * const tmp -) { - register struct state * sp; +localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, + struct tm *const tmp) +{ register const struct ttinfo * ttisp; register int i; register struct tm * result; const time_t t = *timep; - sp = lclptr; -#ifdef ALL_STATE - if (sp == NULL) - return gmtsub(timep, offset, tmp); -#endif /* defined ALL_STATE */ + if (sp == NULL) { + /* Don't bother to set tzname etc.; tzset has already done it. */ + return gmtsub(gmtptr, timep, 0, tmp); + } if ((sp->goback && t < sp->ats[0]) || (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - time_t newt = t; + time_t newt; register time_t seconds; - register time_t tcycles; - register int_fast64_t icycles; + register time_t years; if (t < sp->ats[0]) seconds = sp->ats[0] - t; else seconds = t - sp->ats[sp->timecnt - 1]; --seconds; - tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; - ++tcycles; - icycles = tcycles; - if (tcycles - icycles >= 1 || icycles - tcycles >= 1) - return NULL; - seconds = icycles; - seconds *= YEARSPERREPEAT; - seconds *= AVGSECSPERYEAR; + + /* Beware integer overflow, as SECONDS might + be close to the maximum time_t. */ + years = seconds / SECSPERREPEAT * YEARSPERREPEAT; + seconds = years * AVGSECSPERYEAR; + years += YEARSPERREPEAT; if (t < sp->ats[0]) - newt += seconds; - else newt -= seconds; + newt = t + seconds + SECSPERREPEAT; + else + newt = t - seconds - SECSPERREPEAT; + if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) return NULL; /* "cannot happen" */ - result = localsub(&newt, offset, tmp); - if (result == tmp) { - register time_t newy; + result = localsub(sp, &newt, setname, tmp); + if (result) { + register int_fast64_t newy; - newy = tmp->tm_year; + newy = result->tm_year; if (t < sp->ats[0]) - newy -= icycles * YEARSPERREPEAT; - else newy += icycles * YEARSPERREPEAT; - tmp->tm_year = newy; - if (tmp->tm_year != newy) + newy -= years; + else newy += years; + if (! (INT_MIN <= newy && newy <= INT_MAX)) return NULL; + result->tm_year = newy; } return result; } if (sp->timecnt == 0 || t < sp->ats[0]) { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) { - i = 0; - break; - } + i = sp->defaulttype; } else { register int lo = 1; register int hi = sp->timecnt; @@ -1321,43 +1559,50 @@ localsub( hi = mid; else lo = mid + 1; } - i = (int) sp->types[lo - 1]; + i = sp->types[lo - 1]; } ttisp = &sp->ttis[i]; /* ** To get (wrong) behavior that's compatible with System V Release 2.0 ** you'd replace the statement below with - ** t += ttisp->tt_gmtoff; + ** t += ttisp->tt_utoff; ** timesub(&t, 0L, sp, tmp); */ - result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); - tmp->tm_isdst = ttisp->tt_isdst; - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; -#ifdef TM_ZONE - tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; -#endif /* defined TM_ZONE */ + result = localtime_timesub(&t, ttisp->tt_utoff, sp, tmp); + if (result) { + result->tm_isdst = ttisp->tt_isdst; + result->tm_zone = (char *) &sp->chars[ttisp->tt_desigidx]; + if (setname) + update_tzname_etc(sp, ttisp); + } return result; } -struct tm * -localtime( - const time_t * const timep -) { - tzset(); - return localsub(timep, 0L, &tm); +static struct tm * +localtime_tzset(time_t const *timep, struct tm *tmp, bool setname) +{ + int err = lock(); + if (err) { + errno = err; + return NULL; + } + if (setname || !lcl_is_set) + localtime_tzset_unlocked(); + tmp = localsub(lclptr, timep, setname, tmp); + unlock(); + return tmp; } -/* -** Re-entrant version of localtime. -*/ +struct tm * +localtime(const time_t *timep) +{ + return localtime_tzset(timep, &tm, true); +} struct tm * -localtime_r( - const time_t * const timep, - struct tm * tmp -) { - tzset(); - return localsub(timep, 0L, tmp); +localtime_r(const time_t *timep, struct tm *tmp) +{ + return localtime_tzset(timep, tmp, false); } /* @@ -1365,249 +1610,164 @@ localtime_r( */ static struct tm * -gmtsub( - const time_t * const timep, - const int32_t offset, - struct tm * const tmp -) { +gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset, + struct tm *tmp) +{ register struct tm * result; - if (!gmt_is_set) { - gmt_is_set = TRUE; -#ifdef ALL_STATE - if (!gmtptr) { - gmtptr = malloc(sizeof(*gmtptr)); - __cxa_atexit(free, gmtptr, 0); - } - if (gmtptr) -#endif /* defined ALL_STATE */ - gmtload(gmtptr); - } - result = timesub(timep, offset, gmtptr, tmp); -#ifdef TM_ZONE + + result = localtime_timesub(timep, offset, gmtptr, tmp); /* ** Could get fancy here and deliver something such as - ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, + ** "+xx" or "-xx" if offset is non-zero, ** but this is no time for a treasure hunt. */ - if (offset != 0) - tmp->TM_ZONE = wildabbr; - else { -#ifdef ALL_STATE - if (gmtptr == NULL) - tmp->TM_ZONE = gmt; - else tmp->TM_ZONE = gmtptr->chars; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - tmp->TM_ZONE = gmtptr->chars; -#endif /* State Farm */ - } -#endif /* defined TM_ZONE */ + tmp->tm_zone = ((char *) + (offset ? wildabbr : gmtptr ? gmtptr->chars : gmt)); return result; } -struct tm * -gmtime( - const time_t * const timep -) { - return gmtsub(timep, 0L, &tm); -} - /* * Re-entrant version of gmtime. */ struct tm * -gmtime_r( - const time_t * const timep, - struct tm * tmp -) { - return gmtsub(timep, 0L, tmp); +gmtime_r(const time_t *timep, struct tm *tmp) +{ + localtime_gmtcheck(); + return gmtsub(gmtptr, timep, 0, tmp); } -#ifdef STD_INSPIRED - struct tm * -offtime( - const time_t * const timep, - const int32_t offset -) { - return gmtsub(timep, offset, &tm); +gmtime(const time_t *timep) +{ + return gmtime_r(timep, &tm); } -#endif /* defined STD_INSPIRED */ - /* ** Return the number of leap years through the end of the given year ** where, to make the math easy, the answer for year zero is defined as zero. */ -pureconst optimizespeed static int -leaps_thru_end_of( - const int y -) { - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); +static time_t +leaps_thru_end_of_nonneg(time_t y) +{ + return y / 4 - y / 100 + y / 400; +} + +static time_t +leaps_thru_end_of(time_t y) +{ + return (y < 0 + ? -1 - leaps_thru_end_of_nonneg(-1 - y) + : leaps_thru_end_of_nonneg(y)); } static struct tm * -timesub( - const time_t * const timep, - const int32_t offset, - const struct state * const sp, - struct tm * const tmp -) { - const struct lsinfo * lp; - time_t tdays; - int idays; /* unsigned would be so 2003 */ - long rem; /* ^wut */ - int y; - int leap; - long corr; - int hit; - int i; +localtime_timesub(const time_t *timep, int_fast32_t offset, + const struct state *sp, struct tm *tmp) +{ + register const struct lsinfo * lp; + register time_t tdays; + register const int * ip; + register int_fast32_t corr; + register int i; + int_fast32_t idays, rem, dayoff, dayrem; + time_t y; + + /* If less than SECSPERMIN, the number of seconds since the + most recent positive leap second; otherwise, do not add 1 + to localtime tm_sec because of leap seconds. */ + time_t secs_since_posleap = SECSPERMIN; corr = 0; - hit = 0; -#ifdef ALL_STATE i = (sp == NULL) ? 0 : sp->leapcnt; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - i = sp->leapcnt; -#endif /* State Farm */ while (--i >= 0) { lp = &sp->lsis[i]; if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } corr = lp->ls_corr; + if ((i == 0 ? 0 : lp[-1].ls_corr) < corr) + secs_since_posleap = *timep - lp->ls_trans; break; } } - y = EPOCH_YEAR; - tdays = *timep / SECSPERDAY; - rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= kYearLengths[isleap(y)]) { - int newy; - register time_t tdelta; - register int idelta; - register int leapdays; - tdelta = tdays / DAYSPERLYEAR; - idelta = tdelta; - if (tdelta - idelta >= 1 || idelta - tdelta >= 1) - return NULL; - if (idelta == 0) - idelta = (tdays < 0) ? -1 : 1; - newy = y; - if (increment_overflow(&newy, idelta)) - return NULL; + /* Calculate the year, avoiding integer overflow even if + time_t is unsigned. */ + tdays = *timep / SECSPERDAY; + rem = *timep % SECSPERDAY; + rem += offset % SECSPERDAY - corr % SECSPERDAY + 3 * SECSPERDAY; + dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3; + rem %= SECSPERDAY; + /* y = (EPOCH_YEAR + + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT), + sans overflow. But calculate against 1570 (EPOCH_YEAR - + YEARSPERREPEAT) instead of against 1970 so that things work + for localtime values before 1970 when time_t is unsigned. */ + dayrem = tdays % DAYSPERREPEAT; + dayrem += dayoff % DAYSPERREPEAT; + y = (EPOCH_YEAR - YEARSPERREPEAT + + ((1 + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT + - ((dayrem % DAYSPERREPEAT) < 0) + + tdays / DAYSPERREPEAT) + * YEARSPERREPEAT)); + /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */ + idays = tdays % DAYSPERREPEAT; + idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT; + idays %= DAYSPERREPEAT; + /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */ + while (year_lengths[isleap(y)] <= idays) { + int tdelta = idays / DAYSPERLYEAR; + int_fast32_t ydelta = tdelta + !tdelta; + time_t newy = y + ydelta; + register int leapdays; leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); - tdays -= ((time_t) newy - y) * DAYSPERNYEAR; - tdays -= leapdays; + idays -= ydelta * DAYSPERNYEAR; + idays -= leapdays; y = newy; } - { - register long seconds; - seconds = tdays * SECSPERDAY + 0.5; - tdays = seconds / SECSPERDAY; - rem += seconds - tdays * SECSPERDAY; + if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) { + int signed_y = y; + tmp->tm_year = signed_y - TM_YEAR_BASE; + } else if ((!TYPE_SIGNED(time_t) || INT_MIN + TM_YEAR_BASE <= y) + && y - TM_YEAR_BASE <= INT_MAX) + tmp->tm_year = y - TM_YEAR_BASE; + else { + errno = EOVERFLOW; + return NULL; } - /* - ** Given the range, we can now fearlessly cast... - */ - idays = tdays; - rem += offset - corr; - while (rem < 0) { - rem += SECSPERDAY; - --idays; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++idays; - } - while (idays < 0) { - if (increment_overflow(&y, -1)) - return NULL; - idays += kYearLengths[isleap(y)]; - } - while (idays >= kYearLengths[isleap(y)]) { - idays -= kYearLengths[isleap(y)]; - if (increment_overflow(&y, 1)) - return NULL; - } - tmp->tm_year = y; - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. */ - tmp->tm_wday = EPOCH_WDAY + - ((y - EPOCH_YEAR) % DAYSPERWEEK) * - (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of(y - 1) - - leaps_thru_end_of(EPOCH_YEAR - 1) + - idays; + tmp->tm_wday = (TM_WDAY_BASE + + ((tmp->tm_year % DAYSPERWEEK) + * (DAYSPERNYEAR % DAYSPERWEEK)) + + leaps_thru_end_of(y - 1) + - leaps_thru_end_of(TM_YEAR_BASE - 1) + + idays); tmp->tm_wday %= DAYSPERWEEK; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int) (rem / SECSPERHOUR); + tmp->tm_hour = rem / SECSPERHOUR; rem %= SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. - */ - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - leap = isleap(y); - for (tmp->tm_mon = 0; - idays >= kMonthLengths[leap][tmp->tm_mon]; - ++(tmp->tm_mon)) { - idays -= kMonthLengths[leap][tmp->tm_mon]; - } - tmp->tm_mday = (int) (idays + 1); + tmp->tm_min = rem / SECSPERMIN; + tmp->tm_sec = rem % SECSPERMIN; + + /* Use "... ??:??:60" at the end of the localtime minute containing + the second just before the positive leap second. */ + tmp->tm_sec += secs_since_posleap <= tmp->tm_sec; + + ip = mon_lengths[isleap(y)]; + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) + idays -= ip[tmp->tm_mon]; + tmp->tm_mday = idays + 1; tmp->tm_isdst = 0; -#ifdef TM_GMTOFF - tmp->TM_GMTOFF = offset; -#endif /* defined TM_GMTOFF */ + tmp->tm_gmtoff = offset; return tmp; } -/* char * */ -/* ctime(timep) */ -/* const time_t * const timep; */ -/* { */ -/* /\* */ -/* ** Section 4.12.3.2 of X3.159-1989 requires that */ -/* ** The ctime function converts the calendar time pointed to by timer */ -/* ** to local time in the form of a string. It is equivalent to */ -/* ** asctime(localtime(timer)) */ -/* *\/ */ -/* return asctime(localtime(timep)); */ -/* } */ - -/* char * */ -/* ctime_r(timep, buf) */ -/* const time_t * const timep; */ -/* char * buf; */ -/* { */ -/* struct tm mytm; */ -/* return asctime_r(localtime_r(timep, &mytm), buf); */ -/* } */ - /* ** Adapted from code provided by Robert Elz, who writes: ** The "best" way to do mktime I think is based on an idea of Bob @@ -1622,31 +1782,77 @@ timesub( #endif /* !defined WRONG */ /* -** Simplified normalize logic courtesy Paul Eggert. +** Normalize logic courtesy Paul Eggert. */ -static inline int -increment_overflow( - int * number, - int delta -) { -#ifdef __GNUC__ - return __builtin_add_overflow(*number, delta, number); +static inline bool +increment_overflow(int *ip, int j) +{ +#if defined(__GNUC__) && __GNUC__ >= 6 + int i = *ip; + if (__builtin_add_overflow(i, j, &i)) return true; + *ip = i; + return false; #else - int number0; - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); + register int const i = *ip; + /* + ** If i >= 0 there can only be overflow if i + j > INT_MAX + ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + ** If i < 0 there can only be overflow if i + j < INT_MIN + ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) + return true; + *ip += j; + return false; #endif } -static int -normalize_overflow( - int * const tensptr, - int * const unitsptr, - const int base -) { +static inline bool +increment_overflow32(int_fast32_t *const lp, int const m) +{ +#if defined(__GNUC__) && __GNUC__ >= 6 + int_fast32_t i = *lp; + if (__builtin_add_overflow(i, m, &i)) return true; + *lp = i; + return false; +#else + register int_fast32_t const l = *lp; + if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) + return true; + *lp += m; + return false; +#endif +} + +static inline bool +increment_overflow_time(time_t *tp, int_fast32_t j) +{ +#if defined(__GNUC__) && __GNUC__ >= 6 + time_t i = *tp; + if (__builtin_add_overflow(i, j, &i)) return true; + *tp = i; + return false; +#else + /* + ** This is like + ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', + ** except that it does the right thing even if *tp + j would overflow. + */ + if (! (j < 0 + ? (TYPE_SIGNED(time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp) + : *tp <= TIME_T_MAX - j)) + return true; + *tp += j; + return false; +#endif +} + +static bool +normalize_overflow(int *const tensptr, int *const unitsptr, const int base) +{ register int tensdelta; + tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); @@ -1654,14 +1860,27 @@ normalize_overflow( return increment_overflow(tensptr, tensdelta); } +static bool +normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base) +{ + register int tensdelta; + + tensdelta = (*unitsptr >= 0) ? + (*unitsptr / base) : + (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow32(tensptr, tensdelta); +} + static int -tmcomp( - const struct tm * const atmp, - const struct tm * const btmp -) { +tmcomp(register const struct tm *const atmp, + register const struct tm *const btmp) +{ register int result; - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && + + if (atmp->tm_year != btmp->tm_year) + return atmp->tm_year < btmp->tm_year ? -1 : 1; + if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && (result = (atmp->tm_min - btmp->tm_min)) == 0) @@ -1670,25 +1889,27 @@ tmcomp( } static time_t -time2sub( - struct tm * const tmp, - struct tm * (* const funcp)(const time_t*, int32_t, struct tm*), - const int32_t offset, - int * const okayp, - const int do_norm_secs -) { - register const struct state * sp; +localtime_time2sub( + struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp, + bool do_norm_secs) +{ register int dir; register int i, j; register int saved_seconds; - register long li; + register int_fast32_t li; register time_t lo; register time_t hi; - int32_t y; + int_fast32_t y; time_t newt; time_t t; struct tm yourtm, mytm; - *okayp = FALSE; + + *okayp = false; yourtm = *tmp; if (do_norm_secs) { if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, @@ -1700,42 +1921,42 @@ time2sub( if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) return WRONG; y = yourtm.tm_year; - if (normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) + if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) return WRONG; /* ** Turn y into an actual year number for now. ** It is converted back to an offset from TM_YEAR_BASE later. */ - if (increment_overflow(&y, TM_YEAR_BASE)) + if (increment_overflow32(&y, TM_YEAR_BASE)) return WRONG; while (yourtm.tm_mday <= 0) { - if (increment_overflow(&y, -1)) + if (increment_overflow32(&y, -1)) return WRONG; li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += kYearLengths[isleap(li)]; + yourtm.tm_mday += year_lengths[isleap(li)]; } while (yourtm.tm_mday > DAYSPERLYEAR) { li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= kYearLengths[isleap(li)]; - if (increment_overflow(&y, 1)) + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (increment_overflow32(&y, 1)) return WRONG; } for ( ; ; ) { - i = kMonthLengths[isleap(y)][yourtm.tm_mon]; + i = mon_lengths[isleap(y)][yourtm.tm_mon]; if (yourtm.tm_mday <= i) break; yourtm.tm_mday -= i; if (++yourtm.tm_mon >= MONSPERYEAR) { yourtm.tm_mon = 0; - if (increment_overflow(&y, 1)) + if (increment_overflow32(&y, 1)) return WRONG; } } - if (increment_overflow(&y, -TM_YEAR_BASE)) + if (increment_overflow32(&y, -TM_YEAR_BASE)) + return WRONG; + if (! (INT_MIN <= y && y <= INT_MAX)) return WRONG; yourtm.tm_year = y; - if (yourtm.tm_year != y) - return WRONG; if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) saved_seconds = 0; else if (y + TM_YEAR_BASE < EPOCH_YEAR) { @@ -1758,27 +1979,15 @@ time2sub( /* ** Do a binary search (this works whatever time_t's type is). */ - if (!TYPE_SIGNED(time_t)) { - lo = 0; - hi = lo - 1; - } else if (!TYPE_INTEGRAL(time_t)) { - if (sizeof(time_t) > sizeof(float)) - hi = (time_t) DBL_MAX; - else hi = (time_t) FLT_MAX; - lo = -hi; - } else { - lo = 1; - for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) - lo *= 2; - hi = -(lo + 1); - } + lo = TIME_T_MIN; + hi = TIME_T_MAX; for ( ; ; ) { t = lo / 2 + hi / 2; if (t < lo) t = lo; else if (t > hi) t = hi; - if ((*funcp)(&t, offset, &mytm) == NULL) { + if (! funcp(sp, &t, offset, &mytm)) { /* ** Assume that t is too extreme to be represented in ** a struct tm; arrange things so that it is less @@ -1788,14 +1997,14 @@ time2sub( } else dir = tmcomp(&mytm, &yourtm); if (dir != 0) { if (t == lo) { - ++t; - if (t <= lo) + if (t == TIME_T_MAX) return WRONG; + ++t; ++lo; } else if (t == hi) { - --t; - if (t >= hi) + if (t == TIME_T_MIN) return WRONG; + --t; --hi; } if (lo > hi) @@ -1805,6 +2014,35 @@ time2sub( else lo = t; continue; } +#if defined TM_GMTOFF && ! UNINIT_TRAP + if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF + && (yourtm.TM_GMTOFF < 0 + ? (-SECSPERDAY <= yourtm.TM_GMTOFF + && (mytm.TM_GMTOFF <= + (SMALLEST(INT_FAST32_MAX, LONG_MAX) + + yourtm.TM_GMTOFF))) + : (yourtm.TM_GMTOFF <= SECSPERDAY + && ((BIGGEST(INT_FAST32_MIN, LONG_MIN) + + yourtm.TM_GMTOFF) + <= mytm.TM_GMTOFF)))) { + /* MYTM matches YOURTM except with the wrong UT offset. + YOURTM.TM_GMTOFF is plausible, so try it instead. + It's OK if YOURTM.TM_GMTOFF contains uninitialized data, + since the guess gets checked. */ + time_t altt = t; + int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF; + if (!increment_overflow_time(&altt, diff)) { + struct tm alttm; + if (funcp(sp, &altt, offset, &alttm) + && alttm.tm_isdst == mytm.tm_isdst + && alttm.TM_GMTOFF == yourtm.TM_GMTOFF + && tmcomp(&alttm, &yourtm) == 0) { + t = altt; + mytm = alttm; + } + } + } +#endif if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) break; /* @@ -1813,25 +2051,19 @@ time2sub( ** It's okay to guess wrong since the guess ** gets checked. */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) - (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); -#ifdef ALL_STATE if (sp == NULL) return WRONG; -#endif /* defined ALL_STATE */ for (i = sp->typecnt - 1; i >= 0; --i) { if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; for (j = sp->typecnt - 1; j >= 0; --j) { if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - if ((*funcp)(&newt, offset, &mytm) == NULL) + if (ttunspecified(sp, j)) + continue; + newt = (t + sp->ttis[j].tt_utoff + - sp->ttis[i].tt_utoff); + if (! funcp(sp, &newt, offset, &mytm)) continue; if (tmcomp(&mytm, &yourtm) != 0) continue; @@ -1851,57 +2083,64 @@ label: if ((newt < t) != (saved_seconds < 0)) return WRONG; t = newt; - if ((*funcp)(&t, offset, tmp)) - *okayp = TRUE; + if (funcp(sp, &t, offset, tmp)) + *okayp = true; return t; } static time_t -time2( - struct tm * const tmp, - struct tm * (* const funcp)(const time_t*, int32_t, struct tm*), - const int32_t offset, - int * const okayp -) { - time_t t; +localtime_time2( + struct tm * const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset, + bool *okayp) +{ + time_t t; + /* ** First try without normalization of seconds ** (in case tm_sec contains a value associated with a leap second). ** If that fails, try with normalization of seconds. */ - t = time2sub(tmp, funcp, offset, okayp, FALSE); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); + t = localtime_time2sub(tmp, funcp, sp, offset, okayp, false); + return *okayp ? t : localtime_time2sub(tmp,funcp,sp,offset,okayp,true); } static time_t -time1( - struct tm * const tmp, - struct tm * (* const funcp)(const time_t *, int32_t, struct tm *), - const int32_t offset -) { +localtime_time1( + struct tm *const tmp, + struct tm *(*funcp)(struct state const *, time_t const *, + int_fast32_t, struct tm *), + struct state const *sp, + const int_fast32_t offset) +{ register time_t t; - register const struct state * sp; register int samei, otheri; register int sameind, otherind; register int i; register int nseen; - int seen[TZ_MAX_TYPES]; - int types[TZ_MAX_TYPES]; - int okay; + char seen[TZ_MAX_TYPES]; + unsigned char types[TZ_MAX_TYPES]; + bool okay; + + if (tmp == NULL) { + errno = EINVAL; + return WRONG; + } if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); -#ifdef PCTS - /* - ** PCTS code courtesy Grant Sullivan. - */ + t = localtime_time2(tmp, funcp, sp, offset, &okay); if (okay) return t; if (tmp->tm_isdst < 0) +#ifdef PCTS + /* + ** POSIX Conformance Test Suite code courtesy Grant Sullivan. + */ tmp->tm_isdst = 0; /* reset to std and try again */ -#endif /* defined PCTS */ -#ifndef PCTS - if (okay || tmp->tm_isdst < 0) +#else return t; #endif /* !defined PCTS */ /* @@ -1910,21 +2149,14 @@ time1( ** We try to divine the type they started from and adjust to the ** type they need. */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); -#ifdef ALL_STATE if (sp == NULL) return WRONG; -#endif /* defined ALL_STATE */ for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; + seen[i] = false; nseen = 0; for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) { - seen[sp->types[i]] = TRUE; + if (!seen[sp->types[i]] && !ttunspecified(sp, sp->types[i])) { + seen[sp->types[i]] = true; types[nseen++] = sp->types[i]; } for (sameind = 0; sameind < nseen; ++sameind) { @@ -1935,117 +2167,57 @@ time1( otheri = types[otherind]; if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + tmp->tm_sec += (sp->ttis[otheri].tt_utoff + - sp->ttis[samei].tt_utoff); tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); + t = localtime_time2(tmp, funcp, sp, offset, &okay); if (okay) return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; + tmp->tm_sec -= (sp->ttis[otheri].tt_utoff + - sp->ttis[samei].tt_utoff); tmp->tm_isdst = !tmp->tm_isdst; } } return WRONG; } -time_t -mktime( - struct tm * const tmp -) { - tzset(); - return time1(tmp, localsub, 0L); +static time_t +mktime_tzname(struct state *sp, struct tm *tmp, bool setname) +{ + if (sp) + return localtime_time1(tmp, localsub, sp, setname); + else { + localtime_gmtcheck(); + return localtime_time1(tmp, gmtsub, gmtptr, 0); + } } time_t -timelocal( - struct tm * const tmp -) { - tmp->tm_isdst = -1; /* in case it wasn't initialized */ - return mktime(tmp); +mktime(struct tm *tmp) +{ + time_t t; + int err = lock(); + if (err) { + errno = err; + return -1; + } + localtime_tzset_unlocked(); + t = mktime_tzname(lclptr, tmp, true); + unlock(); + return t; } -time_t -timegm( - struct tm * const tmp -) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); -} - -time_t -timeoff( - struct tm * const tmp, - const long offset -) { - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, offset); -} - -/* -** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 -** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which -** is not the case if we are accounting for leap seconds. -** So, we provide the following conversion routines for use -** when exchanging timestamps with POSIX conforming systems. -*/ - -static long -leapcorr( - time_t * timep -) { - register struct state * sp; - register struct lsinfo * lp; +static int_fast32_t +leapcorr(struct state const *sp, time_t t) +{ + register struct lsinfo const * lp; register int i; - sp = lclptr; i = sp->leapcnt; while (--i >= 0) { lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) + if (t >= lp->ls_trans) return lp->ls_corr; } return 0; } - -pureconst time_t -time2posix( - time_t t -) { - tzset(); - return t - leapcorr(&t); -} - -pureconst time_t -posix2time( - time_t t -) { - time_t x; - time_t y; - - tzset(); - /* - ** For a positive leap second hit, the result - ** is not unique. For a negative leap second - ** hit, the corresponding time doesn't exist, - ** so we return an adjacent second. - */ - x = t + leapcorr(&t); - y = x - leapcorr(&x); - if (y < t) { - do { - x++; - y = x - leapcorr(&x); - } while (y < t); - if (t != y) - return x - 1; - } else if (y > t) { - do { - --x; - y = x - leapcorr(&x); - } while (y > t); - if (t != y) - return x + 1; - } - return x; -} diff --git a/libc/time/strftime.c b/libc/time/strftime.c index f49462291..8ab0ae2d1 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -1,5 +1,5 @@ -/*-*- 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│ +/*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright (c) 1989 The Regents of the University of California. │ │ All rights reserved. │ @@ -16,354 +16,545 @@ │ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/fmt/itoa.h" -#include "libc/macros.internal.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/time/struct/tm.h" +#include "libc/inttypes.h" +#include "libc/stdio/stdio.h" #include "libc/time/time.h" -#include "libc/time/tzfile.internal.h" +#include "libc/time/tz.internal.h" +#include "libc/unicode/locale.h" +// clang-format off +// converts broken-down timestamp to string + +#define DIVISOR 100 asm(".ident\t\"\\n\\n\ strftime (BSD-3)\\n\ Copyright 1989 The Regents of the University of California\""); asm(".include \"libc/disclaimer.inc\""); -static char *strftime_add(char *p, const char *pe, const char *str) { - while (p < pe && (*p = *str++)) ++p; - return p; +/* +** Based on the UCB version with the copyright notice appearing above. +** +** This is ANSIish only when "multibyte character == plain character". +*/ + +struct lc_time_T { + const char * mon[MONSPERYEAR]; + const char * month[MONSPERYEAR]; + const char * wday[DAYSPERWEEK]; + const char * weekday[DAYSPERWEEK]; + const char * X_fmt; + const char * x_fmt; + const char * c_fmt; + const char * am; + const char * pm; + const char * date_fmt; +}; + +#define Locale (&C_time_locale) + +static const struct lc_time_T C_time_locale = { + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }, { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }, { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" + }, { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }, + + /* X_fmt */ + "%H:%M:%S", + + /* + ** x_fmt + ** C99 and later require this format. + ** Using just numbers (as here) makes Quakers happier; + ** it's also compatible with SVR4. + */ + "%m/%d/%y", + + /* + ** c_fmt + ** C99 and later require this format. + ** Previously this code used "%D %X", but we now conform to C99. + ** Note that + ** "%a %b %d %H:%M:%S %Y" + ** is used by Solaris 2.3. + */ + "%a %b %e %T %Y", + + /* am */ + "AM", + + /* pm */ + "PM", + + /* date_fmt */ + "%a %b %e %H:%M:%S %Z %Y" +}; + +enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL }; + +#ifndef YEAR_2000_NAME +#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" +#endif /* !defined YEAR_2000_NAME */ + +static char * +strftime_add(const char *str, char *pt, const char *ptlim) +{ + while (pt < ptlim && (*pt = *str++) != '\0') + ++pt; + return pt; } -static char *strftime_conv(char *p, const char *pe, int n, const char *format) { - char buf[22]; - (snprintf)(buf, sizeof(buf), format, n); - return strftime_add(p, pe, buf); +static char * +strftime_conv(int n, const char *format, char *pt, const char *ptlim) +{ + char buf[INT_STRLEN_MAXIMUM(int) + 1]; + (sprintf)(buf, format, n); + return strftime_add(buf, pt, ptlim); } -static char *strftime_secs(char *p, const char *pe, const struct tm *t) { - char ibuf[21]; - struct tm tmp; - int64_t s; - tmp = *t; /* Make a copy, mktime(3) modifies the tm struct. */ - s = mktime(&tmp); - FormatInt64(ibuf, s); - return strftime_add(p, pe, ibuf); +/* +** POSIX and the C Standard are unclear or inconsistent about +** what %C and %y do if the year is negative or exceeds 9999. +** Use the convention that %C concatenated with %y yields the +** same output as %Y, and that %Y contains at least 4 bytes, +** with more only if necessary. +*/ + +static char * +strftime_yconv( + int a, + int b, + bool convert_top, + bool convert_yy, + char *pt, + const char *ptlim) +{ + register int lead; + register int trail; + + trail = a % DIVISOR + b % DIVISOR; + lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) { + if (lead == 0 && trail < 0) + pt = strftime_add("-0", pt, ptlim); + else pt = strftime_conv(lead, "%02d", pt, ptlim); + } + if (convert_yy) + pt = strftime_conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); + return pt; } -static char *strftime_timefmt(char *p, const char *pe, const char *format, - const struct tm *t) { - int i; - long diff; - char const *sign; - /* size_t z1, z2, z3; */ - for (; *format; ++format) { - if (*format == '%') { - label: - switch (*++format) { - case '\0': - --format; - break; - case 'A': - p = strftime_add(p, pe, - (t->tm_wday < 0 || t->tm_wday > 6) - ? "?" - : kWeekdayName[t->tm_wday]); - continue; - case 'a': - p = strftime_add(p, pe, - (t->tm_wday < 0 || t->tm_wday > 6) - ? "?" - : kWeekdayNameShort[t->tm_wday]); - continue; - case 'B': - p = strftime_add( - p, pe, - (t->tm_mon < 0 || t->tm_mon > 11) ? "?" : kMonthName[t->tm_mon]); - continue; - case 'b': - case 'h': - p = strftime_add(p, pe, - (t->tm_mon < 0 || t->tm_mon > 11) - ? "?" - : kMonthNameShort[t->tm_mon]); - continue; - case 'c': - p = strftime_timefmt(p, pe, "%D %X", t); - continue; - case 'C': - /* - ** %C used to do a... - ** strftime_timefmt("%a %b %e %X %Y", t); - ** ...whereas now POSIX 1003.2 calls for - ** something completely different. - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, div100int64(t->tm_year + TM_YEAR_BASE), - "%02d"); - continue; - case 'D': - p = strftime_timefmt(p, pe, "%m/%d/%y", t); - continue; - case 'x': - /* - ** Version 3.0 of strftime from Arnold Robbins - ** (arnold@skeeve.atl.ga.us) does the - ** equivalent of... - ** strftime_timefmt("%a %b %e %Y"); - ** ...for %x; since the X3J11 C language - ** standard calls for "date, using locale's - ** date format," anything goes. Using just - ** numbers (as here) makes Quakers happier. - ** Word from Paul Eggert (eggert@twinsun.com) - ** is that %Y-%m-%d is the ISO standard date - ** format, specified in ISO 2014 and later - ** ISO 8601:1988, with a summary available in - ** pub/doc/ISO/english/ISO8601.ps.Z on - ** ftp.uni-erlangen.de. - ** (ado, 5/30/93) - */ - p = strftime_timefmt(p, pe, "%m/%d/%y", t); - continue; - case 'd': - p = strftime_conv(p, pe, t->tm_mday, "%02d"); - continue; - case 'E': - case 'O': - /* - ** POSIX locale extensions, a la - ** Arnold Robbins' strftime version 3.0. - ** The sequences - ** %Ec %EC %Ex %Ey %EY - ** %Od %oe %OH %OI %Om %OM - ** %OS %Ou %OU %OV %Ow %OW %Oy - ** are supposed to provide alternate - ** representations. - ** (ado, 5/24/93) - */ - goto label; - case 'e': - p = strftime_conv(p, pe, t->tm_mday, "%2d"); - continue; - case 'H': - p = strftime_conv(p, pe, t->tm_hour, "%02d"); - continue; - case 'I': - p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12, - "%02d"); - continue; - case 'j': - p = strftime_conv(p, pe, t->tm_yday + 1, "%03d"); - continue; - case 'k': - /* - ** This used to be... - ** strftime_conv(t->tm_hour % 12 ? - ** t->tm_hour % 12 : 12, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbins' - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, t->tm_hour, "%2d"); - continue; +static char * +strftime_fmt(const char *format, const struct tm *t, char *pt, + const char *ptlim, enum warn *warnp) +{ + for ( ; *format; ++format) { + if (*format == '%') { +label: + switch (*++format) { + case '\0': + --format; + break; + case 'A': + pt = strftime_add( + (t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->weekday[t->tm_wday], + pt, ptlim); + continue; + case 'a': + pt = strftime_add( + (t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : Locale->wday[t->tm_wday], + pt, ptlim); + continue; + case 'B': + pt = strftime_add( + (t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->month[t->tm_mon], + pt, ptlim); + continue; + case 'b': + case 'h': + pt = strftime_add( + (t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : Locale->mon[t->tm_mon], + pt, ptlim); + continue; + case 'C': + /* + ** %C used to do a... + ** _fmt("%a %b %e %X %Y", t); + ** ...whereas now POSIX 1003.2 calls for + ** something completely different. + ** (ado, 1993-05-24) + */ + pt = strftime_yconv(t->tm_year, TM_YEAR_BASE, + true, false, pt, ptlim); + continue; + case 'c': + { + enum warn warn2 = IN_SOME; + + pt = strftime_fmt(Locale->c_fmt, t, pt, ptlim, &warn2); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'D': + pt = strftime_fmt("%m/%d/%y", t, pt, ptlim, warnp); + continue; + case 'd': + pt = strftime_conv(t->tm_mday, "%02d", pt, ptlim); + continue; + case 'E': + case 'O': + /* + ** Locale modifiers of C99 and later. + ** The sequences + ** %Ec %EC %Ex %EX %Ey %EY + ** %Od %oe %OH %OI %Om %OM + ** %OS %Ou %OU %OV %Ow %OW %Oy + ** are supposed to provide alternative + ** representations. + */ + goto label; + case 'e': + pt = strftime_conv(t->tm_mday, "%2d", pt, ptlim); + continue; + case 'F': + pt = strftime_fmt("%Y-%m-%d", t, pt, ptlim, warnp); + continue; + case 'H': + pt = strftime_conv(t->tm_hour, "%02d", pt, ptlim); + continue; + case 'I': + pt = strftime_conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%02d", pt, ptlim); + continue; + case 'j': + pt = strftime_conv(t->tm_yday + 1, "%03d", pt, ptlim); + continue; + case 'k': + /* + ** This used to be... + ** _conv(t->tm_hour % 12 ? + ** t->tm_hour % 12 : 12, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbins' + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = strftime_conv(t->tm_hour, "%2d", + pt, ptlim); + continue; #ifdef KITCHEN_SINK - case 'K': - /* - ** After all this time, still unclaimed! - */ - p = strftime_add(p, pe, "kitchen sink"); - continue; + case 'K': + /* + ** After all this time, still unclaimed! + */ + pt = strftime_add("kitchen sink", pt, ptlim); + continue; #endif /* defined KITCHEN_SINK */ - case 'l': - /* - ** This used to be... - ** strftime_conv(t->tm_hour, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbin's - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12, - "%2d"); - continue; - case 'M': - p = strftime_conv(p, pe, t->tm_min, "%02d"); - continue; - case 'm': - p = strftime_conv(p, pe, t->tm_mon + 1, "%02d"); - continue; - case 'n': - p = strftime_add(p, pe, "\n"); - continue; - case 'p': - p = strftime_add(p, pe, t->tm_hour >= 12 ? "PM" : "AM"); - continue; - case 'R': - p = strftime_timefmt(p, pe, "%H:%M", t); - continue; - case 'r': - p = strftime_timefmt(p, pe, "%I:%M:%S %p", t); - continue; - case 'S': - p = strftime_conv(p, pe, t->tm_sec, "%02d"); - continue; - case 's': - p = strftime_secs(p, pe, t); - continue; - case 'T': - case 'X': - p = strftime_timefmt(p, pe, "%H:%M:%S", t); - continue; - case 't': - p = strftime_add(p, pe, "\t"); - continue; - case 'U': - p = strftime_conv(p, pe, (t->tm_yday + 7 - t->tm_wday) / 7, "%02d"); - continue; - case 'u': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "ISO 8601: Weekday as a decimal number - ** [1 (Monday) - 7]" - ** (ado, 5/24/93) - */ - p = strftime_conv(p, pe, (t->tm_wday == 0) ? 7 : t->tm_wday, "%d"); - continue; - case 'V': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "the week number of the year (the first - ** Monday as the first day of week 1) as a - ** decimal number (01-53). The method for - ** determining the week number is as specified - ** by ISO 8601 (to wit: if the week containing - ** January 1 has four or more days in the new - ** year, then it is week 1, otherwise it is - ** week 53 of the previous year and the next - ** week is week 1)." - ** (ado, 5/24/93) - */ - /* - ** XXX--If January 1 falls on a Friday, - ** January 1-3 are part of week 53 of the - ** previous year. By analogy, if January - ** 1 falls on a Thursday, are December 29-31 - ** of the PREVIOUS year part of week 1??? - ** (ado 5/24/93) - ** - ** You are understood not to expect this. - */ - i = (t->tm_yday + 10 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7; - if (i == 0) { - /* - ** What day of the week does - ** January 1 fall on? - */ - i = t->tm_wday - (t->tm_yday - 1); - /* - ** Fri Jan 1: 53 - ** Sun Jan 1: 52 - ** Sat Jan 1: 53 if previous - ** year a leap - ** year, else 52 - */ - if (i == TM_FRIDAY) - i = 53; - else if (i == TM_SUNDAY) - i = 52; - else - i = isleap(t->tm_year + TM_YEAR_BASE) ? 53 : 52; -#ifdef XPG4_1994_04_09 - /* - ** As of 4/9/94, though, - ** XPG4 calls for 53 - ** unconditionally. - */ - i = 53; -#endif /* defined XPG4_1994_04_09 */ - } - p = strftime_conv(p, pe, i, "%02d"); - continue; - case 'v': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "date as dd-bbb-YYYY" - ** (ado, 5/24/93) - */ - p = strftime_timefmt(p, pe, "%e-%b-%Y", t); - continue; - case 'W': - p = strftime_conv( - p, pe, (t->tm_yday + 7 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7, - "%02d"); - continue; - case 'w': - p = strftime_conv(p, pe, t->tm_wday, "%d"); - continue; - case 'y': - p = strftime_conv(p, pe, (t->tm_year + TM_YEAR_BASE) % 100, "%02d"); - continue; - case 'Y': - p = strftime_conv(p, pe, t->tm_year + TM_YEAR_BASE, "%04d"); - continue; - case 'Z': - if (t->tm_zone) { - p = strftime_add(p, pe, t->tm_zone); - } else { - if (t->tm_isdst == 0 || t->tm_isdst == 1) { - p = strftime_add(p, pe, tzname[t->tm_isdst]); - } else { - p = strftime_add(p, pe, "?"); - } - } - continue; - case 'z': - if (t->tm_isdst < 0) continue; -#ifdef TM_GMTOFF - diff = t->TM_GMTOFF; -#else /* !defined TM_GMTOFF */ - if (t->tm_isdst == 0) -#ifdef USG_COMPAT - diff = -timezone; -#else /* !defined USG_COMPAT */ - continue; -#endif /* !defined USG_COMPAT */ - else -#ifdef ALTZONE - diff = -altzone; -#else /* !defined ALTZONE */ - continue; -#endif /* !defined ALTZONE */ -#endif /* !defined TM_GMTOFF */ - if (diff < 0) { - sign = "-"; - diff = -diff; - } else { - sign = "+"; - } - p = strftime_add(p, pe, sign); - diff /= SECSPERMIN; - diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR); - p = strftime_conv(p, pe, diff, "%04d"); - continue; - case '%': - /* - * X311J/88-090 (4.12.3.5): if conversion char is - * undefined, behavior is undefined. Print out the - * character itself as printf(3) also does. - */ - default: - break; - } - } - if (p >= pe) break; - *p++ = *format; - } - return p; + case 'l': + /* + ** This used to be... + ** _conv(t->tm_hour, 2, ' '); + ** ...and has been changed to the below to + ** match SunOS 4.1.1 and Arnold Robbin's + ** strftime version 3.0. That is, "%k" and + ** "%l" have been swapped. + ** (ado, 1993-05-24) + */ + pt = strftime_conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + "%2d", pt, ptlim); + continue; + case 'M': + pt = strftime_conv(t->tm_min, "%02d", + pt, ptlim); + continue; + case 'm': + pt = strftime_conv(t->tm_mon + 1, "%02d", + pt, ptlim); + continue; + case 'n': + pt = strftime_add("\n", pt, ptlim); + continue; + case 'p': + pt = strftime_add( + (t->tm_hour >= (HOURSPERDAY / 2)) ? + Locale->pm : + Locale->am, + pt, ptlim); + continue; + case 'R': + pt = strftime_fmt("%H:%M", t, pt, ptlim, warnp); + continue; + case 'r': + pt = strftime_fmt("%I:%M:%S %p", t, pt, + ptlim, warnp); + continue; + case 'S': + pt = strftime_conv(t->tm_sec, "%02d", pt, + ptlim); + continue; + case 's': + { + struct tm tm; + char buf[INT_STRLEN_MAXIMUM( + time_t) + 1]; + time_t mkt; + + tm = *t; + tm.tm_yday = -1; + mkt = mktime(&tm); + if (mkt == (time_t) -1) { + /* Fail unless this -1 represents + a valid time. */ + struct tm tm_1; + if (!localtime_r(&mkt, &tm_1)) + return NULL; + if (!(tm.tm_year == tm_1.tm_year + && tm.tm_yday == tm_1.tm_yday + && tm.tm_hour == tm_1.tm_hour + && tm.tm_min == tm_1.tm_min + && tm.tm_sec == tm_1.tm_sec)) + return NULL; + } + if (TYPE_SIGNED(time_t)) { + intmax_t n = mkt; + (sprintf)(buf, "%"PRIdMAX, n); + } else { + uintmax_t n = mkt; + (sprintf)(buf, "%"PRIuMAX, n); + } + pt = strftime_add(buf, pt, ptlim); + } + continue; + case 'T': + pt = strftime_fmt("%H:%M:%S", t, pt, ptlim, warnp); + continue; + case 't': + pt = strftime_add("\t", pt, ptlim); + continue; + case 'U': + pt = strftime_conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, + "%02d", pt, ptlim); + continue; + case 'u': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "ISO 8601: Weekday as a decimal number + ** [1 (Monday) - 7]" + ** (ado, 1993-05-24) + */ + pt = strftime_conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, + "%d", pt, ptlim); + continue; + case 'V': /* ISO 8601 week number */ + case 'G': /* ISO 8601 year (four digits) */ + case 'g': /* ISO 8601 year (two digits) */ +/* +** From Arnold Robbins' strftime version 3.0: "the week number of the +** year (the first Monday as the first day of week 1) as a decimal number +** (01-53)." +** (ado, 1993-05-24) +** +** From by Markus Kuhn: +** "Week 01 of a year is per definition the first week which has the +** Thursday in this year, which is equivalent to the week which contains +** the fourth day of January. In other words, the first week of a new year +** is the week which has the majority of its days in the new year. Week 01 +** might also contain days from the previous year and the week before week +** 01 of a year is the last week (52 or 53) of the previous year even if +** it contains days from the new year. A week starts with Monday (day 1) +** and ends with Sunday (day 7). For example, the first week of the year +** 1997 lasts from 1996-12-30 to 1997-01-05..." +** (ado, 1996-01-02) +*/ + { + int year; + int base; + int yday; + int wday; + int w; + + year = t->tm_year; + base = TM_YEAR_BASE; + yday = t->tm_yday; + wday = t->tm_wday; + for ( ; ; ) { + int len; + int bot; + int top; + + len = isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + /* + ** What yday (-3 ... 3) does + ** the ISO year begin on? + */ + bot = ((yday + 11 - wday) % + DAYSPERWEEK) - 3; + /* + ** What yday does the NEXT + ** ISO year begin on? + */ + top = bot - + (len % DAYSPERWEEK); + if (top < -3) + top += DAYSPERWEEK; + top += len; + if (yday >= top) { + ++base; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / + DAYSPERWEEK); + break; + } + --base; + yday += isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + } + if (*format == 'V') + pt = strftime_conv(w, "%02d", + pt, ptlim); + else if (*format == 'g') { + *warnp = IN_ALL; + pt = strftime_yconv(year, base, + false, true, + pt, ptlim); + } else pt = strftime_yconv(year, base, + true, true, + pt, ptlim); + } + continue; + case 'v': + /* + ** From Arnold Robbins' strftime version 3.0: + ** "date as dd-bbb-YYYY" + ** (ado, 1993-05-24) + */ + pt = strftime_fmt("%e-%b-%Y", t, pt, ptlim, warnp); + continue; + case 'W': + pt = strftime_conv( + (t->tm_yday + DAYSPERWEEK - + (t->tm_wday ? + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, + "%02d", pt, ptlim); + continue; + case 'w': + pt = strftime_conv(t->tm_wday, "%d", pt, ptlim); + continue; + case 'X': + pt = strftime_fmt(Locale->X_fmt, t, pt, ptlim, warnp); + continue; + case 'x': + { + enum warn warn2 = IN_SOME; + + pt = strftime_fmt(Locale->x_fmt, t, pt, ptlim, &warn2); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'y': + *warnp = IN_ALL; + pt = strftime_yconv(t->tm_year, TM_YEAR_BASE, + false, true, + pt, ptlim); + continue; + case 'Y': + pt = strftime_yconv(t->tm_year, TM_YEAR_BASE, + true, true, + pt, ptlim); + continue; + case 'Z': + pt = strftime_add(t->tm_zone, pt, ptlim); + /* + ** C99 and later say that %Z must be + ** replaced by the empty string if the + ** time zone abbreviation is not + ** determinable. + */ + continue; + case 'z': + { + long diff; + char const * sign; + bool negative; + + diff = t->tm_gmtoff; + negative = diff < 0; + if (diff == 0) { + negative = t->tm_zone[0] == '-'; + } + if (negative) { + sign = "-"; + diff = -diff; + } else sign = "+"; + pt = strftime_add(sign, pt, ptlim); + diff /= SECSPERMIN; + diff = (diff / MINSPERHOUR) * 100 + + (diff % MINSPERHOUR); + pt = strftime_conv(diff, "%04d", pt, ptlim); + } + continue; + case '+': + pt = strftime_fmt(Locale->date_fmt, t, pt, + ptlim, warnp); + continue; + case '%': + /* + ** X311J/88-090 (4.12.3.5): if conversion char is + ** undefined, behavior is undefined. Print out the + ** character itself as printf(3) also does. + */ + default: + break; + } + } + if (pt == ptlim) + break; + *pt++ = *format; + } + return pt; } /** @@ -379,14 +570,32 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format, * * @return bytes copied excluding nul, or 0 on error */ -size_t strftime(char *s, size_t size, const char *f, const struct tm *t) { - char *p; - p = strftime_timefmt(s, s + size, f, t); - if (p < s + size) { - *p = '\0'; - return p - s; - } else { - s[size - 1] = '\0'; - return 0; - } +size_t +strftime(char *s, size_t maxsize, const char *format, const struct tm *t) +{ + char * p; + int saved_errno = errno; + enum warn warn = IN_NONE; + + tzset(); + p = strftime_fmt(format, t, s, s + maxsize, &warn); + if (!p) { + errno = EOVERFLOW; + return 0; + } + if (p == s + maxsize) { + errno = ERANGE; + return 0; + } + *p = '\0'; + errno = saved_errno; + return p - s; +} + +size_t +strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t, + locale_t locale) +{ + /* Just call strftime, as only the C locale is supported. */ + return strftime(s, maxsize, format, t); } diff --git a/libc/time/time.h b/libc/time/time.h index 47e498bee..510d19ba8 100644 --- a/libc/time/time.h +++ b/libc/time/time.h @@ -17,6 +17,8 @@ hidden extern const unsigned short kMonthYearDay[2][12]; extern char *tzname[2]; extern long CLOCKS_PER_SEC; +extern long timezone; +extern int daylight; int64_t clock(void); int64_t time(int64_t *); diff --git a/libc/time/time.mk b/libc/time/time.mk index 412653371..da673d944 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -61,12 +61,15 @@ o/$(MODE)/libc/time/strftime.o: \ OVERRIDE_CFLAGS += \ -fno-jump-tables -o/$(MODE)/libc/time/localtime.o \ -o/$(MODE)/libc/time/strftime.o: \ +o/$(MODE)/libc/time/localtime.o: \ OVERRIDE_CFLAGS += \ -fdata-sections \ -ffunction-sections +o/$(MODE)/libc/time/localtime.o: \ + OVERRIDE_CPPFLAGS += \ + -DSTACK_FRAME_UNLIMITED + o/$(MODE)/libc/time/now.o: \ OVERRIDE_CFLAGS += \ -O3 diff --git a/libc/time/timezone.c b/libc/time/timezone.c new file mode 100644 index 000000000..80d45411c --- /dev/null +++ b/libc/time/timezone.c @@ -0,0 +1,23 @@ +/*-*- 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/time/time.h" + +char *tzname[2]; +long timezone; +int daylight; diff --git a/libc/time/tz.internal.h b/libc/time/tz.internal.h new file mode 100644 index 000000000..8dd16b6da --- /dev/null +++ b/libc/time/tz.internal.h @@ -0,0 +1,544 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ +#define COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ +#include "libc/calls/calls.h" +#include "libc/calls/weirdtypes.h" +#include "libc/errno.h" +#include "libc/inttypes.h" +#include "libc/limits.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/ok.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +/* clang-format off */ +/* Private header for tzdb code. */ + +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +*/ + +/* +** This header is for use ONLY with the time conversion code. +** There is no guarantee that it will remain unchanged, +** or that it will remain at all. +** Do NOT copy it to any system include directory. +** Thank you! +*/ + +/* +** zdump has been made independent of the rest of the time +** conversion package to increase confidence in the verification it provides. +** You can use zdump to help in verifying other implementations. +** To do this, compile with -DUSE_LTZ=0 and link without the tz library. +*/ +#ifndef USE_LTZ +# define USE_LTZ 1 +#endif + +/* This string was in the Factory zone through version 2016f. */ +#define GRANDPARENTED "Local time zone must be set--see zic manual page" + +/* +** Defaults for preprocessor symbols. +** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'. +*/ + +#ifndef HAVE_DECL_ASCTIME_R +#define HAVE_DECL_ASCTIME_R 1 +#endif + +#if !defined HAVE_GENERIC && defined __has_extension +# if __has_extension(c_generic_selections) +# define HAVE_GENERIC 1 +# else +# define HAVE_GENERIC 0 +# endif +#endif +/* _Generic is buggy in pre-4.9 GCC. */ +#if !defined HAVE_GENERIC && defined __GNUC__ +# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__)) +#endif +#ifndef HAVE_GENERIC +# define HAVE_GENERIC (201112 <= __STDC_VERSION__) +#endif + +#ifndef HAVE_GETTEXT +#define HAVE_GETTEXT 0 +#endif /* !defined HAVE_GETTEXT */ + +#ifndef HAVE_INCOMPATIBLE_CTIME_R +#define HAVE_INCOMPATIBLE_CTIME_R 0 +#endif + +#ifndef HAVE_LINK +#define HAVE_LINK 1 +#endif /* !defined HAVE_LINK */ + +#ifndef HAVE_MALLOC_ERRNO +#define HAVE_MALLOC_ERRNO 1 +#endif + +#ifndef HAVE_POSIX_DECLS +#define HAVE_POSIX_DECLS 1 +#endif + +#ifndef HAVE_STRTOLL +#define HAVE_STRTOLL 1 +#endif + +#ifndef HAVE_SYMLINK +#define HAVE_SYMLINK 1 +#endif /* !defined HAVE_SYMLINK */ + +#if HAVE_INCOMPATIBLE_CTIME_R +#define asctime_r _incompatible_asctime_r +#define ctime_r _incompatible_ctime_r +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + +/* +** Nested includes +*/ + +/* Avoid clashes with NetBSD by renaming NetBSD's declarations. + If defining the 'timezone' variable, avoid a clash with FreeBSD's + 'timezone' function by renaming its declaration. */ +#define localtime_rz sys_localtime_rz +#define mktime_z sys_mktime_z +#define posix2time_z sys_posix2time_z +#define time2posix_z sys_time2posix_z +#if defined USG_COMPAT && USG_COMPAT == 2 +# define timezone sys_timezone +#endif +#define timezone_t sys_timezone_t +#define tzalloc sys_tzalloc +#define tzfree sys_tzfree +#undef localtime_rz +#undef mktime_z +#undef posix2time_z +#undef time2posix_z +#if defined USG_COMPAT && USG_COMPAT == 2 +# undef timezone +#endif +#undef timezone_t +#undef tzalloc +#undef tzfree + +#if HAVE_GETTEXT +#include +#endif /* HAVE_GETTEXT */ + +#ifndef HAVE_STRFTIME_L +# if _POSIX_VERSION < 200809 +# define HAVE_STRFTIME_L 0 +# else +# define HAVE_STRFTIME_L 1 +# endif +#endif + +#ifndef USG_COMPAT +# ifndef _XOPEN_VERSION +# define USG_COMPAT 0 +# else +# define USG_COMPAT 1 +# endif +#endif + +#ifndef HAVE_TZNAME +# if _POSIX_VERSION < 198808 && !USG_COMPAT +# define HAVE_TZNAME 0 +# else +# define HAVE_TZNAME 1 +# endif +#endif + +#ifndef ALTZONE +# if defined __sun || defined _M_XENIX +# define ALTZONE 1 +# else +# define ALTZONE 0 +# endif +#endif + +#ifndef R_OK +#define R_OK 4 +#endif /* !defined R_OK */ + +#if 3 <= __GNUC__ +# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec)) +#else +# define ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +/* +** Workarounds for compilers/systems. +*/ + +#ifndef EPOCH_LOCAL +# define EPOCH_LOCAL 0 +#endif +#ifndef EPOCH_OFFSET +# define EPOCH_OFFSET 0 +#endif + +/* +** Compile with -Dtime_tz=T to build the tz package with a private +** int64_t type equivalent to T rather than the system-supplied int64_t. +** This debugging feature can test unusual design decisions +** (e.g., int64_t wider than 'long', or unsigned int64_t) even on +** typical platforms. +*/ +#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0 +# define TZ_INT64_T 1 +#else +# define TZ_INT64_T 0 +#endif + +#if defined LOCALTIME_IMPLEMENTATION && TZ_INT64_T +static int64_t sys_time(int64_t *x) { return time(x); } +#endif + +#if TZ_INT64_T + +typedef time_tz tz_int64_t; + +# undef asctime +# define asctime tz_asctime +# undef asctime_r +# define asctime_r tz_asctime_r +# undef ctime +# define ctime tz_ctime +# undef ctime_r +# define ctime_r tz_ctime_r +# undef difftime +# define difftime tz_difftime +# undef gmtime +# define gmtime tz_gmtime +# undef gmtime_r +# define gmtime_r tz_gmtime_r +# undef localtime +# define localtime tz_localtime +# undef localtime_r +# define localtime_r tz_localtime_r +# undef localtime_rz +# define localtime_rz tz_localtime_rz +# undef mktime +# define mktime tz_mktime +# undef mktime_z +# define mktime_z tz_mktime_z +# undef offtime +# define offtime tz_offtime +# undef posix2time +# define posix2time tz_posix2time +# undef posix2time_z +# define posix2time_z tz_posix2time_z +# undef strftime +# define strftime tz_strftime +# undef time +# define time tz_time +# undef time2posix +# define time2posix tz_time2posix +# undef time2posix_z +# define time2posix_z tz_time2posix_z +# undef int64_t +# define int64_t tz_int64_t +# undef timegm +# define timegm tz_timegm +# undef timelocal +# define timelocal tz_timelocal +# undef timeoff +# define timeoff tz_timeoff +# undef tzalloc +# define tzalloc tz_tzalloc +# undef tzfree +# define tzfree tz_tzfree +# undef tzset +# define tzset tz_tzset +# if HAVE_STRFTIME_L +# undef strftime_l +# define strftime_l tz_strftime_l +# endif +# if HAVE_TZNAME +# undef tzname +# define tzname tz_tzname +# endif +# if USG_COMPAT +# undef daylight +# define daylight tz_daylight +# undef timezone +# define timezone tz_timezone +# endif +# if ALTZONE +# undef altzone +# define altzone tz_altzone +# endif + +char *asctime(struct tm const *); +char *asctime_r(struct tm const *restrict, char *restrict); +char *ctime(int64_t const *); +char *ctime_r(int64_t const *, char *); +double difftime(int64_t, int64_t) pureconst; +size_t strftime(char *restrict, size_t, char const *restrict, + struct tm const *restrict); +# if HAVE_STRFTIME_L +size_t strftime_l(char *restrict, size_t, char const *restrict, + struct tm const *restrict, locale_t); +# endif +struct tm *gmtime(int64_t const *); +struct tm *gmtime_r(int64_t const *restrict, struct tm *restrict); +struct tm *localtime(int64_t const *); +struct tm *localtime_r(int64_t const *restrict, struct tm *restrict); +int64_t mktime(struct tm *); +int64_t time(int64_t *); +void tzset(void); +#endif + +#if !HAVE_DECL_ASCTIME_R && !defined asctime_r +extern char *asctime_r(struct tm const *restrict, char *restrict); +#endif + +#ifndef HAVE_DECL_ENVIRON +# if defined environ || defined __USE_GNU +# define HAVE_DECL_ENVIRON 1 +# else +# define HAVE_DECL_ENVIRON 0 +# endif +#endif + +#if 2 <= HAVE_TZNAME + (TZ_INT64_T || !HAVE_POSIX_DECLS) +extern char *tzname[]; +#endif +#if 2 <= USG_COMPAT + (TZ_INT64_T || !HAVE_POSIX_DECLS) +extern long timezone; +extern int daylight; +#endif +#if 2 <= ALTZONE + (TZ_INT64_T || !HAVE_POSIX_DECLS) +extern long altzone; +#endif + +/* +** The STD_INSPIRED functions are similar, but most also need +** declarations if time_tz is defined. +*/ + +#ifdef STD_INSPIRED +# if TZ_INT64_T || !defined offtime +struct tm *offtime(int64_t const *, long); +# endif +# if TZ_INT64_T || !defined timegm +int64_t timegm(struct tm *); +# endif +# if TZ_INT64_T || !defined timelocal +int64_t timelocal(struct tm *); +# endif +# if TZ_INT64_T || !defined timeoff +int64_t timeoff(struct tm *, long); +# endif +# if TZ_INT64_T || !defined time2posix +int64_t time2posix(int64_t); +# endif +# if TZ_INT64_T || !defined posix2time +int64_t posix2time(int64_t); +# endif +#endif + +/* Infer TM_ZONE on systems where this information is known, but suppress + guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ +#define TM_GMTOFF tm_gmtoff +#define TM_ZONE tm_zone + +/* +** Define functions that are ABI compatible with NetBSD but have +** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t +** and labors under the misconception that 'const timezone_t' is a +** pointer to a constant. This use of 'const' is ineffective, so it +** is not done here. What we call 'struct state' NetBSD calls +** 'struct __state', but this is a private name so it doesn't matter. +*/ +#if NETBSD_INSPIRED +typedef struct state *timezone_t; +struct tm *localtime_rz(timezone_t restrict, int64_t const *restrict, + struct tm *restrict); +int64_t mktime_z(timezone_t restrict, struct tm *restrict); +timezone_t tzalloc(char const *); +void tzfree(timezone_t); +# ifdef STD_INSPIRED +# if TZ_INT64_T || !defined posix2time_z +int64_t posix2time_z(timezone_t, int64_t) nosideeffect; +# endif +# if TZ_INT64_T || !defined time2posix_z +int64_t time2posix_z(timezone_t, int64_t) nosideeffect; +# endif +# endif +#endif + +/* +** Finally, some convenience items. +*/ + +#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT) +#define TYPE_SIGNED(type) (((type) -1) < 0) +#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) + +/* Max and min values of the integer type T, of which only the bottom + B bits are used, and where the highest-order used bit is considered + to be a sign bit if T is signed. */ +#define MAXVAL(t, b) \ + ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \ + - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t))))) +#define MINVAL(t, b) \ + ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0)) + +/* The extreme time values, assuming no padding. */ +#define INT64_T_MIN_NO_PADDING MINVAL(int64_t, TYPE_BIT(int64_t)) +#define INT64_T_MAX_NO_PADDING MAXVAL(int64_t, TYPE_BIT(int64_t)) + +/* The extreme time values. These are macros, not constants, so that + any portability problems occur only when compiling .c files that use + the macros, which is safer for applications that need only zdump and zic. + This implementation assumes no padding if int64_t is signed and + either the compiler lacks support for _Generic or int64_t is not one + of the standard signed integer types. */ +#if HAVE_GENERIC +# define INT64_T_MIN \ + _Generic((int64_t) 0, \ + signed char: SCHAR_MIN, short: SHRT_MIN, \ + int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \ + default: INT64_T_MIN_NO_PADDING) +# define INT64_T_MAX \ + (TYPE_SIGNED(int64_t) \ + ? _Generic((int64_t) 0, \ + signed char: SCHAR_MAX, short: SHRT_MAX, \ + int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \ + default: INT64_T_MAX_NO_PADDING) \ + : (int64_t) -1) +#else +# define INT64_T_MIN INT64_T_MIN_NO_PADDING +# define INT64_T_MAX INT64_T_MAX_NO_PADDING +#endif + +/* +** 302 / 1000 is log10(2.0) rounded up. +** Subtract one for the sign bit if the type is signed; +** add one for integer division truncation; +** add one more for a minus sign if the type is signed. +*/ +#define INT_STRLEN_MAXIMUM(type) \ + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) + +/* +** INITIALIZE(x) +*/ + +#define INITIALIZE(x) ((x) = 0) + +/* Whether memory access must strictly follow the C standard. + If 0, it's OK to read uninitialized storage so long as the value is + not relied upon. Defining it to 0 lets mktime access parts of + struct tm that might be uninitialized, as a heuristic when the + standard doesn't say what to return and when tm_gmtoff can help + mktime likely infer a better value. */ +#ifndef UNINIT_TRAP +# define UNINIT_TRAP 0 +#endif + +#ifdef DEBUG +# define UNREACHABLE() abort() +#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__) +# define UNREACHABLE() __builtin_unreachable() +#elif defined __has_builtin +# if __has_builtin(__builtin_unreachable) +# define UNREACHABLE() __builtin_unreachable() +# endif +#endif +#ifndef UNREACHABLE +# define UNREACHABLE() ((void) 0) +#endif + +/* +** For the benefit of GNU folk... +** '_(MSGID)' uses the current locale's message library string for MSGID. +** The default is to use gettext if available, and use MSGID otherwise. +*/ + +#if HAVE_GETTEXT +#define _(msgid) gettext(msgid) +#else /* !HAVE_GETTEXT */ +#define _(msgid) msgid +#endif /* !HAVE_GETTEXT */ + +#if !defined TZ_DOMAIN && defined HAVE_GETTEXT +# define TZ_DOMAIN "tz" +#endif + +#if HAVE_INCOMPATIBLE_CTIME_R +#undef asctime_r +#undef ctime_r +char *asctime_r(struct tm const *, char *); +char *ctime_r(int64_t const *, char *); +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + +/* Handy macros that are independent of tzfile implementation. */ + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ +#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1) +#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY) +#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT) + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 +#define TM_WDAY_BASE TM_MONDAY + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + +/* +** Since everything in isleap is modulo 400 (or a factor of 400), we know that +** isleap(y) == isleap(y % 400) +** and so +** isleap(a + b) == isleap((a + b) % 400) +** or +** isleap(a + b) == isleap(a % 400 + b % 400) +** This is true even if % means modulo rather than Fortran remainder +** (which is allowed by C89 but not by C99 or later). +** We use this to avoid addition overflow problems. +*/ + +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ */ diff --git a/libc/time/tzfile.internal.h b/libc/time/tzfile.internal.h index cc2aa6067..bfef13de0 100644 --- a/libc/time/tzfile.internal.h +++ b/libc/time/tzfile.internal.h @@ -1,46 +1,56 @@ #ifndef TZFILE_H #define TZFILE_H - -#define TM_ZONE tm_zone -#define TM_GMTOFF tm_gmtoff +/* clang-format off */ +/* Layout and location of TZif files. */ /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. */ +/* +** This header is for use ONLY with the time conversion code. +** There is no guarantee that it will remain unchanged, +** or that it will remain at all. +** Do NOT copy it to any system include directory. +** Thank you! +*/ + /* ** Information about time zone files. */ #ifndef TZDIR -#define TZDIR "/zip/usr/share/zoneinfo" -#endif +#define TZDIR "/zip/usr/share/zoneinfo" /* Time zone object file directory */ +#endif /* !defined TZDIR */ #ifndef TZDEFAULT -#define TZDEFAULT "GST" -#endif +#define TZDEFAULT "GST" +#endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES -#define TZDEFRULES "New_York" -#endif +#define TZDEFRULES "New_York" +#endif /* !defined TZDEFRULES */ + + +/* See Internet RFC 8536 for more details about the following format. */ /* ** Each file begins with. . . */ -#define TZ_MAGIC "TZif" +#define TZ_MAGIC "TZif" struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ - char tzh_reserved[15]; /* reserved; must be zero */ - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */ + char tzh_reserved[15]; /* reserved; must be zero */ + char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* @@ -58,14 +68,15 @@ struct tzhead { ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if 1, transition ** time is standard time, if 0, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition -** time is UT, if 0, -** transition time is local time -** if absent, transition times are +** transition time is local (wall clock) +** time; if absent, transition times are ** assumed to be local time +** tzh_ttisutcnt (char)s indexed by type; if 1, transition +** time is UT, if 0, transition time is +** local time; if absent, transition +** times are assumed to be local time. +** When this is 1, the corresponding +** std/wall indicator must also be 1. */ /* @@ -91,73 +102,21 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 1200 +#define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ -/* (limited by what unsigned chars can hold) */ +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY - -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) - -/* -** Since everything in isleap is modulo 400 (or a factor of 400), we know that -** isleap(y) == isleap(y % 400) -** and so -** isleap(a + b) == isleap((a + b) % 400) -** or -** isleap(a + b) == isleap(a % 400 + b % 400) -** This is true even if % means modulo rather than Fortran remainder -** (which is allowed by C89 but not C99). -** We use this to avoid addition overflow problems. -*/ - -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) - #endif /* !defined TZFILE_H */ diff --git a/libc/tinymath/complex.internal.h b/libc/tinymath/complex.internal.h new file mode 100644 index 000000000..40b2130f4 --- /dev/null +++ b/libc/tinymath/complex.internal.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_TINYMATH_COMPLEX_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_TINYMATH_COMPLEX_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +_Complex double __ldexp_cexp(_Complex double, int) hidden; +_Complex float __ldexp_cexpf(_Complex float, int) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_TINYMATH_COMPLEX_INTERNAL_H_ */ diff --git a/libc/tinymath/csqrt.c b/libc/tinymath/csqrt.c new file mode 100644 index 000000000..d182ec42b --- /dev/null +++ b/libc/tinymath/csqrt.c @@ -0,0 +1,139 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/complex.h" +#include "libc/math.h" +#include "libc/tinymath/complex.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +/* clang-format off */ + +/* origin: FreeBSD /usr/src/lib/msun/src/s_csqrt.c */ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +// TODO(elimisteve): write test proving why we do / don't need it +// #pragma STDC CX_LIMITED_RANGE ON + +/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ +#define THRESH 0x1.a827999fcef32p+1022 + +/** + * Returns complex square root. + */ +complex double csqrt(complex double z) +{ + complex double result; + double a, b; + double t; + int scale; + + a = creal(z); + b = cimag(z); + + /* Handle special cases. */ + if (z == 0) + return CMPLX(0, b); + if (isinf(b)) + return CMPLX(INFINITY, b); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return CMPLX(a, t); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrt(inf + NaN i) = inf + NaN i + * csqrt(inf + y i) = inf + 0 i + * csqrt(-inf + NaN i) = NaN +- inf i + * csqrt(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return CMPLX(fabs(b - b), copysign(a, b)); + else + return CMPLX(a, copysign(b - b, b)); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* Scale to avoid overflow. */ + if (fabs(a) >= THRESH || fabs(b) >= THRESH) { + a *= 0.25; + b *= 0.25; + scale = 1; + } else { + scale = 0; + } + + /* Algorithm 312, CACM vol 10, Oct 1967. */ + if (a >= 0) { + t = sqrt((a + hypot(a, b)) * 0.5); + result = CMPLX(t, b / (2 * t)); + } else { + t = sqrt((-a + hypot(a, b)) * 0.5); + result = CMPLX(fabs(b) / (2 * t), copysign(t, b)); + } + + /* Rescale. */ + if (scale) + result *= 2; + return result; +} diff --git a/libc/zip.h b/libc/zip.h index 038cac34c..35477adf5 100644 --- a/libc/zip.h +++ b/libc/zip.h @@ -44,11 +44,10 @@ #define kZipCompressionNone 0 #define kZipCompressionDeflate 8 -#define kZipCdirHdrMagic 0x06054b50 /* PK♣♠ "PK\5\6" */ -#define kZipCdirHdrMinSize 22 -#define kZipCdirAlign kZipAlign -#define kZipCdirHdrLinkableSize \ - ROUNDUP(kZipCfileHdrMinSize + PATH_MAX, kZipCdirAlign) +#define kZipCdirHdrMagic 0x06054b50 /* PK♣♠ "PK\5\6" */ +#define kZipCdirHdrMinSize 22 +#define kZipCdirAlign kZipAlign +#define kZipCdirHdrLinkableSize 294 #define kZipCdir64HdrMagic 0x06064b50 /* PK♣♠ "PK\6\6" */ #define kZipCdir64HdrMinSize 56 diff --git a/net/http/decodelatin1.c b/net/http/decodelatin1.c index a52ce8b95..04ec84bd7 100644 --- a/net/http/decodelatin1.c +++ b/net/http/decodelatin1.c @@ -39,7 +39,7 @@ char *DecodeLatin1(const char *p, size_t n, size_t *z) { if (n == -1) n = p ? strlen(p) : 0; if ((q = r = malloc(n * 2 + 1))) { for (i = 0; i < n;) { - memset(vz, 0, 16); /* 3x speedup for ASCII */ + bzero(vz, 16); /* 3x speedup for ASCII */ while (i + 16 < n) { memcpy(v1, p + i, 16); pcmpgtb(v2, v1, vz); diff --git a/net/http/encodelatin1.c b/net/http/encodelatin1.c index 924188c4e..abb15c433 100644 --- a/net/http/encodelatin1.c +++ b/net/http/encodelatin1.c @@ -40,7 +40,7 @@ char *EncodeLatin1(const char *p, size_t n, size_t *z, int f) { size_t i; char t[256]; char *r, *q; - memset(t, 0, sizeof(t)); + bzero(t, sizeof(t)); if (f & kControlC0) memset(t + 0x00, 1, 0x20 - 0x00), t[0x7F] = 1; if (f & kControlC1) memset(t + 0x80, 1, 0xA0 - 0x80); t['\t'] = t['\r'] = t['\n'] = t['\v'] = !!(f & kControlWs); diff --git a/net/http/foldheader.c b/net/http/foldheader.c index f840527dc..ac930abf9 100644 --- a/net/http/foldheader.c +++ b/net/http/foldheader.c @@ -21,6 +21,9 @@ #include "libc/str/str.h" #include "net/http/http.h" +/** + * Collapses repeating headers onto a single line. + */ char *FoldHeader(struct HttpMessage *msg, char *b, int h, size_t *z) { char *p; size_t i, n, m; diff --git a/net/http/hascontrolcodes.c b/net/http/hascontrolcodes.c index c7fba8f22..dd6c4e30d 100644 --- a/net/http/hascontrolcodes.c +++ b/net/http/hascontrolcodes.c @@ -34,7 +34,7 @@ ssize_t HasControlCodes(const char *p, size_t n, int f) { char t[256]; wint_t x, a, b; size_t i, j, m, g; - memset(t, 0, sizeof(t)); + bzero(t, sizeof(t)); if (f & kControlC0) memset(t + 0x00, 1, 0x20 - 0x00), t[0x7F] = 1; if (f & kControlC1) memset(t + 0x80, 1, 0xA0 - 0x80); t['\t'] = t['\r'] = t['\n'] = t['\v'] = !!(f & kControlWs); diff --git a/net/http/parsehttpmessage.c b/net/http/parsehttpmessage.c index f9babfebb..77afdf730 100644 --- a/net/http/parsehttpmessage.c +++ b/net/http/parsehttpmessage.c @@ -36,7 +36,7 @@ */ void InitHttpMessage(struct HttpMessage *r, int type) { assert(type == kHttpRequest || type == kHttpResponse); - memset(r, 0, sizeof(*r)); + bzero(r, sizeof(*r)); r->type = type; } diff --git a/net/http/parseurl.c b/net/http/parseurl.c index cf5a8c0a6..8bdc6ac1b 100644 --- a/net/http/parseurl.c +++ b/net/http/parseurl.c @@ -257,7 +257,7 @@ static char *ParseUrlImpl(const char *s, size_t n, struct Url *h, bool latin1) { u.isform = false; u.isopaque = false; u.islatin1 = latin1; - memset(h, 0, sizeof(*h)); + bzero(h, sizeof(*h)); if ((m = malloc(latin1 ? u.n * 2 : u.n))) { u.q = u.p = m; if (ParseScheme(&u, h)) ParseAuthority(&u, h); diff --git a/net/http/underlong.c b/net/http/underlong.c index 0d924dbfb..e6e1c959b 100644 --- a/net/http/underlong.c +++ b/net/http/underlong.c @@ -46,7 +46,7 @@ char *Underlong(const char *p, size_t n, size_t *z) { if (n == -1) n = p ? strlen(p) : 0; if ((q = r = malloc(n + 1))) { for (i = 0; i < n;) { - memset(vz, 0, 16); /* 50x speedup for ASCII */ + bzero(vz, 16); /* 50x speedup for ASCII */ while (i + 16 < n) { memcpy(v1, p + i, 16); pcmpgtb(v2, v1, vz); diff --git a/net/https/getsslcachefile.c b/net/https/getsslcachefile.c index 6023a471f..bbcffaf3c 100644 --- a/net/https/getsslcachefile.c +++ b/net/https/getsslcachefile.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/fmt/fmt.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "net/https/sslcache.h" @@ -26,10 +27,10 @@ * @return pointer to static memory */ char *GetSslCacheFile(void) { - static char sslcachefile[PATH_MAX + 1]; + static char sslcachefile[PATH_MAX]; if (snprintf(sslcachefile, sizeof(sslcachefile), "%s/%s.sslcache", firstnonnull(getenv("TMPDIR"), "/tmp"), - getenv("USER")) <= PATH_MAX) { + getenv("USER")) < ARRAYLEN(sslcachefile)) { return sslcachefile; } else { return 0; diff --git a/net/https/getsslroots.c b/net/https/getsslroots.c index d6bd77fcb..7b5bc2fc2 100644 --- a/net/https/getsslroots.c +++ b/net/https/getsslroots.c @@ -47,15 +47,15 @@ mbedtls_x509_crt *GetSslRoots(void) { size_t n, m; struct dirent *e; static bool once; + char path[PATH_MAX]; static mbedtls_x509_crt *c; - char path[PATH_MAX + 1]; if (!once) { if ((c = calloc(1, sizeof(*c)))) { m = stpcpy(path, "/zip/usr/share/ssl/root/") - path; if ((d = opendir(path))) { while ((e = readdir(d))) { if (e->d_type != DT_REG) continue; - if (m + (n = strlen(e->d_name)) > PATH_MAX) continue; + if (m + (n = strlen(e->d_name)) >= ARRAYLEN(path)) continue; memcpy(path + m, e->d_name, n + 1); CHECK((p = xslurp(path, &n))); CHECK_GE(mbedtls_x509_crt_parse(c, p, n + 1), 0, "%s", path); diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index 88e101a7a..07587ab20 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -72,5 +72,5 @@ TEST(access, testRequestWriteOnReadOnly_returnsEaccess) { } TEST(access, runThisExecutable) { - ASSERT_SYS(0, 0, access(program_executable_name, R_OK | X_OK)); + ASSERT_SYS(0, 0, access(GetProgramExecutableName(), R_OK | X_OK)); } diff --git a/test/libc/calls/clock_gettime_test.c b/test/libc/calls/clock_gettime_test.c new file mode 100644 index 000000000..e67d61473 --- /dev/null +++ b/test/libc/calls/clock_gettime_test.c @@ -0,0 +1,29 @@ +/*-*- 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/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/sysv/consts/clock.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +BENCH(clock_gettime, bench) { + struct timespec ts; + EZBENCH2("nowl", donothing, nowl()); + EZBENCH2("clock_gettime", donothing, clock_gettime(CLOCK_REALTIME, &ts)); +} diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index e4f6da7a7..955d41266 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -54,34 +54,34 @@ void TearDown(void) { TEST(commandv, testPathSearch) { EXPECT_NE(-1, touch("bin/sh", 0755)); - EXPECT_STREQ("bin/sh", commandv("sh", pathbuf)); + EXPECT_STREQ("bin/sh", commandv("sh", pathbuf, sizeof(pathbuf))); } TEST(commandv, testPathSearch_appendsComExtension) { EXPECT_NE(-1, touch("bin/sh.com", 0755)); - EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf)); + EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf, sizeof(pathbuf))); } TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) { EXPECT_NE(-1, touch("home/sh", 0755)); i = g_syscount; - EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf)); + EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf, sizeof(pathbuf))); if (!IsWindows()) EXPECT_EQ(i + 1, g_syscount); } TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) { EXPECT_NE(-1, touch("home/sh.com", 0755)); i = g_syscount; - EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf)); + EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf, sizeof(pathbuf))); if (!IsWindows()) EXPECT_EQ(i + 2, g_syscount); } TEST(commandv, testSameDir_doesntHappenByDefaultUnlessItsWindows) { EXPECT_NE(-1, touch("bog", 0755)); if (IsWindows()) { - EXPECT_STREQ("./bog", commandv("bog", pathbuf)); + EXPECT_STREQ("./bog", commandv("bog", pathbuf, sizeof(pathbuf))); } else { - EXPECT_EQ(NULL, commandv("bog", pathbuf)); + EXPECT_EQ(NULL, commandv("bog", pathbuf, sizeof(pathbuf))); } } @@ -89,9 +89,9 @@ TEST(commandv, testSameDir_willHappenWithColonBlank) { CHECK_NE(-1, setenv("PATH", "bin:", true)); EXPECT_NE(-1, touch("bog", 0755)); if (IsWindows()) { - EXPECT_STREQ("./bog", commandv("bog", pathbuf)); + EXPECT_STREQ("./bog", commandv("bog", pathbuf, sizeof(pathbuf))); } else { - EXPECT_STREQ("bog", commandv("bog", pathbuf)); + EXPECT_STREQ("bog", commandv("bog", pathbuf, sizeof(pathbuf))); } } @@ -99,8 +99,8 @@ TEST(commandv, testSameDir_willHappenWithColonBlank2) { CHECK_NE(-1, setenv("PATH", ":bin", true)); EXPECT_NE(-1, touch("bog", 0755)); if (IsWindows()) { - EXPECT_STREQ("./bog", commandv("bog", pathbuf)); + EXPECT_STREQ("./bog", commandv("bog", pathbuf, sizeof(pathbuf))); } else { - EXPECT_STREQ("bog", commandv("bog", pathbuf)); + EXPECT_STREQ("bog", commandv("bog", pathbuf, sizeof(pathbuf))); } } diff --git a/test/libc/calls/dup_test.c b/test/libc/calls/dup_test.c index 1f7ff126a..5c8734786 100644 --- a/test/libc/calls/dup_test.c +++ b/test/libc/calls/dup_test.c @@ -51,8 +51,8 @@ TEST(dup, clearsCloexecFlag) { ASSERT_NE(-1, (ws = xspawn(0))); if (ws == -2) { dup2(3, 0); - execv(program_executable_name, - (char *const[]){program_executable_name, "boop", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "boop", 0}); _exit(127); } ASSERT_EQ(72, WEXITSTATUS(ws)); diff --git a/test/libc/calls/execve_test.c b/test/libc/calls/execve_test.c index ce11cd967..88cbd140f 100644 --- a/test/libc/calls/execve_test.c +++ b/test/libc/calls/execve_test.c @@ -29,6 +29,13 @@ void SetUp(void) { } else { exit(7); } + } else if (getenv("_WEIRDENV")) { + for (char **e = environ; *e; ++e) { + if (!strcmp(*e, "WEIRD")) { + exit(0); + } + } + exit(7); } } @@ -47,3 +54,20 @@ TEST(execve, testWeirdAnsiC89emptyArgv) { EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(0, WEXITSTATUS(ws)); } + +TEST(execve, testWeirdEnvironmentVariable) { + char *prog; + int pid, ws; + if (IsWindows()) return; + if (IsOpenbsd()) return; + prog = GetProgramExecutableName(); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + execve(prog, (char *const[]){prog, 0}, + (char *const[]){"_WEIRDENV=1", "WEIRD", 0}); + _Exit(127); + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); +} diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index e6adae3bd..022ca22b6 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -40,3 +40,10 @@ TEST(getcwd, testNullBuf_allocatesResult) { EXPECT_NE(-1, chdir("subdir")); EXPECT_STREQ("subdir", basename(gc(getcwd(0, 0)))); } + +TEST(getcwd, testWindows_addsFunnyPrefix) { + if (!IsWindows()) return; + char path[PATH_MAX]; + ASSERT_NE(0, getcwd(path, sizeof(path))); + EXPECT_STARTSWITH("//?/", path); +} diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index 8da031afd..e0037d558 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -17,9 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/log/check.h" +#include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" @@ -58,6 +60,15 @@ TEST(mkdir, testPathIsDirectory_EEXIST) { EXPECT_EQ(EEXIST, errno); } +TEST(mkdir, enametoolong) { + int i; + size_t n = 2048; + char *d, *s = gc(calloc(1, n)); + for (i = 0; i < n - 1; ++i) s[i] = 'x'; + s[i] = 0; + EXPECT_SYS(ENAMETOOLONG, -1, mkdir(s, 0644)); +} + TEST(makedirs, testEmptyString_EEXIST) { EXPECT_EQ(-1, mkdir("", 0755)); EXPECT_EQ(ENOENT, errno); @@ -72,3 +83,14 @@ TEST(mkdirat, testRelativePath_opensRelativeToDirFd) { EXPECT_EQ(-1, makedirs("", 0755)); EXPECT_NE(-1, close(dirfd)); } + +TEST(mkdir, longname) { + int i; + char *d, s[270] = {0}; + for (i = 0; i < sizeof(s) - 1; ++i) s[i] = 'x'; + s[i] = 0; + ASSERT_NE(NULL, (d = gc(getcwd(0, 0)))); + memcpy(s, d, strlen(d)); + s[strlen(d)] = '/'; + ASSERT_SYS(0, 0, mkdir(s, 0644)); +} diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index 755e7f6db..db1b4afe8 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -23,7 +23,7 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -char16_t cmdline[ARG_MAX]; +char16_t cmdline[ARG_MAX / 2]; TEST(mkntcmdline, emptyArgvList_cantBeEmptyOnWindows) { char *argv[] = {NULL}; diff --git a/test/libc/calls/mkntenvblock_test.c b/test/libc/calls/mkntenvblock_test.c index b441a08fa..3c7c4bac2 100644 --- a/test/libc/calls/mkntenvblock_test.c +++ b/test/libc/calls/mkntenvblock_test.c @@ -20,7 +20,7 @@ #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" -char16_t envvars[ARG_MAX]; +char16_t envvars[ARG_MAX / 2]; TEST(mkntenvblock, emptyList_onlyOutputsDoubleNulStringTerminator) { char *envp[] = {NULL}; diff --git a/test/libc/calls/read_test.c b/test/libc/calls/read_test.c index c2f156805..20db7ebb4 100644 --- a/test/libc/calls/read_test.c +++ b/test/libc/calls/read_test.c @@ -38,7 +38,13 @@ BENCH(read, bench) { char buf[16]; ASSERT_SYS(0, 3, open("/dev/zero", O_RDONLY)); EZBENCH2("read", donothing, read(3, buf, 5)); - EZBENCH2("readv", donothing, readv(3, &(struct iovec){buf, 5}, 1)); + EZBENCH2("pread", donothing, pread(3, buf, 5, 0)); + EZBENCH2("readv₁", donothing, readv(3, &(struct iovec){buf, 5}, 1)); + EZBENCH2("readv₂", donothing, + readv(3, (struct iovec[]){{buf, 1}, {buf + 1, 4}}, 2)); + EZBENCH2("preadv₁", donothing, preadv(3, &(struct iovec){buf, 5}, 1, 0)); + EZBENCH2("preadv₂", donothing, + preadv(3, (struct iovec[]){{buf, 1}, {buf + 1, 4}}, 2, 0)); EZBENCH2("sys_read", donothing, sys_read(3, buf, 5)); EZBENCH2("sys_readv", donothing, sys_readv(3, &(struct iovec){buf, 5}, 1)); EZBENCH2("Read", donothing, Read(3, buf, 5)); diff --git a/test/libc/calls/readlinkat_test.c b/test/libc/calls/readlinkat_test.c index add8e75aa..b178c07ee 100644 --- a/test/libc/calls/readlinkat_test.c +++ b/test/libc/calls/readlinkat_test.c @@ -83,10 +83,8 @@ TEST(readlinkat, frootloop) { ASSERT_SYS(0, 0, symlink("froot", "froot")); ASSERT_SYS(ELOOP, -1, readlink("froot/loop", buf, sizeof(buf))); if (O_NOFOLLOW) { - ASSERT_SYS(IsFreebsd() ? EMLINK - : IsNetbsd() ? EFTYPE - : ELOOP, - -1, open("froot", O_RDONLY | O_NOFOLLOW)); + ASSERT_SYS(IsFreebsd() ? EMLINK : IsNetbsd() ? EFTYPE : ELOOP, -1, + open("froot", O_RDONLY | O_NOFOLLOW)); if (0 && O_PATH) { /* need rhel5 test */ ASSERT_NE(-1, (fd = open("froot", O_RDONLY | O_NOFOLLOW | O_PATH))); ASSERT_NE(-1, close(fd)); @@ -101,3 +99,11 @@ TEST(readlinkat, statReadsNameLength) { EXPECT_TRUE(S_ISLNK(st.st_mode)); EXPECT_EQ(5, st.st_size); } + +TEST(readlinkat, realpathReturnsLongPath) { + if (!IsWindows()) return; + struct stat st; + char buf[PATH_MAX]; + ASSERT_SYS(0, 0, touch("froot", 0644)); + ASSERT_STARTSWITH("//?/", realpath("froot", buf)); +} diff --git a/test/libc/calls/seccomp_test.c b/test/libc/calls/seccomp_test.c index 7ea7d6329..3fe4a74b8 100644 --- a/test/libc/calls/seccomp_test.c +++ b/test/libc/calls/seccomp_test.c @@ -17,12 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/bpf.h" #include "libc/calls/struct/filter.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/seccomp.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/audit.h" @@ -32,16 +32,6 @@ #include "libc/testlib/testlib.h" #include "tool/net/sandbox.h" -bool __is_linux_2_6_23(void) { - int rc; - if (!IsLinux()) return false; - asm volatile("syscall" - : "=a"(rc) - : "0"(157), "D"(PR_GET_SECCOMP) - : "rcx", "r11", "memory"); - return rc != -EINVAL; -} - void SetUp(void) { if (!__is_linux_2_6_23()) { exit(0); diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index efbc18aab..fe4983c30 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -23,7 +23,6 @@ #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" diff --git a/test/libc/calls/sigprocmask_test.c b/test/libc/calls/sigprocmask_test.c index 5ccc94071..51836565e 100644 --- a/test/libc/calls/sigprocmask_test.c +++ b/test/libc/calls/sigprocmask_test.c @@ -23,7 +23,6 @@ #include "libc/calls/struct/sigset.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" -#include "libc/intrin/kprintf.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/calls/sigsuspend_test.c b/test/libc/calls/sigsuspend_test.c index 7cd46385e..0772c8ef9 100644 --- a/test/libc/calls/sigsuspend_test.c +++ b/test/libc/calls/sigsuspend_test.c @@ -21,7 +21,6 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" 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/fmt/atoi_test.c b/test/libc/fmt/atoi_test.c index a36ff54e3..1b0fc92c3 100644 --- a/test/libc/fmt/atoi_test.c +++ b/test/libc/fmt/atoi_test.c @@ -23,6 +23,13 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" +void __on_arithmetic_overflow(void) { + // prevent -ftrapv crashes + // + // for some reason gcc generates trap code even when we're doing it + // manually with __builtin_mul_overflow() :'( +} + TEST(atoi, test) { EXPECT_EQ(0, atoi("")); EXPECT_EQ(0, atoi("-b")); diff --git a/test/libc/fmt/formatbinary64_test.c b/test/libc/fmt/formatbinary64_test.c new file mode 100644 index 000000000..930a946d8 --- /dev/null +++ b/test/libc/fmt/formatbinary64_test.c @@ -0,0 +1,98 @@ +/*-*- 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/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +char buf[67]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(FormatBinary64, test1) { + EXPECT_EQ(1, FormatBinary64(buf, 0, 2) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatBinary64, test2) { + EXPECT_EQ(1, FormatBinary64(buf, 0, 0) - buf); + EXPECT_STREQ("0", buf); + EXPECT_EQ(3, FormatBinary64(buf, 0, 1) - buf); + EXPECT_STREQ("0b0", buf); +} + +TEST(FormatBinary64, test3) { + EXPECT_EQ(3, FormatBinary64(buf, 1, 2) - buf); + EXPECT_STREQ("0b1", buf); +} + +TEST(FormatBinary64, test4) { + EXPECT_EQ(1, FormatBinary64(buf, 1, 0) - buf); + EXPECT_STREQ("1", buf); +} + +TEST(FormatBinary64, test5) { + EXPECT_EQ(66, FormatBinary64(buf, 01777777777777777777777UL, 2) - buf); + EXPECT_STREQ( + "0b1111111111111111111111111111111111111111111111111111111111111111", + buf); +} + +TEST(FormatBinary64, test6) { + EXPECT_EQ(64, FormatBinary64(buf, 01777777777777777777777UL, 0) - buf); + EXPECT_STREQ( + "1111111111111111111111111111111111111111111111111111111111111111", buf); +} + +TEST(FormatBinary64, test7) { + EXPECT_EQ(66, FormatBinary64(buf, 0xEBF2AA499B9028EAul, 2) - buf); + EXPECT_STREQ( + "0b1110101111110010101010100100100110011011100100000010100011101010", + buf); +} + +TEST(FormatBinary64, test8) { + EXPECT_EQ(66, FormatBinary64(buf, 0x00F2AA499B9028EAul, 2) - buf); + EXPECT_STREQ( + "0b0000000011110010101010100100100110011011100100000010100011101010", + buf); +} + +TEST(FormatBinary64, testScalesToWordSizes) { + EXPECT_EQ(8 + 2, FormatBinary64(buf, 13, 2) - buf); + EXPECT_STREQ("0b00001101", buf); + EXPECT_EQ(16 + 2, FormatBinary64(buf, 31337, 2) - buf); + EXPECT_STREQ("0b0111101001101001", buf); + EXPECT_EQ(32 + 2, FormatBinary64(buf, 65536, 2) - buf); + EXPECT_STREQ("0b00000000000000010000000000000000", buf); +} + +BENCH(FormatBinary64, bench) { + EZBENCH2("FormatUint64 tiny", donothing, FormatUint64(buf, 1)); + EZBENCH2("FormatOctal64 tiny", donothing, FormatOctal64(buf, 1, true)); + EZBENCH2("FormatBinary64 tiny", donothing, FormatBinary64(buf, 1, 2)); + EZBENCH2("FormatUint64 big", donothing, + FormatUint64(buf, 01777777777777777777777UL)); + EZBENCH2("FormatOctal64 big", donothing, + FormatOctal64(buf, 01777777777777777777777UL, true)); + EZBENCH2("FormatBinary64 big", donothing, + FormatBinary64(buf, 01777777777777777777777UL, 2)); +} diff --git a/test/libc/fmt/formatflex64_test.c b/test/libc/fmt/formatflex64_test.c new file mode 100644 index 000000000..974966272 --- /dev/null +++ b/test/libc/fmt/formatflex64_test.c @@ -0,0 +1,34 @@ +/*-*- 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/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/testlib/testlib.h" + +char buf[25]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(formatflex64, test) { + EXPECT_EQ(5, FormatFlex64(buf, 31337, 2) - buf); + EXPECT_STREQ("31337", buf); + EXPECT_EQ(10, FormatFlex64(buf, 0x80000000, 2) - buf); + EXPECT_STREQ("0x80000000", buf); +} diff --git a/test/libc/fmt/formathex64_test.c b/test/libc/fmt/formathex64_test.c new file mode 100644 index 000000000..9b41dda2f --- /dev/null +++ b/test/libc/fmt/formathex64_test.c @@ -0,0 +1,87 @@ +/*-*- 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/fmt.h" +#include "libc/fmt/itoa.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +char buf[19]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(FormatHex64, test1) { + EXPECT_EQ(1, FormatHex64(buf, 0, 2) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatHex64, test2) { + EXPECT_EQ(1, FormatHex64(buf, 0, 0) - buf); + EXPECT_STREQ("0", buf); + EXPECT_EQ(3, FormatHex64(buf, 0, 1) - buf); + EXPECT_STREQ("0x0", buf); +} + +TEST(FormatHex64, test3) { + EXPECT_EQ(4, FormatHex64(buf, 1, 2) - buf); + EXPECT_STREQ("0x01", buf); +} + +TEST(FormatHex64, test4) { + EXPECT_EQ(1, FormatHex64(buf, 1, 0) - buf); + EXPECT_STREQ("1", buf); +} + +TEST(FormatHex64, test5) { + EXPECT_EQ(18, FormatHex64(buf, 01777777777777777777777UL, 2) - buf); + EXPECT_STREQ("0xffffffffffffffff", buf); +} + +TEST(FormatHex64, test6) { + EXPECT_EQ(16, FormatHex64(buf, 01777777777777777777777UL, 0) - buf); + EXPECT_STREQ("ffffffffffffffff", buf); +} + +TEST(FormatHex64, test7) { + EXPECT_EQ(18, FormatHex64(buf, 0xEBF2AA499B9028EAul, 2) - buf); + EXPECT_STREQ("0xebf2aa499b9028ea", buf); +} + +TEST(FormatHex64, test8) { + EXPECT_EQ(18, FormatHex64(buf, 0x00F2AA499B9028EAul, 2) - buf); + EXPECT_STREQ("0x00f2aa499b9028ea", buf); +} + +TEST(FormatHex64, testScalesToWordSizes) { + EXPECT_EQ(2 + 2, FormatHex64(buf, 13, 2) - buf); + EXPECT_STREQ("0x0d", buf); + EXPECT_EQ(4 + 2, FormatHex64(buf, 31337, 2) - buf); + EXPECT_STREQ("0x7a69", buf); + EXPECT_EQ(8 + 2, FormatHex64(buf, 65536, 2) - buf); + EXPECT_STREQ("0x00010000", buf); +} + +BENCH(FormatHex64, bench) { + EZBENCH2("FormatUint64 tiny", donothing, FormatUint64(buf, 1)); + EZBENCH2("FormatOctal64 tiny", donothing, FormatOctal64(buf, 1, true)); + EZBENCH2("FormatHex64 tiny", donothing, FormatHex64(buf, 1, 2)); + EZBENCH2("FormatHex64 big", donothing, + FormatHex64(buf, 01777777777777777777777UL, 2)); +} diff --git a/test/libc/fmt/formatoctal64_test.c b/test/libc/fmt/formatoctal64_test.c index 421b48f17..82e3b675d 100644 --- a/test/libc/fmt/formatoctal64_test.c +++ b/test/libc/fmt/formatoctal64_test.c @@ -21,7 +21,7 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -char buf[25]; +char buf[24]; void SetUp(void) { memset(buf, 0x55, sizeof(buf)); diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index cf4f9ab09..071c15f5d 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -354,6 +354,33 @@ TEST(ksnprintf, badUtf16) { } } +TEST(ksnprintf, truncation) { + char buf[16] = {0}; + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(0, 0, "%s", "xxxxx"); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 1, "%s", "xxxxx"); + EXPECT_STREQ("", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 2, "%s", "xxxxx"); + EXPECT_STREQ(".", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 3, "%s", "xxxxx"); + EXPECT_STREQ("..", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 4, "%s", "xxxxx"); + EXPECT_STREQ("...", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 5, "%s", "xxxxx"); + EXPECT_STREQ("x...", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 6, "%s", "xxxxxxxxxxx"); + EXPECT_STREQ("xx...", buf); + rngset(buf, sizeof(buf) - 1, lemur64, -1); + ksnprintf(buf, 7, "%s", "xxxxxxxxx"); + EXPECT_STREQ("xxx...", buf); +} + BENCH(printf, bench) { char b[128]; int snprintf_(char *, size_t, const char *, ...) asm("snprintf"); diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index eb8ab23cd..e283d19cc 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -177,8 +177,8 @@ TEST(ShowCrashReports, testMemoryLeakCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "6", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "6", 0}); _exit(127); } close(fds[1]); @@ -255,8 +255,8 @@ TEST(ShowCrashReports, testStackOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "5", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "5", 0}); _exit(127); } close(fds[1]); @@ -364,8 +364,8 @@ TEST(ShowCrashReports, testDivideByZero) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "1", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "1", 0}); _exit(127); } close(fds[1]); @@ -486,8 +486,8 @@ TEST(ShowCrashReports, testBssOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "2", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "2", 0}); _exit(127); } close(fds[1]); @@ -565,8 +565,8 @@ TEST(ShowCrashReports, testNpeCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "7", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "7", 0}); _exit(127); } close(fds[1]); @@ -625,8 +625,8 @@ TEST(ShowCrashReports, testDataOverrunCrash) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "4", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "4", 0}); _exit(127); } close(fds[1]); @@ -680,8 +680,8 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) { if (!pid) { dup2(fds[1], 1); dup2(fds[1], 2); - execv(program_executable_name, - (char *const[]){program_executable_name, "8", 0}); + execv(GetProgramExecutableName(), + (char *const[]){GetProgramExecutableName(), "8", 0}); _exit(127); } close(fds[1]); diff --git a/test/libc/mem/pledge_test.c b/test/libc/mem/pledge_test.c new file mode 100644 index 000000000..808141f41 --- /dev/null +++ b/test/libc/mem/pledge_test.c @@ -0,0 +1,62 @@ +/*-*- 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" + +void SetUp(void) { + if (!__is_linux_2_6_23() && !IsOpenbsd()) { + exit(0); + } +} + +TEST(pledge, default_allowsExit) { + int ws, pid; + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + ASSERT_SYS(0, 0, pledge("", 0)); + _Exit(0); + } + EXPECT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); +} + +TEST(pledge, stdio_forbidsOpeningPasswd) { + int ws, pid; + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + ASSERT_SYS(0, 0, pledge("stdio", 0)); + ASSERT_SYS(EPERM, -1, open("/etc/passwd", O_RDWR)); + _Exit(0); + } + EXPECT_NE(-1, wait(&ws)); + if (IsLinux()) { + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + } else { + EXPECT_TRUE(WIFSIGNALED(ws)); + EXPECT_EQ(SIGABRT, WTERMSIG(ws)); + } +} diff --git a/test/libc/mem/test.mk b/test/libc/mem/test.mk index 63b50a2d4..cdb4dc420 100644 --- a/test/libc/mem/test.mk +++ b/test/libc/mem/test.mk @@ -4,6 +4,7 @@ PKGS += TEST_LIBC_MEM TEST_LIBC_MEM_FILES := $(wildcard test/libc/mem/*) +TEST_LIBC_MEM_SRCS = $(TEST_LIBC_MEM_SRCS_C) $(TEST_LIBC_MEM_SRCS_CC) TEST_LIBC_MEM_SRCS_C = $(filter %_test.c,$(TEST_LIBC_MEM_FILES)) TEST_LIBC_MEM_SRCS_CC = $(filter %_test.cc,$(TEST_LIBC_MEM_FILES)) @@ -12,7 +13,7 @@ TEST_LIBC_MEM_OBJS = \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.o) TEST_LIBC_MEM_COMS = \ - $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com) \ + $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com) \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.com) TEST_LIBC_MEM_BINS = \ @@ -20,7 +21,7 @@ TEST_LIBC_MEM_BINS = \ $(TEST_LIBC_MEM_COMS:%=%.dbg) TEST_LIBC_MEM_TESTS = \ - $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com.ok) \ + $(TEST_LIBC_MEM_SRCS_C:%.c=o/$(MODE)/%.com.ok) \ $(TEST_LIBC_MEM_SRCS_CC:%.cc=o/$(MODE)/%.com.ok) TEST_LIBC_MEM_CHECKS = \ diff --git a/test/libc/calls/fork_test.c b/test/libc/runtime/fork_test.c similarity index 76% rename from test/libc/calls/fork_test.c rename to test/libc/runtime/fork_test.c index 53e6c9a74..688d4cd04 100644 --- a/test/libc/calls/fork_test.c +++ b/test/libc/runtime/fork_test.c @@ -17,11 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/dce.h" +#include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/sig.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -79,6 +84,51 @@ TEST(fork, testSharedMemory) { EXPECT_NE(-1, munmap(privatevar, FRAMESIZE)); } +static volatile bool gotsigusr1; +static volatile bool gotsigusr2; + +static void OnSigusr1(int sig) { + gotsigusr1 = true; +} + +static void OnSigusr2(int sig) { + gotsigusr2 = true; +} + +TEST(fork, childToChild) { + if (IsWindows()) return; // :'( + sigset_t mask, oldmask; + int ws, parent, child1, child2; + gotsigusr1 = false; + gotsigusr2 = false; + parent = getpid(); + signal(SIGUSR1, OnSigusr1); + signal(SIGUSR2, OnSigusr2); + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, &oldmask); + ASSERT_NE(-1, (child1 = fork())); + if (!child1) { + kill(parent, SIGUSR2); + sigsuspend(0); + _Exit(!gotsigusr1); + } + sigdelset(&mask, SIGUSR2); + sigsuspend(&mask); + ASSERT_NE(-1, (child2 = fork())); + if (!child2) { + kill(child1, SIGUSR1); + _Exit(0); + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + sigprocmask(SIG_SETMASK, &oldmask, 0); +} + void ForkInSerial(void) { int pid, ws; ASSERT_NE(-1, (pid = fork())); diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index ccc7e20c0..83c1f247c 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -22,7 +22,7 @@ TEST(GetDosArgv, empty) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(0, GetDosArgv(u"", buf, size, argv, max)); @@ -33,7 +33,7 @@ TEST(GetDosArgv, empty) { TEST(GetDosArgv, emptyish) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(0, GetDosArgv(u" ", buf, size, argv, max)); @@ -44,7 +44,7 @@ TEST(GetDosArgv, emptyish) { TEST(GetDosArgv, basicUsage) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(3, GetDosArgv(u"a\t \"b c\" d ", buf, size, argv, max)); @@ -58,7 +58,7 @@ TEST(GetDosArgv, basicUsage) { TEST(GetDosArgv, advancedUsage) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, GetDosArgv(u"(╯°□°)╯︵ ┻━┻", buf, size, argv, max)); @@ -71,7 +71,7 @@ TEST(GetDosArgv, advancedUsage) { TEST(GetDosArgv, testAegeanGothicSupplementaryPlanes) { size_t max = 4; /* these symbols are almost as old as dos */ - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, GetDosArgv(u"𐄷𐄸𐄹𐄺𐄻𐄼 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", buf, size, argv, max)); @@ -84,7 +84,7 @@ TEST(GetDosArgv, testAegeanGothicSupplementaryPlanes) { TEST(GetDosArgv, realWorldUsage) { size_t max = 512; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(5, GetDosArgv(u"C:\\Users\\jtunn\\printargs.com oh yes yes yes", @@ -145,7 +145,7 @@ TEST(GetDosArgv, quoteInMiddleOfArg_wontSplitArg) { TEST(GetDosArgv, waqQuoting1) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, @@ -159,7 +159,7 @@ TEST(GetDosArgv, waqQuoting1) { TEST(GetDosArgv, waqQuoting2) { size_t max = 4; - size_t size = ARG_MAX; + size_t size = ARG_MAX / 2; char *buf = malloc(size * sizeof(char)); char **argv = malloc(max * sizeof(char *)); EXPECT_EQ(2, GetDosArgv(u"\"a\\\"b c\" d", buf, size, argv, max)); diff --git a/test/libc/runtime/memtrack_test.c b/test/libc/runtime/memtrack_test.c index ff84e1727..200ec8a2e 100644 --- a/test/libc/runtime/memtrack_test.c +++ b/test/libc/runtime/memtrack_test.c @@ -63,7 +63,7 @@ static void CheckMemoryIntervalsAreOk(const struct MemoryIntervals *mm) { static void RunTrackMemoryIntervalTest(const struct MemoryIntervals t[2], int x, int y, long h) { struct MemoryIntervals *mm; - mm = memcpy(malloc(sizeof(*t)), t, sizeof(*t)); + mm = memcpy(memalign(64, sizeof(*t)), t, sizeof(*t)); CheckMemoryIntervalsAreOk(mm); CHECK_NE(-1, TrackMemoryInterval(mm, x, y, h, 0, 0, 0, 0, 0, 0)); CheckMemoryIntervalsAreOk(mm); @@ -75,7 +75,7 @@ static int RunReleaseMemoryIntervalsTest(const struct MemoryIntervals t[2], int x, int y) { int rc; struct MemoryIntervals *mm; - mm = memcpy(malloc(sizeof(*t)), t, sizeof(*t)); + mm = memcpy(memalign(64, sizeof(*t)), t, sizeof(*t)); CheckMemoryIntervalsAreOk(mm); if ((rc = ReleaseMemoryIntervals(mm, x, y, NULL)) != -1) { CheckMemoryIntervalsAreOk(mm); 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/test/libc/runtime/mprotect_test.c b/test/libc/runtime/mprotect_test.c index 366bb2cbc..d9f64e1a4 100644 --- a/test/libc/runtime/mprotect_test.c +++ b/test/libc/runtime/mprotect_test.c @@ -57,7 +57,7 @@ void OnSigBus(int sig, struct siginfo *si, struct ucontext *ctx) { #if 0 kprintf("SIGBUS%n"); kprintf("si->si_signo = %G%n", si->si_signo); - kprintf("si->si_errno = %s (%d)%n", strerror_short(si->si_errno), + kprintf("si->si_errno = %s (%d)%n", strerrno(si->si_errno), si->si_errno); kprintf("si->si_code = %s (%d)%n", GetSiCodeName(sig, si->si_code), si->si_code); diff --git a/test/libc/tinymath/complex_test.c b/test/libc/tinymath/complex_test.c new file mode 100644 index 000000000..70e6f493e --- /dev/null +++ b/test/libc/tinymath/complex_test.c @@ -0,0 +1,34 @@ +/*-*- 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/complex.h" +#include "libc/fmt/fmt.h" +#include "libc/testlib/testlib.h" + +TEST(complex, test) { + char buf[128]; + complex double z, z1; + sprintf(buf, "I = %.1f%+.1fi", creal(_Complex_I), cimag(_Complex_I)); + EXPECT_STREQ("I = 0.0+1.0i", buf); + z1 = _Complex_I * _Complex_I; // imaginary unit squared + sprintf(buf, "I * I = %.1f%+.1fi", creal(z1), cimag(z1)); + EXPECT_STREQ("I * I = -1.0+0.0i", buf); + z = 1.0 + 2.0 * _Complex_I; // usual way to form a complex number pre-C11 + sprintf(buf, "z = %.1f%+.1fi", creal(z), cimag(z)); + EXPECT_STREQ("z = 1.0+2.0i", buf); +} diff --git a/test/libc/tinymath/csqrt_test.c b/test/libc/tinymath/csqrt_test.c new file mode 100644 index 000000000..d585a1b08 --- /dev/null +++ b/test/libc/tinymath/csqrt_test.c @@ -0,0 +1,28 @@ +/*-*- 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/complex.h" +#include "libc/fmt/fmt.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(csqrt, test) { + complex double x = csqrt(-1); + EXPECT_STREQ("0 1", gc(xasprintf("%g %g", creal(x), cimag(x)))); +} diff --git a/third_party/argon2/blake2b.c b/third_party/argon2/blake2b.c index b004e8ef5..5aad00f29 100644 --- a/third_party/argon2/blake2b.c +++ b/third_party/argon2/blake2b.c @@ -42,7 +42,7 @@ static const uint64_t blake2b_IV[8] = { UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179), }; -static const unsigned int blake2b_sigma[12][16] = { +static const unsigned char blake2b_sigma[12][16] = { {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, @@ -69,7 +69,7 @@ static inline void blake2b_set_lastblock(blake2b_state *S) { } static inline void blake2b_increment_counter(blake2b_state *S, - uint64_t inc) { + uint64_t inc) { S->t[0] += inc; S->t[1] += (S->t[0] < inc); } diff --git a/third_party/chibicc/chibicc.mk b/third_party/chibicc/chibicc.mk index c77672dac..ec387ba89 100644 --- a/third_party/chibicc/chibicc.mk +++ b/third_party/chibicc/chibicc.mk @@ -116,7 +116,7 @@ o/$(MODE)/third_party/chibicc/chibicc.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/chibicc/.chibicc/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/chibicc/.chibicc/.symtab o/$(MODE)/third_party/chibicc/as.com.dbg: \ diff --git a/third_party/dlmalloc/dlmalloc_abort.greg.c b/third_party/dlmalloc/dlmalloc_abort.greg.c index 12ea05742..d2b2fd17a 100644 --- a/third_party/dlmalloc/dlmalloc_abort.greg.c +++ b/third_party/dlmalloc/dlmalloc_abort.greg.c @@ -17,13 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" -#include "libc/intrin/kprintf.h" +#include "libc/calls/calls.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" -#include "third_party/dlmalloc/dlmalloc.h" +#include "libc/str/str.h" + +#define MESSAGE "dlmalloc_abort()\n" void dlmalloc_abort(void) { - kprintf("dlmalloc_abort()%n"); + write(2, MESSAGE, strlen(MESSAGE)); if (weaken(__die)) weaken(__die)(); _Exit(44); } diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 7952909ec..0f572ba11 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -125,6 +125,7 @@ │ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" #include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" @@ -136,6 +137,7 @@ #include "libc/calls/termios.h" #include "libc/calls/ttydefaults.h" #include "libc/calls/weirdtypes.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" @@ -147,6 +149,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/nt/version.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/stdio/append.internal.h" @@ -190,15 +193,22 @@ Copyright 2010-2013 Pieter Noordhuis \""); #define DEBUG(L, ...) (void)0 #endif -#define DUFF_ROUTINE_READ(STATE) \ - case STATE: \ - linenoiseRefreshLineForce(l); \ - rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, block); \ - if (rc == -1 && errno == EAGAIN) { \ - l->state = STATE; \ - return -1; \ - } \ - l->state = 0 +#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 = STATE + +#define DUFF_ROUTINE_READ(STATE) \ + DUFF_ROUTINE_LABEL(STATE); \ + rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, block); \ + if (rc == -1 && errno == EAGAIN) { \ + l->state = STATE; \ + return -1; \ + } #define BLOCKING_READ() rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, false) @@ -329,7 +339,7 @@ static int notwseparator(wint_t c) { } static int iswname(wint_t c) { - return !iswseparator(c) || c == '_' || c == '-' || c == '.'; + return !iswseparator(c) || c == '_' || c == '-' || c == '.' || c == ':'; } static int notwname(wint_t c) { @@ -399,7 +409,9 @@ static int linenoiseIsUnsupportedTerm(void) { char *term; static char once, res; if (!once) { - if ((term = getenv("TERM"))) { + if (IsWindows() && !IsAtLeastWindows10()) { + res = 1; + } else if ((term = getenv("TERM"))) { for (i = 0; i < sizeof(kUnsupported) / sizeof(*kUnsupported); i++) { if (!strcasecmp(term, kUnsupported[i])) { res = 1; @@ -458,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--); @@ -656,7 +668,7 @@ int linenoiseEnableRawMode(int fd) { raw = orig_termios; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - raw.c_oflag &= ~OPOST; + raw.c_oflag |= OPOST | ONLCR; raw.c_iflag |= IUTF8; raw.c_cflag |= CS8; raw.c_cc[VMIN] = 1; @@ -769,7 +781,9 @@ static ssize_t linenoiseRead(int fd, char *buf, size_t size, if (l && gotwinch) refreshme = 1; if (refreshme) linenoiseRefreshLine(l); if (!block && linenoisePoll(l, fd) == -1) return -1; + --__strace; rc = readansi(fd, buf, size); + ++__strace; if (rc == -1 && errno == EINTR) { if (!block) break; } else { @@ -810,10 +824,10 @@ struct winsize linenoiseGetTerminalSize(struct winsize ws, int ifd, int ofd) { } if (((!ws.ws_col || !ws.ws_row) && linenoiseRead(ifd, 0, 0, 0, 1) != -1 && linenoiseWriteStr( - ofd, "\0337" /* save position */ - "\033[9979;9979H" /* move cursor to bottom right corner */ - "\033[6n" /* report position */ - "\0338") != -1 && /* restore position */ + ofd, "\e7" /* save position */ + "\e[9979;9979H" /* move cursor to bottom right corner */ + "\e[6n" /* report position */ + "\e8") != -1 && /* restore position */ (n = linenoiseRead(ifd, b, sizeof(b), 0, 1)) != -1 && n && b[0] == 033 && b[1] == '[' && b[n - 1] == 'R')) { p = b + 2; @@ -827,8 +841,8 @@ struct winsize linenoiseGetTerminalSize(struct winsize ws, int ifd, int ofd) { /* Clear the screen. Used to handle ctrl+l */ void linenoiseClearScreen(int fd) { - linenoiseWriteStr(fd, "\033[H" /* move cursor to top left corner */ - "\033[2J"); /* erase display */ + linenoiseWriteStr(fd, "\e[H" /* move cursor to top left corner */ + "\e[2J"); /* erase display */ } static void linenoiseBeep(void) { @@ -962,7 +976,7 @@ static char *linenoiseMakeSearchPrompt(int fail, const char *s, int n) { if (fail) abAppends(&ab, "failed "); abAppends(&ab, "reverse-i-search `\e[4m"); abAppend(&ab, s, n); - abAppends(&ab, "\033[24m"); + abAppends(&ab, "\e[24m"); abAppends(&ab, s + n); abAppendw(&ab, READ32LE("') ")); return ab.b; @@ -1007,8 +1021,8 @@ static char *linenoiseRefreshHints(struct linenoiseState *l) { if (!hintsCallback) return 0; if (!(hint = hintsCallback(l->buf, &ansi1, &ansi2))) return 0; abInit(&ab); - ansi1 = "\033[90m"; - ansi2 = "\033[39m"; + ansi1 = "\e[90m"; + ansi2 = "\e[39m"; if (ansi1) abAppends(&ab, ansi1); abAppends(&ab, hint); if (ansi2) abAppends(&ab, ansi2); @@ -1174,7 +1188,7 @@ StartOver: abInit(&ab); abAppendw(&ab, '\r'); /* start of line */ if (l->rows - l->oldpos - 1 > 0) { - abAppends(&ab, "\033["); + abAppends(&ab, "\e["); abAppendu(&ab, l->rows - l->oldpos - 1); abAppendw(&ab, 'A'); /* cursor up clamped */ } @@ -1185,7 +1199,7 @@ StartOver: if (x && x + rune.n > xn) { if (cy >= 0) ++cy; if (x < xn) { - abAppends(&ab, "\033[K"); /* clear line forward */ + abAppends(&ab, "\e[K"); /* clear line forward */ } abAppends(&ab, "\r" /* start of line */ "\n"); /* cursor down unclamped */ @@ -1200,9 +1214,9 @@ StartOver: abAppendw(&ab, '*'); } else { flipit = hasflip && (i == flip[0] || i == flip[1]); - if (flipit) abAppendw(&ab, READ32LE("\033[1m")); + if (flipit) abAppendw(&ab, READ32LE("\e[1m")); abAppendw(&ab, tpenc(rune.c)); - if (flipit) abAppendw(&ab, READ64LE("\033[22m\0\0")); + if (flipit) abAppendw(&ab, READ64LE("\e[22m\0\0")); } t = wcwidth(rune.c); t = MAX(0, t); @@ -1217,7 +1231,7 @@ StartOver: } free(hint); } - abAppendw(&ab, READ32LE("\033[J")); /* erase display forwards */ + abAppendw(&ab, READ32LE("\e[J")); /* erase display forwards */ /* * if we are at the very end of the screen with our prompt, we need to @@ -1232,12 +1246,12 @@ StartOver: * move cursor to right position */ if (cy > 0) { - abAppendw(&ab, READ32LE("\033[\0")); + abAppendw(&ab, READ32LE("\e[\0")); abAppendu(&ab, cy); abAppendw(&ab, 'A'); /* cursor up */ } if (cx > 0) { - abAppendw(&ab, READ32LE("\r\033[")); + abAppendw(&ab, READ32LE("\r\e[")); abAppendu(&ab, cx); abAppendw(&ab, 'C'); /* cursor right */ } else if (!cx) { @@ -1265,11 +1279,15 @@ StartOver: } void linenoiseRefreshLine(struct linenoiseState *l) { + --__strace; linenoiseRefreshLineImpl(l, 0); + ++__strace; } static void linenoiseRefreshLineForce(struct linenoiseState *l) { + --__strace; linenoiseRefreshLineImpl(l, 1); + ++__strace; } static void linenoiseEditInsert(struct linenoiseState *l, const char *p, @@ -1509,7 +1527,7 @@ static void linenoiseEditYank(struct linenoiseState *l) { static void linenoiseEditRotate(struct linenoiseState *l) { if ((l->seq[1][0] == CTRL('Y') || - (l->seq[1][0] == 033 && l->seq[1][1] == 'y'))) { + (l->seq[1][0] == '\e' && l->seq[1][1] == 'y'))) { if (l->yi < l->len && l->yj <= l->len) { memmove(l->buf + l->yi, l->buf + l->yj, l->len - l->yj + 1); l->len -= l->yj - l->yi; @@ -1780,18 +1798,19 @@ struct linenoiseState *linenoiseBegin(const char *prompt, int ifd, int ofd) { free(l); return 0; } + l->state = DUFF_ROUTINE_START; l->buf[0] = 0; l->ifd = ifd; l->ofd = ofd; l->prompt = strdup(prompt ? prompt : ""); l->ws = linenoiseGetTerminalSize(l->ws, l->ifd, l->ofd); - linenoiseHistoryAdd(""); linenoiseWriteStr(l->ofd, l->prompt); abInit(&l->ab); return l; } void linenoiseReset(struct linenoiseState *l) { + l->buf[0] = 0; l->dirty = true; l->final = 0; l->hindex = 0; @@ -1814,6 +1833,10 @@ void linenoiseEnd(struct linenoiseState *l) { } } +static int CompareStrings(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + /** * Runs linenoise engine. * @@ -1837,14 +1860,15 @@ 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); } switch (l->state) { for (;;) { - DUFF_ROUTINE_READ(0); + DUFF_ROUTINE_READ(DUFF_ROUTINE_LOOP); HandleRead: if (!rc && l->len) { rc = 1; @@ -1870,7 +1894,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; @@ -1891,7 +1915,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; @@ -1933,29 +1956,30 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, // handle tab and tab-tab completion if (seq[0] == '\t' && completionCallback) { - size_t i, j, k, n, m, perline, itemlen; + size_t i, n, m; // we know that the user pressed tab once rc = 0; linenoiseFreeCompletions(&l->lc); i = Backwards(l, l->pos, iswname); - j = Forwards(l, l->pos, iswname); { - char *s = strndup(l->buf + i, j - i); + char *s = strndup(l->buf + i, l->pos - i); completionCallback(s, &l->lc); free(s); } m = GetCommonPrefixLength(&l->lc); - if (m > j - i || (m == j - i && l->lc.len == 1)) { + if (m > l->pos - i || (m == l->pos - i && l->lc.len == 1)) { // on common prefix (or single completion) we complete and return - n = i + m + (l->len - j); + n = i + m + (l->len - l->pos); if (linenoiseGrow(l, n + 1)) { - memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1); + memmove(l->buf + i + m, l->buf + l->pos, l->len - l->pos + 1); memcpy(l->buf + i, l->lc.cvec[0], m); - l->len = l->pos = n; + l->pos = i + m; + l->len = n; } continue; } if (l->lc.len > 1) { + qsort(l->lc.cvec, l->lc.len, sizeof(*l->lc.cvec), CompareStrings); // if there's a multiline completions, then do nothing and wait and // see if the user presses tab again. if the user does this we then // print ALL the completions, to above the editing line @@ -1965,29 +1989,42 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, free(s); } for (;;) { - DUFF_ROUTINE_READ(3); + DUFF_ROUTINE_READ(2); if (rc == 1 && seq[0] == '\t') { + const char **p; struct abuf ab; + int i, k, x, y, xn, yn, xy, itemlen; itemlen = linenoiseMaxCompletionWidth(&l->lc) + 4; - perline = MAX(1, (l->ws.ws_col - 1) / itemlen); - abInit(&ab); - abAppends(&ab, "\r\n\033[K"); - for (size_t i = 0; i < l->lc.len;) { - for (size_t j = 0; i < l->lc.len && j < perline; ++j, ++i) { - n = GetMonospaceWidth(l->lc.cvec[i], strlen(l->lc.cvec[i]), - 0); - abAppends(&ab, l->lc.cvec[i]); + xn = MAX(1, (l->ws.ws_col - 1) / itemlen); + yn = (l->lc.len + (xn - 1)) / xn; + if (!__builtin_mul_overflow(xn, yn, &xy) && + (p = calloc(xy, sizeof(char *)))) { + // arrange in column major order + for (i = x = 0; x < xn; ++x) { + for (y = 0; y < yn; ++y) { + p[y * xn + x] = i < l->lc.len ? l->lc.cvec[i++] : ""; + } + } + abInit(&ab); + abAppends(&ab, "\r\n\e[K"); + for (x = i = 0; i < xy; ++i) { + n = GetMonospaceWidth(p[i], strlen(p[i]), 0); + abAppends(&ab, p[i]); for (k = n; k < itemlen; ++k) { abAppendw(&ab, ' '); } + if (++x == xn) { + abAppendw(&ab, READ16LE("\r\n")); + x = 0; + } } - abAppendw(&ab, READ16LE("\r\n")); + ab.len -= 2; + abAppends(&ab, "\n"); + linenoiseWriteStr(l->ofd, ab.b); + linenoiseRefreshLine(l); + abFree(&ab); + free(p); } - ab.len -= 2; - abAppends(&ab, "\n"); - linenoiseWriteStr(l->ofd, ab.b); - linenoiseRefreshLine(l); - abFree(&ab); } else { goto HandleRead; } @@ -1998,7 +2035,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, // handle (1) emacs keyboard combos // (2) otherwise sigint exit if (seq[0] == CTRL('C')) { - DUFF_ROUTINE_READ(4); + DUFF_ROUTINE_READ(3); if (rc == 1) { switch (seq[0]) { CASE(CTRL('C'), linenoiseEditInterrupt(l)); @@ -2019,7 +2056,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, if (ispaused) { linenoiseUnpause(l->ofd); } else { - DUFF_ROUTINE_READ(5); + DUFF_ROUTINE_READ(4); if (rc > 0) { char esc[sizeof(seq) * 4]; size_t m = linenoiseEscape(esc, seq, rc); @@ -2031,6 +2068,28 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, continue; } + // handle enter key + if (seq[0] == '\r') { + char *p; + l->final = 1; + free(history[--historylen]); + history[historylen] = 0; + linenoiseEditEnd(l); + linenoiseRefreshLineForce(l); + p = strdup(l->buf); + linenoiseReset(l); + if (p) { + *obuf = p; + l->state = DUFF_ROUTINE_START; + return l->len; + } else { + return -1; + } + DUFF_ROUTINE_LABEL(DUFF_ROUTINE_START); + linenoiseHistoryAdd(""); + continue; + } + // handle keystrokes that don't need read() switch (seq[0]) { CASE(CTRL('P'), linenoiseEditUp(l)); @@ -2070,22 +2129,6 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, } break; - case '\r': { - l->final = 1; - free(history[--historylen]); - history[historylen] = 0; - linenoiseEditEnd(l); - linenoiseRefreshLineForce(l); - char *p = strdup(l->buf); - linenoiseReset(l); - if (p) { - *obuf = p; - return l->len; - } else { - return -1; - } - } - case '\e': // handle ansi escape if (rc < 2) break; @@ -2414,15 +2457,17 @@ static int linenoiseFallback(const char *prompt, char **res) { * @return chomped allocated string of read line or null on eof/error */ char *linenoise(const char *prompt) { - bool rm; char *res; + bool rm, rs; if (linenoiseFallback(prompt, &res)) return res; fflush(stdout); fflush(stdout); rm = __replmode; + rs = __replstderr; __replmode = true; + if (isatty(2)) __replstderr = true; res = linenoiseRaw(prompt, fileno(stdin), fileno(stdout)); - __replmode = false; + __replstderr = rs; __replmode = rm; return res; } diff --git a/third_party/lua/README.cosmo b/third_party/lua/README.cosmo index 9cdfdeee5..4ec2c7a03 100644 --- a/third_party/lua/README.cosmo +++ b/third_party/lua/README.cosmo @@ -24,9 +24,13 @@ LOCAL MODIFICATIONS Integer literals such as `033` will now be interpreted as octal. + Integer literals such as `0b10` will now be interpreted as binary. + The `\e` string literal escape sequence has been added, which is equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC character. It may be used for teletypewriter control like having bold text, which can be encoded elegantly as `\e[1mHELLO\e[0m`. Added luaL_traceback2() for function parameters in traceback. + + Added Python-like printf modulus operator for strings. diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h index d8df45356..b717418c6 100644 --- a/third_party/lua/cosmo.h +++ b/third_party/lua/cosmo.h @@ -8,15 +8,16 @@ 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 **, char *, int); +int LuaEncodeLuaData(lua_State *, char **, char *, int); int LuaEncodeUrl(lua_State *); int LuaParseUrl(lua_State *); -void EscapeLuaString(char *, size_t, char **); -void LuaPushUrlParams(lua_State *, struct UrlParams *); -int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *); -void LuaPushLatin1(lua_State *, const char *, size_t); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); +int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *); +void EscapeLuaString(char *, size_t, char **); +void LuaPrintStack(lua_State *); +void LuaPushLatin1(lua_State *, const char *, size_t); +void LuaPushUrlParams(lua_State *, struct UrlParams *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ 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/lapi.c b/third_party/lua/lapi.c index ae3212cb9..fdda5f93d 100644 --- a/third_party/lua/lapi.c +++ b/third_party/lua/lapi.c @@ -1,12 +1,32 @@ -/* -** $Id: lapi.c $ -** Lua API -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lapi_c #define LUA_CORE - #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -22,8 +42,13 @@ #include "third_party/lua/lua.h" #include "third_party/lua/lundump.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ const char lua_ident[] = "$LuaVersion: " LUA_COPYRIGHT " $" @@ -479,26 +504,51 @@ static void *touserdata (const TValue *o) { } +/** + * lua_touserdata [-0, +0, –] + * + * If the value at the given index is a full userdata, returns its + * memory-block address. If the value is a light userdata, returns its value + * (a pointer). Otherwise, returns NULL. + */ LUA_API void *lua_touserdata (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return touserdata(o); } +/** + * lua_tothread [-0, +0, –] + * + * Converts the value at the given index to a Lua thread (represented as + * lua_State*). This value must be a thread; otherwise, the function returns + * NULL. + */ LUA_API lua_State *lua_tothread (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } -/* -** Returns a pointer to the internal representation of an object. -** Note that ANSI C does not allow the conversion of a pointer to -** function to a 'void*', so the conversion here goes through -** a 'size_t'. (As the returned pointer is only informative, this -** conversion should not be a problem.) -*/ +/** + * lua_topointer [-0, +0, –] + * + * Converts the value at the given index to a generic C pointer (void*). The + * value can be a userdata, a table, a thread, a string, or a function; + * otherwise, lua_topointer returns NULL. Different objects will give + * different pointers. There is no way to convert the pointer back to its + * original value. + * + * Typically this function is used only for hashing and debug information. + */ LUA_API const void *lua_topointer (lua_State *L, int idx) { + /* + ** Returns a pointer to the internal representation of an object. + ** Note that ANSI C does not allow the conversion of a pointer to + ** function to a 'void*', so the conversion here goes through + ** a 'size_t'. (As the returned pointer is only informative, this + ** conversion should not be a problem.) + */ const TValue *o = index2value(L, idx); switch (ttypetag(o)) { case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o))); @@ -856,6 +906,12 @@ static Table *gettable (lua_State *L, int idx) { } +/** + * lua_rawget [-1, +1, –] + * + * Similar to lua_gettable, but does a raw access (i.e., without + * metamethods). + */ LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; const TValue *val; @@ -876,6 +932,15 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { } +/** + * lua_rawgetp [-0, +1, –] + * + * Pushes onto the stack the value t[k], where t is the table at the given + * index and k is the pointer p represented as a light userdata. The access + * is raw; that is, it does not use the __index metavalue. + * + * Returns the type of the pushed value. + */ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { Table *t; TValue k; @@ -886,6 +951,17 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { } +/** + * lua_createtable [-0, +1, m] + * + * Creates a new empty table and pushes it onto the stack. Parameter narr is + * a hint for how many elements the table will have as a sequence; parameter + * nrec is a hint for how many other elements the table will have. Lua may + * use these hints to preallocate memory for the new table. This + * preallocation may help performance when you know in advance how many + * elements the table will have. Otherwise you can use the function + * lua_newtable. + */ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); @@ -899,6 +975,15 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { } +/** + * lua_getmetatable [-0, +(0|1), –] + * + * int lua_getmetatable (lua_State *L, int index); + * + * If the value at the given index has a metatable, the function pushes that + * metatable onto the stack and returns 1. Otherwise, the function returns 0 + * and pushes nothing on the stack. + */ LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt; @@ -926,6 +1011,17 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) { } +/** + * lua_getiuservalue [-0, +1, –] + * + * int lua_getiuservalue (lua_State *L, int index, int n); + * + * Pushes onto the stack the n-th user value associated with the full + * userdata at the given index and returns the type of the pushed value. + * + * If the userdata does not have that value, pushes nil and returns + * LUA_TNONE. + */ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { TValue *o; int t; @@ -1091,6 +1187,15 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { } +/** + * lua_setmetatable [-1, +0, –] + * + * Pops a table or nil from the stack and sets that value as the new + * metatable for the value at the given index. (nil means no metatable.) + * + * (For historical reasons, this function returns an int, which now is always + * 1.) + */ LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; @@ -1131,6 +1236,13 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { } +/** + * lua_setiuservalue [-1, +0, –] + * + * Pops a value from the stack and sets it as the new n-th user value + * associated to the full userdata at the given index. Returns 0 if the + * userdata does not have that value. + */ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { TValue *o; int res; diff --git a/third_party/lua/lapi.h b/third_party/lua/lapi.h index 48c1c203d..0c5d784d7 100644 --- a/third_party/lua/lapi.h +++ b/third_party/lua/lapi.h @@ -1,9 +1,3 @@ -/* -** $Id: lapi.h $ -** Auxiliary functions from Lua API -** See Copyright Notice in lua.h -*/ - #ifndef lapi_h #define lapi_h diff --git a/third_party/lua/lauxlib.c b/third_party/lua/lauxlib.c index 03e7a6b52..16d4db7f3 100644 --- a/third_party/lua/lauxlib.c +++ b/third_party/lua/lauxlib.c @@ -1,19 +1,43 @@ -/* -** $Id: lauxlib.c $ -** Auxiliary functions for building Lua libraries -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lauxlib_c #define LUA_LIB - #include "libc/calls/calls.h" #include "libc/errno.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /** diff --git a/third_party/lua/lbaselib.c b/third_party/lua/lbaselib.c index 25f37e902..13d8013a9 100644 --- a/third_party/lua/lbaselib.c +++ b/third_party/lua/lbaselib.c @@ -1,19 +1,43 @@ -/* -** $Id: lbaselib.c $ -** Basic library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lbaselib_c #define LUA_LIB - #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" - /* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); + static int luaB_print (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ diff --git a/third_party/lua/lcode.c b/third_party/lua/lcode.c index b448fd185..1e54b3f6a 100644 --- a/third_party/lua/lcode.c +++ b/third_party/lua/lcode.c @@ -1,12 +1,32 @@ -/* -** $Id: lcode.c $ -** Code generator for Lua -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lcode_c #define LUA_CORE - #include "libc/fmt/conv.h" #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" @@ -22,8 +42,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* Maximum number of registers in a Lua function (must fit in 8 bits) */ #define MAXREGS 255 diff --git a/third_party/lua/lcode.h b/third_party/lua/lcode.h index 5538136da..fe7da153a 100644 --- a/third_party/lua/lcode.h +++ b/third_party/lua/lcode.h @@ -1,9 +1,3 @@ -/* -** $Id: lcode.h $ -** Code generator for Lua -** See Copyright Notice in lua.h -*/ - #ifndef lcode_h #define lcode_h diff --git a/third_party/lua/lcorolib.c b/third_party/lua/lcorolib.c index 9c34df9ba..6cb615f10 100644 --- a/third_party/lua/lcorolib.c +++ b/third_party/lua/lcorolib.c @@ -1,18 +1,43 @@ -/* -** $Id: lcorolib.c $ -** Coroutine Library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lcorolib_c #define LUA_LIB - #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ static lua_State *getco (lua_State *L) { lua_State *co = lua_tothread(L, 1); diff --git a/third_party/lua/lctype.c b/third_party/lua/lctype.c deleted file mode 100644 index 373e56bc5..000000000 --- a/third_party/lua/lctype.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -** $Id: lctype.c $ -** 'ctype' functions for Lua -** See Copyright Notice in lua.h -*/ - -#define lctype_c -#define LUA_CORE - -#include "third_party/lua/lctype.h" -#include "third_party/lua/lprefix.h" - -/* clang-format off */ - -#if !LUA_USE_CTYPE /* { */ - - - -#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ -/* consider all non-ascii codepoints to be alphabetic */ -#define NONA 0x01 -#else -#define NONA 0x00 /* default */ -#endif - - -LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { - 0x00, /* EOZ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ - 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ - 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ - 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, - 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ - 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ - NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, - NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -#endif /* } */ diff --git a/third_party/lua/lctype.h b/third_party/lua/lctype.h index 472a5fb73..1204fe0d3 100644 --- a/third_party/lua/lctype.h +++ b/third_party/lua/lctype.h @@ -1,100 +1,60 @@ -/* -** $Id: lctype.h $ -** 'ctype' functions for Lua -** See Copyright Notice in lua.h -*/ - #ifndef lctype_h #define lctype_h - #include "third_party/lua/lua.h" -/* clang-format off */ - -/* -** WARNING: the functions defined here do not necessarily correspond -** to the similar functions in the standard C ctype.h. They are -** optimized for the specific needs of Lua. -*/ - -#if !defined(LUA_USE_CTYPE) - -#if 'A' == 65 && '0' == 48 -/* ASCII case: can use its own tables; faster and fixed */ -#define LUA_USE_CTYPE 0 -#else -/* must use standard C ctype */ -#define LUA_USE_CTYPE 1 -#endif - -#endif - - -#if !LUA_USE_CTYPE /* { */ - - -#include "third_party/lua/llimits.h" - - -#define ALPHABIT 0 -#define DIGITBIT 1 -#define PRINTBIT 2 -#define SPACEBIT 3 -#define XDIGITBIT 4 - - -#define MASK(B) (1 << (B)) - - -/* -** add 1 to char to allow index -1 (EOZ) -*/ -#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) - -/* -** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' -*/ -#define lislalpha(c) testprop(c, MASK(ALPHABIT)) -#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) -#define lisdigit(c) testprop(c, MASK(DIGITBIT)) -#define lisspace(c) testprop(c, MASK(SPACEBIT)) -#define lisprint(c) testprop(c, MASK(PRINTBIT)) -#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) - - /* ** In ASCII, this 'ltolower' is correct for alphabetic characters and ** for '.'. That is enough for Lua needs. ('check_exp' ensures that ** the character either is an upper-case letter or is unchanged by ** the transformation, which holds for lower-case letters and '.'.) */ -#define ltolower(c) \ - check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ +#define ltolower(c) \ + check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ (c) | ('A' ^ 'a')) +#define lisdigit(C) \ + ({ \ + unsigned char c_ = (C); \ + '0' <= c_&& c_ <= '9'; \ + }) -/* one entry for each character and for -1 (EOZ) */ -LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) +#define lislalpha(C) \ + ({ \ + unsigned char c_ = (C); \ + ('A' <= c_ && c_ <= 'Z') || ('a' <= c_ && c_ <= 'z') || c_ == '_'; \ + }) +#define lislalnum(C) \ + ({ \ + unsigned char c_ = (C); \ + (('0' <= c_ && c_ <= '9') || ('A' <= c_ && c_ <= 'Z') || \ + ('a' <= c_ && c_ <= 'z') || c_ == '_'); \ + }) -#else /* }{ */ +#define lisspace(C) \ + ({ \ + unsigned char c_ = (C); \ + (c_ == ' ' || c_ == '\t' || c_ == '\r' || c_ == '\n' || c_ == '\f' || \ + c_ == '\v'); \ + }) -/* -** use standard C ctypes -*/ +#define lisxdigit(C) \ + ({ \ + unsigned char c_ = (C); \ + (('0' <= c_ && c_ <= '9') || ('A' <= c_ && c_ <= 'F') || \ + ('a' <= c_ && c_ <= 'f')); \ + }) +#define lisbdigit(C) \ + ({ \ + unsigned char c_ = (C); \ + '0' <= c_&& c_ <= '1'; \ + }) - -#define lislalpha(c) (isalpha(c) || (c) == '_') -#define lislalnum(c) (isalnum(c) || (c) == '_') -#define lisdigit(c) (isdigit(c)) -#define lisspace(c) (isspace(c)) -#define lisprint(c) (isprint(c)) -#define lisxdigit(c) (isxdigit(c)) - -#define ltolower(c) (tolower(c)) - -#endif /* } */ +#define lisprint(C) \ + ({ \ + unsigned char c_ = (C); \ + 32 <= c_&& c_ <= 126; \ + }) #endif - diff --git a/third_party/lua/ldblib.c b/third_party/lua/ldblib.c index ed9c48d31..8cdde9f90 100644 --- a/third_party/lua/ldblib.c +++ b/third_party/lua/ldblib.c @@ -1,20 +1,42 @@ -/* -** $Id: ldblib.c $ -** Interface from Lua to its debug API -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldblib_c #define LUA_LIB - -#include "third_party/lua/lprefix.h" - - - -#include "third_party/lua/lua.h" - #include "third_party/lua/lauxlib.h" +#include "third_party/lua/lprefix.h" +#include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /* @@ -477,4 +499,3 @@ LUAMOD_API int luaopen_debug (lua_State *L) { luaL_newlib(L, dblib); return 1; } - diff --git a/third_party/lua/ldebug.c b/third_party/lua/ldebug.c index 1f2c0785c..7d710db75 100644 --- a/third_party/lua/ldebug.c +++ b/third_party/lua/ldebug.c @@ -1,18 +1,32 @@ -/* -** $Id: ldebug.c $ -** Debug Interface -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldebug_c #define LUA_CORE - -#include "third_party/lua/lprefix.h" - - - -#include "third_party/lua/lua.h" - #include "third_party/lua/lapi.h" #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" @@ -20,12 +34,19 @@ #include "third_party/lua/lfunc.h" #include "third_party/lua/lobject.h" #include "third_party/lua/lopcodes.h" +#include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" #include "third_party/lua/ltm.h" +#include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) diff --git a/third_party/lua/ldebug.h b/third_party/lua/ldebug.h index e6a1a17ed..fb87123e7 100644 --- a/third_party/lua/ldebug.h +++ b/third_party/lua/ldebug.h @@ -1,9 +1,3 @@ -/* -** $Id: ldebug.h $ -** Auxiliary functions from Debug Interface module -** See Copyright Notice in lua.h -*/ - #ifndef ldebug_h #define ldebug_h diff --git a/third_party/lua/ldo.c b/third_party/lua/ldo.c index 0ebffb139..b9a1670cb 100644 --- a/third_party/lua/ldo.c +++ b/third_party/lua/ldo.c @@ -1,12 +1,33 @@ -/* -** $Id: ldo.c $ -** Stack and Call structure of Lua -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldo_c #define LUA_CORE - +#include "libc/log/log.h" #include "libc/runtime/gc.h" #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" @@ -26,8 +47,12 @@ #include "third_party/lua/lundump.h" #include "third_party/lua/lvm.h" #include "third_party/lua/lzio.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); #define errorstatus(s) ((s) > LUA_YIELD) @@ -124,7 +149,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { lua_unlock(L); g->panic(L); /* call panic function (last chance to jump out) */ } - abort(); + __die(); } } } diff --git a/third_party/lua/ldo.h b/third_party/lua/ldo.h index 209f7b60f..5a5bff6a8 100644 --- a/third_party/lua/ldo.h +++ b/third_party/lua/ldo.h @@ -1,9 +1,3 @@ -/* -** $Id: ldo.h $ -** Stack and Call structure of Lua -** See Copyright Notice in lua.h -*/ - #ifndef ldo_h #define ldo_h diff --git a/third_party/lua/ldump.c b/third_party/lua/ldump.c index 0fb11c043..05c2c1cab 100644 --- a/third_party/lua/ldump.c +++ b/third_party/lua/ldump.c @@ -1,19 +1,44 @@ -/* -** $Id: ldump.c $ -** save precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ldump_c #define LUA_CORE - #include "third_party/lua/lobject.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lua.h" #include "third_party/lua/lundump.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ typedef struct { lua_State *L; diff --git a/third_party/lua/lfunc.c b/third_party/lua/lfunc.c index 0b5d66d59..cedce1480 100644 --- a/third_party/lua/lfunc.c +++ b/third_party/lua/lfunc.c @@ -1,12 +1,32 @@ -/* -** $Id: lfunc.c $ -** Auxiliary functions to manipulate prototypes and closures -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lfunc_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -17,8 +37,13 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ CClosure *luaF_newCclosure (lua_State *L, int nupvals) { GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); diff --git a/third_party/lua/lfunc.h b/third_party/lua/lfunc.h index ba8839496..4f15eb98b 100644 --- a/third_party/lua/lfunc.h +++ b/third_party/lua/lfunc.h @@ -1,9 +1,3 @@ -/* -** $Id: lfunc.h $ -** Auxiliary functions to manipulate prototypes and closures -** See Copyright Notice in lua.h -*/ - #ifndef lfunc_h #define lfunc_h diff --git a/third_party/lua/lgc.c b/third_party/lua/lgc.c index da7894b72..224f15ab5 100644 --- a/third_party/lua/lgc.c +++ b/third_party/lua/lgc.c @@ -1,12 +1,32 @@ -/* -** $Id: lgc.c $ -** Garbage Collector -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lgc_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -19,8 +39,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Maximum number of elements to sweep in each single step. diff --git a/third_party/lua/lgc.h b/third_party/lua/lgc.h index 2e1b59ecb..efe88f329 100644 --- a/third_party/lua/lgc.h +++ b/third_party/lua/lgc.h @@ -1,9 +1,3 @@ -/* -** $Id: lgc.h $ -** Garbage Collector -** See Copyright Notice in lua.h -*/ - #ifndef lgc_h #define lgc_h diff --git a/third_party/lua/linit.c b/third_party/lua/linit.c index 3cca5c479..f64dd1327 100644 --- a/third_party/lua/linit.c +++ b/third_party/lua/linit.c @@ -1,12 +1,32 @@ -/* -** $Id: linit.c $ -** Initialization of libraries for lua.c and other clients -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define linit_c #define LUA_LIB - /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a @@ -22,13 +42,17 @@ ** lua_setfield(L, -2, modname); ** lua_pop(L, 1); // remove PRELOAD table */ - #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** these libs are loaded by lua.c and are readily available to any Lua diff --git a/third_party/lua/liolib.c b/third_party/lua/liolib.c index 8974e2823..92027cb1b 100644 --- a/third_party/lua/liolib.c +++ b/third_party/lua/liolib.c @@ -1,12 +1,32 @@ -/* -** $Id: liolib.c $ -** Standard I/O (and system) library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define liolib_c #define LUA_LIB - #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" @@ -16,8 +36,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Change this macro to accept other modes for 'fopen' besides diff --git a/third_party/lua/llex.c b/third_party/lua/llex.c index 535f4cfee..337adf666 100644 --- a/third_party/lua/llex.c +++ b/third_party/lua/llex.c @@ -1,12 +1,32 @@ -/* -** $Id: llex.c $ -** Lexical Analyzer -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define llex_c #define LUA_CORE - #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -20,8 +40,12 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lzio.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); #define next(ls) (ls->current = zgetc(ls->z)) diff --git a/third_party/lua/llimits.h b/third_party/lua/llimits.h index babd88ae2..03c2fd812 100644 --- a/third_party/lua/llimits.h +++ b/third_party/lua/llimits.h @@ -1,12 +1,7 @@ -/* -** $Id: llimits.h $ -** Limits, basic types, and some other 'installation-dependent' definitions -** See Copyright Notice in lua.h -*/ - #ifndef llimits_h #define llimits_h +#include "libc/limits.h" #include "libc/math.h" #include "third_party/lua/lua.h" diff --git a/third_party/lua/lmathlib.c b/third_party/lua/lmathlib.c index ac8d6070c..9e0ee7305 100644 --- a/third_party/lua/lmathlib.c +++ b/third_party/lua/lmathlib.c @@ -1,12 +1,32 @@ -/* -** $Id: lmathlib.c $ -** Standard mathematical library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lmathlib_c #define LUA_LIB - #include "libc/math.h" #include "libc/nt/struct/msg.h" #include "libc/time/time.h" @@ -14,8 +34,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ #undef PI #define PI (l_mathop(3.141592653589793238462643383279502884)) diff --git a/third_party/lua/lmem.c b/third_party/lua/lmem.c index 58a98fbe9..c83b25f86 100644 --- a/third_party/lua/lmem.c +++ b/third_party/lua/lmem.c @@ -1,12 +1,32 @@ -/* -** $Id: lmem.c $ -** Interface to Memory Manager -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lmem_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -15,8 +35,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ #if defined(EMERGENCYGCTESTS) /* diff --git a/third_party/lua/lmem.h b/third_party/lua/lmem.h index 3b4b66dd2..e3b4f4d01 100644 --- a/third_party/lua/lmem.h +++ b/third_party/lua/lmem.h @@ -1,9 +1,3 @@ -/* -** $Id: lmem.h $ -** Interface to Memory Manager -** See Copyright Notice in lua.h -*/ - #ifndef lmem_h #define lmem_h diff --git a/third_party/lua/loadlib.c b/third_party/lua/loadlib.c index ae7631f9a..10b69877c 100644 --- a/third_party/lua/loadlib.c +++ b/third_party/lua/loadlib.c @@ -1,24 +1,52 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#define loadlib_c +#define LUA_LIB +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lprefix.h" +#include "third_party/lua/lua.h" +#include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); + /* -** $Id: loadlib.c $ -** Dynamic library loader for Lua -** See Copyright Notice in lua.h -** ** This module contains an implementation of loadlib for Unix systems ** that have dlfcn, an implementation for Windows, and a stub for other ** systems. */ -#define loadlib_c -#define LUA_LIB - -#include "third_party/lua/lauxlib.h" -#include "third_party/lua/lprefix.h" -#include "third_party/lua/lua.h" -#include "third_party/lua/lualib.h" const char *g_lua_path_default = LUA_PATH_DEFAULT; -/* clang-format off */ /* ** LUA_IGMARK is a mark to ignore all before it when building the diff --git a/third_party/lua/lobject.c b/third_party/lua/lobject.c index ff67a677c..c86af6686 100644 --- a/third_party/lua/lobject.c +++ b/third_party/lua/lobject.c @@ -1,13 +1,32 @@ -/* -** $Id: lobject.c $ -** Some generic functions over Lua objects -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lobject_c #define LUA_CORE - -#include "libc/intrin/kprintf.h" #include "third_party/lua/lctype.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -18,8 +37,12 @@ #include "third_party/lua/lstring.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, @@ -261,6 +284,14 @@ static const char *l_str2int (const char *s, lua_Integer *result) { empty = 0; } } + if (s[0] == '0' && + (s[1] == 'b' || s[1] == 'B')) { /* [jart] binary */ + s += 2; /* skip '0b' */ + for (; lisbdigit(cast_uchar(*s)); s++) { + a = a * 2 + (*s - '0'); + empty = 0; + } + } else if (s[0] == '0') { /* [jart] octal is the best radix */ for (s += 1; lisdigit(cast_uchar(*s)); s++) { int d = *s - '0'; diff --git a/third_party/lua/lobject.h b/third_party/lua/lobject.h index 0514a80fe..2f784103d 100644 --- a/third_party/lua/lobject.h +++ b/third_party/lua/lobject.h @@ -1,9 +1,3 @@ -/* -** $Id: lobject.h $ -** Type definitions for Lua objects -** See Copyright Notice in lua.h -*/ - #ifndef lobject_h #define lobject_h diff --git a/third_party/lua/lopcodes.c b/third_party/lua/lopcodes.c index f9eca6ae5..9af17c352 100644 --- a/third_party/lua/lopcodes.c +++ b/third_party/lua/lopcodes.c @@ -1,16 +1,41 @@ -/* -** $Id: lopcodes.c $ -** Opcodes for Lua virtual machine -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lopcodes_c #define LUA_CORE - #include "third_party/lua/lopcodes.h" #include "third_party/lua/lprefix.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ORDER OP */ diff --git a/third_party/lua/lopcodes.h b/third_party/lua/lopcodes.h index 2c5ef28db..7ae716b85 100644 --- a/third_party/lua/lopcodes.h +++ b/third_party/lua/lopcodes.h @@ -1,9 +1,3 @@ -/* -** $Id: lopcodes.h $ -** Opcodes for Lua virtual machine -** See Copyright Notice in lua.h -*/ - #ifndef lopcodes_h #define lopcodes_h diff --git a/third_party/lua/loslib.c b/third_party/lua/loslib.c index 1373637fe..314d0308d 100644 --- a/third_party/lua/loslib.c +++ b/third_party/lua/loslib.c @@ -1,12 +1,32 @@ -/* -** $Id: loslib.c $ -** Standard Operating System library -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define loslib_c #define LUA_LIB - #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" @@ -20,8 +40,13 @@ #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** {================================================================== diff --git a/third_party/lua/lparser.c b/third_party/lua/lparser.c index 4cf90cca2..ca933328e 100644 --- a/third_party/lua/lparser.c +++ b/third_party/lua/lparser.c @@ -1,12 +1,32 @@ -/* -** $Id: lparser.c $ -** Lua Parser -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lparser_c #define LUA_CORE - #include "third_party/lua/lcode.h" #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" @@ -21,8 +41,12 @@ #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /* maximum number of local variables per function (must be smaller diff --git a/third_party/lua/lparser.h b/third_party/lua/lparser.h index f6244474c..96ad3bc31 100644 --- a/third_party/lua/lparser.h +++ b/third_party/lua/lparser.h @@ -1,9 +1,3 @@ -/* -** $Id: lparser.h $ -** Lua Parser -** See Copyright Notice in lua.h -*/ - #ifndef lparser_h #define lparser_h diff --git a/third_party/lua/lprefix.h b/third_party/lua/lprefix.h index 484f2ad6f..373580fe4 100644 --- a/third_party/lua/lprefix.h +++ b/third_party/lua/lprefix.h @@ -1,45 +1,36 @@ -/* -** $Id: lprefix.h $ -** Definitions for Lua code that must come before any other header file -** See Copyright Notice in lua.h -*/ - #ifndef lprefix_h #define lprefix_h - /* ** Allows POSIX/XSI stuff */ -#if !defined(LUA_USE_C89) /* { */ +#if !defined(LUA_USE_C89) /* { */ #if !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE 600 +#define _XOPEN_SOURCE 600 #elif _XOPEN_SOURCE == 0 -#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ #endif /* ** Allows manipulation of large files in gcc and some other compilers */ #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) -#define _LARGEFILE_SOURCE 1 -#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 #endif -#endif /* } */ - +#endif /* } */ /* ** Windows stuff */ -#if defined(_WIN32) /* { */ +#if defined(_WIN32) /* { */ #if !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ #endif -#endif /* } */ +#endif /* } */ #endif - diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 4b446c088..482b0577e 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -1,24 +1,74 @@ +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lua_c +#include "libc/alg/alg.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" -#include "libc/intrin/kprintf.h" +#include "libc/errno.h" #include "libc/intrin/nomultics.internal.h" -#include "libc/intrin/spinlock.h" #include "libc/log/check.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/sysv/consts/sa.h" -#include "libc/x/x.h" #include "third_party/linenoise/linenoise.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" +#include "third_party/lua/lrepl.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" // clang-format off +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +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; -_Alignas(64) char lualock; +linenoiseCompletionCallback *lua_repl_completions_callback; +_Alignas(64) char lua_repl_lock; struct linenoiseState *lua_repl_linenoise; static lua_State *globalL; static const char *g_progname; @@ -39,45 +89,103 @@ 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, char *s) { + char **p; + if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) { c->cvec = p; - c->len = n; + c->cvec[c->len++] = s; } } -void lua_readline_completions(const char *p, linenoiseCompletions *c) { + +void lua_readline_completions (const char *p, linenoiseCompletions *c) { + int i; + size_t n; + bool found; lua_State *L; const char *name; + char *a, *b, *s, *component; + + // start searching globals 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)); + + // traverse parent objects + // split foo.bar and foo:bar + a = p; + b = strpbrk(a, ".:"); + while (b) { + component = strndup(a, b - a); + found = false; + lua_pushnil(L); // search key + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { + name = lua_tostring(L, -2); + if (!strcmp(name, component)) { + lua_remove(L, -3); // remove table + lua_remove(L, -2); // remove key + found = true; + break; + } + } + lua_pop(L, 1); // pop value } - lua_pop(L, 1); + free(component); + if (!found) { + lua_pop(L, 1); // pop table + return; + } + a = b + 1; + b = strpbrk(a, ".:"); + } + + // search final object + if (lua_type(L, -1) == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TSTRING) { + name = lua_tolstring(L, -2, &n); + if (startswithi(name, a) && (s = malloc(a - p + n + 1))) { + memcpy(s, p, a - p); + memcpy(s + (a - p), name, n + 1); + lua_readline_addcompletion(c, s); + } + } + lua_pop(L, 1); + } + } + + lua_pop(L, 1); // pop table + + for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) { + if (startswithi(kKeywordHints[i], p)) { + if ((s = strdup(kKeywordHints[i]))) { + lua_readline_addcompletion(c, s); + } + } + } + if (lua_repl_completions_callback) { + lua_repl_completions_callback(p, c); } - lua_pop(L, 1); } -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 @@ -93,6 +201,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) @@ -128,7 +237,7 @@ static ssize_t pushline (lua_State *L, int firstline) { if (lua_repl_isterminal) { prmt = strdup(get_prompt(L, firstline)); lua_pop(L, 1); /* remove prompt */ - _spunlock(&lualock); + LUA_REPL_UNLOCK; rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); free(prmt); if (rc != -1) { @@ -138,23 +247,27 @@ static ssize_t pushline (lua_State *L, int firstline) { linenoiseHistorySave(g_historypath); } } - _spinlock(&lualock); + LUA_REPL_LOCK; } else { - _spunlock(&lualock); + LUA_REPL_UNLOCK; b = linenoiseGetLine(stdin); - _spinlock(&lualock); + LUA_REPL_LOCK; rc = b ? 1 : -1; } + if (!(rc == -1 && errno == EAGAIN)) { + write(1, "\n", 1); + } if (rc == -1 || (!rc && !b)) { return rc; } 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; } @@ -208,7 +321,7 @@ static int multiline (lua_State *L) { void lua_initrepl(lua_State *L, const char *progname) { const char *prompt; - _spinlock(&lualock); + LUA_REPL_LOCK; g_progname = progname; if ((lua_repl_isterminal = linenoiseIsTerminal())) { linenoiseSetCompletionCallback(lua_readline_completions); @@ -217,27 +330,26 @@ void lua_initrepl(lua_State *L, const char *progname) { prompt = get_prompt(L, 1); if ((g_historypath = linenoiseGetHistoryPath(progname))) { if (linenoiseHistoryLoad(g_historypath) == -1) { - kprintf("%r%s: failed to load history: %m%n", g_historypath); + fprintf(stderr, "%r%s: failed to load history: %m%n", g_historypath); free(g_historypath); g_historypath = 0; } } lua_repl_linenoise = linenoiseBegin(prompt, 0, 1); lua_pop(L, 1); /* remove prompt */ - __nomultics = 2; __replmode = true; + if (isatty(2)) __replstderr = true; } - _spunlock(&lualock); + LUA_REPL_UNLOCK; } void lua_freerepl(void) { - _spinlock(&lualock); - __nomultics = false; + LUA_REPL_LOCK; __replmode = false; linenoiseEnd(lua_repl_linenoise); free(g_historypath); - _spunlock(&lualock); + LUA_REPL_UNLOCK; } @@ -254,16 +366,16 @@ int lua_loadline (lua_State *L) { ssize_t rc; int status; lua_settop(L, 0); - _spinlock(&lualock); + LUA_REPL_LOCK; if ((rc = pushline(L, 1)) != 1) { - _spunlock(&lualock); + LUA_REPL_UNLOCK; return rc - 1; /* eof or error */ } if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ status = multiline(L); /* try as command, maybe with continuation lines */ lua_remove(L, 1); /* remove line from the stack */ lua_assert(lua_gettop(L) == 1); - _spunlock(&lualock); + LUA_REPL_UNLOCK; return status; } diff --git a/third_party/lua/lrepl.h b/third_party/lua/lrepl.h index f1d6ac11c..5de303a7a 100644 --- a/third_party/lua/lrepl.h +++ b/third_party/lua/lrepl.h @@ -1,14 +1,29 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ #define COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ +#include "libc/dce.h" +#include "libc/intrin/spinlock.h" #include "third_party/linenoise/linenoise.h" #include "third_party/lua/lauxlib.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern char lualock; +#if !defined(STATIC) && SupportsWindows() +#define LUA_REPL_LOCK _spinlock(&lua_repl_lock) +#else +#define LUA_REPL_LOCK (void)0 +#endif + +#if !defined(STATIC) && SupportsWindows() +#define LUA_REPL_UNLOCK _spunlock(&lua_repl_lock) +#else +#define LUA_REPL_UNLOCK (void)0 +#endif + +extern char lua_repl_lock; extern bool lua_repl_blocking; extern bool lua_repl_isterminal; extern struct linenoiseState *lua_repl_linenoise; +extern linenoiseCompletionCallback *lua_repl_completions_callback; void lua_freerepl(void); int lua_loadline(lua_State *); diff --git a/third_party/lua/lstate.c b/third_party/lua/lstate.c index db3871d69..100766934 100644 --- a/third_party/lua/lstate.c +++ b/third_party/lua/lstate.c @@ -1,12 +1,32 @@ -/* -** $Id: lstate.c $ -** Global State -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lstate_c #define LUA_CORE - #include "libc/time/time.h" #include "third_party/lua/lapi.h" #include "third_party/lua/ldebug.h" @@ -21,8 +41,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** thread state + extra space diff --git a/third_party/lua/lstate.h b/third_party/lua/lstate.h index e5d5096ef..b0b7cd17f 100644 --- a/third_party/lua/lstate.h +++ b/third_party/lua/lstate.h @@ -1,9 +1,3 @@ -/* -** $Id: lstate.h $ -** Global State -** See Copyright Notice in lua.h -*/ - #ifndef lstate_h #define lstate_h diff --git a/third_party/lua/lstring.c b/third_party/lua/lstring.c index 3ea66a5f8..d5cfcf764 100644 --- a/third_party/lua/lstring.c +++ b/third_party/lua/lstring.c @@ -1,12 +1,32 @@ -/* -** $Id: lstring.c $ -** String table (keeps all strings handled by Lua) -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lstring_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lmem.h" @@ -15,8 +35,13 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/lua.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Maximum size for string table. diff --git a/third_party/lua/lstring.h b/third_party/lua/lstring.h index 00d632865..d9cd11ada 100644 --- a/third_party/lua/lstring.h +++ b/third_party/lua/lstring.h @@ -1,9 +1,3 @@ -/* -** $Id: lstring.h $ -** String table (keep all strings handled by Lua) -** See Copyright Notice in lua.h -*/ - #ifndef lstring_h #define lstring_h diff --git a/third_party/lua/lstrlib.c b/third_party/lua/lstrlib.c index 0b503196b..e49444ea2 100644 --- a/third_party/lua/lstrlib.c +++ b/third_party/lua/lstrlib.c @@ -1,19 +1,44 @@ -/* -** $Id: lstrlib.c $ -** Standard library for string operations and pattern-matching -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lstrlib_c #define LUA_LIB - #include "libc/math.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off -/* clang-format off */ +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); /* @@ -41,6 +66,8 @@ +static int str_format(lua_State *); + static int str_len (lua_State *L) { size_t l; @@ -297,7 +324,21 @@ static int arith_mul (lua_State *L) { } static int arith_mod (lua_State *L) { - return arith(L, LUA_OPMOD, "__mod"); + int i, n; + if (lua_istable(L, 2)) { // [jart] python printf operator + lua_len(L, 2); + n = lua_tointeger(L, -1); + lua_pop(L, 1); + lua_pushcfunction(L, str_format); + lua_pushvalue(L, 1); + for (i = 1; i <= n; ++i) { + lua_geti(L, 2, i); + } + lua_call(L, 1 + n, 1); + return 1; + } else { + return arith(L, LUA_OPMOD, "__mod"); + } } static int arith_pow (lua_State *L) { @@ -536,7 +577,7 @@ static const char *start_capture (MatchState *ms, const char *s, static const char *end_capture (MatchState *ms, const char *s, - const char *p) { + const char *p) { int l = capture_to_close(ms); const char *res; ms->capture[l].len = s - ms->capture[l].init; /* close capture */ diff --git a/third_party/lua/ltable.c b/third_party/lua/ltable.c index 4d021a141..a5bf84153 100644 --- a/third_party/lua/ltable.c +++ b/third_party/lua/ltable.c @@ -1,12 +1,32 @@ -/* -** $Id: ltable.c $ -** Lua tables (hash) -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltable_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -18,8 +38,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Implementation of tables (aka arrays, objects, or hash tables). diff --git a/third_party/lua/ltable.h b/third_party/lua/ltable.h index f5d462601..7b4014e14 100644 --- a/third_party/lua/ltable.h +++ b/third_party/lua/ltable.h @@ -1,9 +1,3 @@ -/* -** $Id: ltable.h $ -** Lua tables (hash) -** See Copyright Notice in lua.h -*/ - #ifndef ltable_h #define ltable_h diff --git a/third_party/lua/ltablib.c b/third_party/lua/ltablib.c index 553f2a7e6..9edcc3f52 100644 --- a/third_party/lua/ltablib.c +++ b/third_party/lua/ltablib.c @@ -1,20 +1,45 @@ -/* -** $Id: ltablib.c $ -** Library for Table Manipulation -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltablib_c #define LUA_LIB - #include "libc/calls/weirdtypes.h" #include "libc/time/time.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** Operations that an object must define to mimic a table diff --git a/third_party/lua/ltests.c b/third_party/lua/ltests.c index 7658c8c40..f0cf71020 100644 --- a/third_party/lua/ltests.c +++ b/third_party/lua/ltests.c @@ -1,12 +1,32 @@ -/* -** $Id: ltests.c $ -** Internal Module for Debugging of the Lua Implementation -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltests_c #define LUA_CORE - #include "third_party/lua/lapi.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lcode.h" @@ -23,8 +43,13 @@ #include "third_party/lua/ltable.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** The whole module only makes sense with LUA_DEBUG on @@ -1699,7 +1724,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { lua_error(L1); } else if EQ("abort") { - abort(); + __die(); } else if EQ("throw") { #if defined(__cplusplus) diff --git a/third_party/lua/ltests.h b/third_party/lua/ltests.h index b8fab980a..20fe75070 100644 --- a/third_party/lua/ltests.h +++ b/third_party/lua/ltests.h @@ -1,12 +1,5 @@ -/* -** $Id: ltests.h $ -** Internal Header for Debugging of the Lua Implementation -** See Copyright Notice in lua.h -*/ - #ifndef ltests_h #define ltests_h - #include "third_party/lua/lua.h" /* clang-format off */ diff --git a/third_party/lua/ltm.c b/third_party/lua/ltm.c index 75f67c5f6..25becb214 100644 --- a/third_party/lua/ltm.c +++ b/third_party/lua/ltm.c @@ -1,12 +1,32 @@ -/* -** $Id: ltm.c $ -** Tag methods -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define ltm_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lgc.h" @@ -18,8 +38,13 @@ #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ static const char udatatypename[] = "userdata"; diff --git a/third_party/lua/ltm.h b/third_party/lua/ltm.h index aa8c3785e..0bc15fddf 100644 --- a/third_party/lua/ltm.h +++ b/third_party/lua/ltm.h @@ -1,16 +1,8 @@ -/* -** $Id: ltm.h $ -** Tag methods -** See Copyright Notice in lua.h -*/ - #ifndef ltm_h #define ltm_h - #include "third_party/lua/lobject.h" #include "third_party/lua/luaconf.h" #include "third_party/lua/tms.h" - /* clang-format off */ /* diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 876d78ec7..3ff767466 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -1,11 +1,31 @@ -/* -** $Id: lua.c $ -** Lua stand-alone interpreter -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lua_c - #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" @@ -28,11 +48,15 @@ #include "third_party/lua/lrepl.h" #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); STATIC_STACK_SIZE(0x40000); -/* clang-format off */ - #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" #endif @@ -300,7 +324,6 @@ static void doREPL (lua_State *L) { progname = oldprogname; return; } - lua_writeline(); if (status == LUA_OK) status = lua_runchunk(L, 0, LUA_MULTRET); if (status == LUA_OK) { @@ -311,7 +334,6 @@ static void doREPL (lua_State *L) { } lua_freerepl(); lua_settop(L, 0); /* clear stack */ - lua_writeline(); progname = oldprogname; } diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 2fc49188b..e63b3fe92 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -77,9 +77,17 @@ o/$(MODE)/third_party/lua/lua.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/lua/.lua/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/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/luac.main.c b/third_party/lua/luac.main.c index 7b3c23470..83b11dcbc 100644 --- a/third_party/lua/luac.main.c +++ b/third_party/lua/luac.main.c @@ -1,12 +1,32 @@ -/* -** $Id: luac.c $ -** Lua compiler (saves bytecodes to files; also lists bytecodes) -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define luac_c #define LUA_CORE - #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" @@ -22,8 +42,13 @@ #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" #include "third_party/lua/lundump.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ static void PrintFunction(const Proto* f, int full); #define luaU_print PrintFunction diff --git a/third_party/lua/luacallwithtrace.c b/third_party/lua/luacallwithtrace.c index 0e89c912c..c7640d19d 100644 --- a/third_party/lua/luacallwithtrace.c +++ b/third_party/lua/luacallwithtrace.c @@ -20,6 +20,26 @@ #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" + +// this is called with the error objects/string at -1 +// and returns the stringified error object at -1 +// @param L is main Lua interpreter +void expanderr(lua_State *L) { + if (lua_tostring(L, -1) == NULL) { // is error object not a string? + if (luaL_callmeta(L, -1, "__tostring")) { // is there a metamethod? + if (lua_type(L, -1) != LUA_TSTRING) { // returns not a string? + lua_pushfstring(L, "(error object returned a %s value)", + luaL_typename(L, -1)); + lua_remove(L, -2); // remove non-string value from __tostring + } + } else { + lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, -1)); + } + lua_remove(L, -2); // remove the error object + } +} + // calling convention for lua stack of L is: // -2 is function // -1 is is argument (assuming nargs == 1) @@ -44,19 +64,19 @@ int LuaCallWithTrace(lua_State *L, int nargs, int nres, lua_State *C) { status = lua_resume(C, L, nargs, &nresults); // remove coroutine (still) at the bottom, but only if not yielding // keep it when yielding to anchor, so it's not GC-collected - // it's going to be removed at the beggining of the request handling + // it's going to be removed at the beginning of the request handling if (!canyield) lua_remove(L, 1); if (status != LUA_OK && status != LUA_YIELD) { - // move the error message - lua_xmove(C, L, 1); + lua_xmove(C, L, 1); // move the error message + expanderr(L); // handle non-string error objects // replace the error with the traceback on failure luaL_traceback2(L, C, lua_tostring(L, -1), 0); lua_remove(L, -2); // remove the error message } else { if (!lua_checkstack(L, MAX(nresults, nres))) { - lua_pop(C, nresults); /* remove results anyway */ + lua_pop(C, nresults); // remove results anyway lua_pushliteral(L, "too many results to resume"); - return LUA_ERRRUN; /* error flag */ + return LUA_ERRRUN; // error flag } lua_xmove(C, L, nresults); // move results to the main stack // grow the stack in case returned fewer results diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c index 6cc64cd5a..3b18f0f7d 100644 --- a/third_party/lua/luaencodejsondata.c +++ b/third_party/lua/luaencodejsondata.c @@ -16,70 +16,132 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/bits/bits.h" +#include "libc/fmt/itoa.h" +#include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/stdio/append.internal.h" #include "net/http/escape.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/lua.h" +#include "third_party/lua/visitor.h" -int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen; +static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level, + char *numformat, int idx, + struct LuaVisited *visited) { + 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, i, z; + char ibuf[21], fmt[] = "%.14g"; + if (level > 0) { + switch (lua_type(L, idx)) { + + case LUA_TNIL: + appendw(buf, READ32LE("null")); + return 0; + + case LUA_TBOOLEAN: + appendw(buf, lua_toboolean(L, idx) ? READ32LE("true") + : READ64LE("false\0\0")); + return 0; + + 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_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: + free(visited->p); + luaL_error(L, "numformat string not allowed"); + unreachable; + } + 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_TTABLE: + if (LuaPushVisit(visited, lua_topointer(L, idx))) { + lua_pushvalue(L, idx); // table ref + tbllen = lua_rawlen(L, -1); + // encode tables with numeric indices and empty tables as arrays + isarray = + tbllen > 0 || // integer keys present + (lua_pushnil(L), !lua_next(L, -2)) || // no non-integer keys + (lua_pop(L, 2), false); // pop key/value pushed by lua_next + appendw(buf, isarray ? '[' : '{'); + if (isarray) { + for (i = 1; i <= tbllen; i++) { + if (i > 1) appendw(buf, ','); + lua_rawgeti(L, -1, i); // table/-2, value/-1 + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited); + lua_pop(L, 1); + } + } else { + i = 1; + lua_pushnil(L); // push the first key + while (lua_next(L, -2)) { + if (lua_type(L, -2) != LUA_TSTRING) { + free(visited->p); + luaL_error(L, "json tables must be arrays or use string keys"); + unreachable; + } + if (i++ > 1) appendw(buf, ','); + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -2, visited); + appendw(buf, ':'); + LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + } + appendw(buf, isarray ? ']' : '}'); + LuaPopVisit(visited); + lua_pop(L, 1); // table ref + return 0; + } else { + free(visited->p); + luaL_error(L, "can't serialize cyclic data structure to json"); + unreachable; + } + + default: + free(visited->p); + luaL_error(L, "won't serialize %s to json", luaL_typename(L, idx)); + 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"); + free(visited->p); + luaL_error(L, "too many nested tables"); + unreachable; } - return 0; +} + +int LuaEncodeJsonData(lua_State *L, char **buf, char *numformat, int idx) { + int rc; + struct LuaVisited visited = {0}; + rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx, &visited); + assert(!visited.n); + free(visited.p); + return rc; } diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c index fbee64e63..7e855286b 100644 --- a/third_party/lua/luaencodeluadata.c +++ b/third_party/lua/luaencodeluadata.c @@ -16,51 +16,185 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/bits/bits.h" +#include "libc/fmt/itoa.h" #include "libc/math.h" +#include "libc/mem/mem.h" #include "libc/stdio/append.internal.h" +#include "libc/x/x.h" #include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" +#include "third_party/lua/lctype.h" #include "third_party/lua/lua.h" +#include "third_party/lua/visitor.h" -int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen, slen; - 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 - } - // 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"); +static bool IsLuaIdentifier(lua_State *L, int idx) { + size_t i, n; + const char *p; + p = luaL_checklstring(L, idx, &n); + if (!lislalpha(p[0])) return false; + for (i = 1; i < n; ++i) { + if (!lislalnum(p[i])) return false; } - return 0; + return true; +} + +// TODO: Can we be smarter with lua_rawlen? +static bool IsLuaArray(lua_State *L) { + int i; + lua_pushnil(L); + for (i = 1; lua_next(L, -2); ++i) { + if (!lua_isinteger(L, -2) || lua_tointeger(L, -2) != i) { + lua_pop(L, 2); + return false; + } + lua_pop(L, 1); + } + return true; +} + +static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level, + char *numformat, int idx, + struct LuaVisited *visited) { + char *s; + bool isarray; + lua_Integer i; + int ktype, vtype; + size_t tbllen, buflen, slen; + char ibuf[24], 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_topointer(L, idx)); + return 0; + + case LUA_TLIGHTUSERDATA: + appendf(buf, "\"light@%p\"", lua_topointer(L, idx)); + return 0; + + case LUA_TTHREAD: + appendf(buf, "\"thread@%p\"", lua_topointer(L, idx)); + return 0; + + case LUA_TUSERDATA: + if (luaL_callmeta(L, idx, "__repr")) { + if (lua_type(L, -1) == LUA_TSTRING) { + s = lua_tolstring(L, -1, &slen); + appendd(buf, s, slen); + } else { + appendf(buf, "[[error %s returned a %s value]]", "__repr", + luaL_typename(L, -1)); + } + lua_pop(L, 1); + return 0; + } + if (luaL_callmeta(L, idx, "__tostring")) { + if (lua_type(L, -1) == LUA_TSTRING) { + s = lua_tolstring(L, -1, &slen); + EscapeLuaString(s, slen, buf); + } else { + appendf(buf, "[[error %s returned a %s value]]", "__tostring", + luaL_typename(L, -1)); + } + lua_pop(L, 1); + return 0; + } + appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx)); + return 0; + + case LUA_TNUMBER: + if (lua_isinteger(L, idx)) { + appendd(buf, ibuf, + FormatFlex64(ibuf, luaL_checkinteger(L, idx), 2) - 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: + free(visited->p); + luaL_error(L, "numformat string not allowed"); + unreachable; + } + appendf(buf, fmt, lua_tonumber(L, idx)); + } + return 0; + + case LUA_TBOOLEAN: + appendw(buf, lua_toboolean(L, idx) ? READ32LE("true") + : READ64LE("false\0\0")); + return 0; + + case LUA_TTABLE: + i = 0; + if (LuaPushVisit(visited, lua_topointer(L, idx))) { + appendw(buf, '{'); + lua_pushvalue(L, idx); // idx becomes invalid once we change stack + isarray = IsLuaArray(L); + lua_pushnil(L); // push the first key + while (lua_next(L, -2)) { + ktype = lua_type(L, -2); + vtype = lua_type(L, -1); + if (i++ > 0) appendw(buf, ',' | ' ' << 8); + if (isarray) { + // use {v₁′,v₂′,...} for lua-normal integer keys + } else if (ktype == LUA_TSTRING && IsLuaIdentifier(L, -2)) { + // use {𝑘=𝑣′} syntax when 𝑘 is legal as a lua identifier + s = lua_tolstring(L, -2, &slen); + appendd(buf, s, slen); + appendw(buf, '='); + } else { + // use {[𝑘′]=𝑣′} otherwise + appendw(buf, '['); + LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -2, visited); + appendw(buf, ']' | '=' << 010); + } + LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -1, visited); + lua_pop(L, 1); // table/-2, key/-1 + } + lua_pop(L, 1); // table ref + LuaPopVisit(visited); + appendw(buf, '}'); + } else { + appendf(buf, "\"cyclic@%p\"", lua_topointer(L, idx)); + } + return 0; + + default: + free(visited->p); + luaL_error(L, "can't serialize value of this type"); + unreachable; + } + } else { + free(visited->p); + luaL_error(L, "too many nested tables"); + unreachable; + } +} + +int LuaEncodeLuaData(lua_State *L, char **buf, char *numformat, int idx) { + int rc; + struct LuaVisited visited = {0}; + rc = LuaEncodeLuaDataImpl(L, buf, 64, numformat, idx, &visited); + assert(!visited.n); + free(visited.p); + return rc; } diff --git a/third_party/lua/luaformatstack.c b/third_party/lua/luaformatstack.c index 936a658f2..f6b362dce 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, "g", i); } return b; } diff --git a/third_party/lua/lualib.h b/third_party/lua/lualib.h index 7fa19122d..47dfc39e3 100644 --- a/third_party/lua/lualib.h +++ b/third_party/lua/lualib.h @@ -1,12 +1,5 @@ -/* -** $Id: lualib.h $ -** Lua standard libraries -** See Copyright Notice in lua.h -*/ - #ifndef lualib_h #define lualib_h - #include "third_party/lua/lua.h" /* clang-format off */ diff --git a/third_party/lua/luaprintstack.c b/third_party/lua/luaprintstack.c new file mode 100644 index 000000000..6d286fb4b --- /dev/null +++ b/third_party/lua/luaprintstack.c @@ -0,0 +1,31 @@ +/*-*- 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/stdio/stdio.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lua.h" + +/** + * Development tool for quickly viewing the Lua stack. + */ +void LuaPrintStack(lua_State *L) { + char *s = LuaFormatStack(L); + fputs(s, stderr); + fputc('\n', stderr); + free(s); +} diff --git a/third_party/lua/lundump.h b/third_party/lua/lundump.h index ee0dbe0ab..84fa58ab3 100644 --- a/third_party/lua/lundump.h +++ b/third_party/lua/lundump.h @@ -1,12 +1,5 @@ -/* -** $Id: lundump.h $ -** load precompiled Lua chunks -** See Copyright Notice in lua.h -*/ - #ifndef lundump_h #define lundump_h - #include "third_party/lua/llimits.h" #include "third_party/lua/lobject.h" #include "third_party/lua/lzio.h" diff --git a/third_party/lua/lvm.c b/third_party/lua/lvm.c index 7780b974a..f3bf1e711 100644 --- a/third_party/lua/lvm.c +++ b/third_party/lua/lvm.c @@ -1,12 +1,32 @@ -/* -** $Id: lvm.c $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lvm_c #define LUA_CORE - #include "third_party/lua/ldebug.h" #include "third_party/lua/ldo.h" #include "third_party/lua/lfunc.h" @@ -20,8 +40,13 @@ #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ /* ** By default, use jump tables in the main interpreter loop on gcc diff --git a/third_party/lua/lvm.h b/third_party/lua/lvm.h index 8fd25ed22..9abf879ce 100644 --- a/third_party/lua/lvm.h +++ b/third_party/lua/lvm.h @@ -1,12 +1,5 @@ -/* -** $Id: lvm.h $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - #ifndef lvm_h #define lvm_h - #include "third_party/lua/ldo.h" #include "third_party/lua/lobject.h" #include "third_party/lua/ltm.h" diff --git a/third_party/lua/lzio.c b/third_party/lua/lzio.c index 622bf60b1..4857b0957 100644 --- a/third_party/lua/lzio.c +++ b/third_party/lua/lzio.c @@ -1,20 +1,45 @@ -/* -** $Id: lzio.c $ -** Buffered streams -** See Copyright Notice in lua.h -*/ - +/*-*- 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│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Lua │ +│ Copyright © 2004-2021 Lua.org, PUC-Rio. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ #define lzio_c #define LUA_CORE - #include "third_party/lua/llimits.h" #include "third_party/lua/lmem.h" #include "third_party/lua/lprefix.h" #include "third_party/lua/lstate.h" #include "third_party/lua/lua.h" #include "third_party/lua/lzio.h" +// clang-format off + +asm(".ident\t\"\\n\\n\ +Lua 5.4.3 (MIT License)\\n\ +Copyright 1994–2021 Lua.org, PUC-Rio.\""); +asm(".include \"libc/disclaimer.inc\""); -/* clang-format off */ int luaZ_fill (ZIO *z) { size_t size; diff --git a/third_party/lua/visitor.c b/third_party/lua/visitor.c new file mode 100644 index 000000000..8d0b28386 --- /dev/null +++ b/third_party/lua/visitor.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/assert.h" +#include "libc/x/x.h" +#include "third_party/lua/visitor.h" + +bool LuaPushVisit(struct LuaVisited *visited, const void *p) { + int i; + for (i = 0; i < visited->n; ++i) { + if (visited->p[i] == p) { + return false; + } + } + visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p)); + visited->p[visited->n - 1] = p; + return true; +} + +void LuaPopVisit(struct LuaVisited *visited) { + assert(visited->n > 0); + --visited->n; +} diff --git a/third_party/lua/visitor.h b/third_party/lua/visitor.h new file mode 100644 index 000000000..dcf150e22 --- /dev/null +++ b/third_party/lua/visitor.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ +#define COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct LuaVisited { + int n; + const void **p; +}; + +bool LuaPushVisit(struct LuaVisited *, const void *); +void LuaPopVisit(struct LuaVisited *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ */ diff --git a/third_party/lz4cli/lz4cli.mk b/third_party/lz4cli/lz4cli.mk index 48116f25e..568db66c6 100644 --- a/third_party/lz4cli/lz4cli.mk +++ b/third_party/lz4cli/lz4cli.mk @@ -41,6 +41,7 @@ o/$(MODE)/third_party/lz4cli/datagen.o: \ THIRD_PARTY_LZ4CLI_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_STDIO \ + LIBC_LOG \ LIBC_TIME \ LIBC_UNICODE diff --git a/third_party/lz4cli/platform.h b/third_party/lz4cli/platform.h index 00701a22e..6da8d6fe6 100644 --- a/third_party/lz4cli/platform.h +++ b/third_party/lz4cli/platform.h @@ -107,14 +107,10 @@ extern "C" { * Detect if isatty() and fileno() are available ************************************************/ #if (defined(__linux__) && (PLATFORM_POSIX_VERSION >= 1)) || (PLATFORM_POSIX_VERSION >= 200112L) || defined(__DJGPP__) -# include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) #elif defined(MSDOS) || defined(OS2) || defined(__CYGWIN__) -# include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) #elif defined(WIN32) || defined(_WIN32) -# include /* _isatty */ -# include /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */ #include "libc/stdio/stdio.h" /* FILE */ static __inline int IS_CONSOLE(FILE* stdStream) { @@ -130,11 +126,7 @@ static __inline int IS_CONSOLE(FILE* stdStream) * OS-specific Includes ******************************/ #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) -# include /* _O_BINARY */ -# include /* _setmode, _fileno, _get_osfhandle */ # if !defined(__DJGPP__) -# include /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */ -# include /* FSCTL_SET_SPARSE */ # define SET_BINARY_MODE(file) { int unused=_setmode(_fileno(file), _O_BINARY); (void)unused; } # define SET_SPARSE_FILE_MODE(file) { DWORD dw; DeviceIoControl((HANDLE) _get_osfhandle(_fileno(file)), FSCTL_SET_SPARSE, 0, 0, 0, 0, &dw, 0); } # else diff --git a/third_party/make/make.mk b/third_party/make/make.mk index b759ab637..fd472595e 100644 --- a/third_party/make/make.mk +++ b/third_party/make/make.mk @@ -131,7 +131,7 @@ o/$(MODE)/third_party/make/make.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/make/.make/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/make/.make/.symtab $(THIRD_PARTY_MAKE_OBJS): \ diff --git a/third_party/mbedtls/test/lib.c b/third_party/mbedtls/test/lib.c index a95392471..2d6532367 100644 --- a/third_party/mbedtls/test/lib.c +++ b/third_party/mbedtls/test/lib.c @@ -1011,7 +1011,7 @@ int execute_tests(int argc, const char **argv, const char *default_filename) { file = fopen(test_filename, "r"); if (file == NULL) { WRITE("%s (%s) failed to open test file: %s %m\n", - program_invocation_short_name, program_executable_name, + program_invocation_short_name, GetProgramExecutableName(), test_filename); if (outcome_file != NULL) fclose(outcome_file); return 1; diff --git a/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py b/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py index a105b4db2..d86335869 100644 --- a/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py +++ b/third_party/python/Lib/_sysconfigdata_m_cosmo_x86_64_cosmo.py @@ -787,7 +787,7 @@ build_time_vars = {'ABIFLAGS': 'm', 'UNICODE_DEPS': '\\', 'UNIVERSALSDK': '', 'UPDATE_FILE': 'python3 ./Tools/scripts/update_file.py', - 'USE_COMPUTED_GOTOS': 0, + 'USE_COMPUTED_GOTOS': 1, 'USE_INLINE': 1, 'VERSION': '3.6', 'WANT_SIGFPE_HANDLER': 0, diff --git a/third_party/python/Modules/getpath.c b/third_party/python/Modules/getpath.c index d45728be0..59178a01c 100644 --- a/third_party/python/Modules/getpath.c +++ b/third_party/python/Modules/getpath.c @@ -663,7 +663,7 @@ Py_GetProgramFullPath(void) { static bool once; if (_cmpxchg(&once, false, true)) { - progpath = utf8toutf32(program_executable_name, -1, 0); + progpath = utf8toutf32(GetProgramExecutableName(), -1, 0); __cxa_atexit(free, progpath, 0); } return progpath; diff --git a/third_party/python/Modules/termios.c b/third_party/python/Modules/termios.c index 63814c1b1..31a9a09ba 100644 --- a/third_party/python/Modules/termios.c +++ b/third_party/python/Modules/termios.c @@ -603,7 +603,7 @@ PyInit_termios(void) if (IXANY) PyModule_AddIntConstant(m, "IXANY", IXANY); if (IXOFF) PyModule_AddIntConstant(m, "IXOFF", IXOFF); if (IMAXBEL) PyModule_AddIntConstant(m, "IMAXBEL", IMAXBEL); - if (OPOST) PyModule_AddIntConstant(m, "OPOST", OPOST); + PyModule_AddIntConstant(m, "OPOST", OPOST); if (OLCUC) PyModule_AddIntConstant(m, "OLCUC", OLCUC); if (ONLCR) PyModule_AddIntConstant(m, "ONLCR", ONLCR); if (OCRNL) PyModule_AddIntConstant(m, "OCRNL", OCRNL); diff --git a/third_party/python/pyconfig.h b/third_party/python/pyconfig.h index 33d6a177a..c3ff787f0 100644 --- a/third_party/python/pyconfig.h +++ b/third_party/python/pyconfig.h @@ -38,7 +38,7 @@ #define HAVE_WORKING_TZSET 1 #define HAVE_STRUCT_TM_TM_ZONE 1 #define HAVE_TM_ZONE 1 /* deprecated */ -/* #undef HAVE_DECL_TZNAME */ +#define HAVE_DECL_TZNAME 1 /* #undef HAVE_ALTZONE */ /* #undef GETTIMEOFDAY_NO_TZ */ @@ -141,6 +141,7 @@ #define HAVE_FSTATAT 1 #define HAVE_FSYNC 1 #define HAVE_GETENTROPY 1 +#define HAVE_GETLOADAVG 1 /* #undef HAVE_FEXECVE */ /* #undef HAVE_FSTATVFS */ /* #undef HAVE_FTIME */ @@ -148,7 +149,6 @@ /* #define HAVE_SETGROUPS 1 */ /* #define HAVE_INITGROUPS 1 */ /* #define HAVE_GETGROUPLIST 1 */ -/* #undef HAVE_GETLOADAVG */ #define HAVE_FSEEKO 1 #define HAVE_FTELLO 1 @@ -162,8 +162,8 @@ /* #undef HAVE_GETHOSTBYNAME_R_5_ARG */ /* #undef HAVE_GETHOSTBYNAME_R_6_ARG */ -/* #undef HAVE_GETRESGID */ -/* #undef HAVE_GETRESUID */ +#define HAVE_GETRESGID 1 +#define HAVE_GETRESUID 1 /* #undef HAVE_GETSPENT */ /* #undef HAVE_GETSPNAM */ @@ -477,7 +477,7 @@ /* #undef TM_IN_SYS_TIME */ /* Define if you want to use computed gotos in ceval.c. */ -/* #define USE_COMPUTED_GOTOS 1 */ +#define USE_COMPUTED_GOTOS 1 /* Define to use the C99 inline keyword. */ #define USE_INLINE 1 diff --git a/third_party/python/pyobj.c b/third_party/python/pyobj.c index 47de11a58..0429a2485 100644 --- a/third_party/python/pyobj.c +++ b/third_party/python/pyobj.c @@ -35,6 +35,7 @@ #include "libc/sysv/consts/o.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "libc/zip.h" #include "third_party/getopt/getopt.h" #include "third_party/python/Include/abstract.h" #include "third_party/python/Include/bytesobject.h" @@ -658,16 +659,19 @@ Objectify(void) if (ispkg) { elfwriter_zip(elf, zipdir, zipdir, strlen(zipdir), pydata, 0, 040755, timestamp, timestamp, - timestamp, nocompress, image_base); + timestamp, nocompress, image_base, + kZipCdirHdrLinkableSize); } if (!binonly) { elfwriter_zip(elf, gc(xstrcat("py:", modname)), zipfile, strlen(zipfile), pydata, pysize, st.st_mode, timestamp, - timestamp, timestamp, nocompress, image_base); + timestamp, timestamp, nocompress, image_base, + kZipCdirHdrLinkableSize); } elfwriter_zip(elf, gc(xstrcat("pyc:", modname)), gc(xstrcat(zipfile, 'c')), strlen(zipfile) + 1, pycdata, pycsize, st.st_mode, timestamp, - timestamp, timestamp, nocompress, image_base); + timestamp, timestamp, nocompress, image_base, + kZipCdirHdrLinkableSize); elfwriter_align(elf, 1, 0); elfwriter_startsection(elf, ".yoink", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); diff --git a/third_party/python/python.mk b/third_party/python/python.mk index 840617bb9..3448ae3b9 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -4197,7 +4197,7 @@ o/$(MODE)/third_party/python/python.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/python/.python/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/python/.python/.symtab ################################################################################ diff --git a/third_party/python/repl.c b/third_party/python/repl.c index 45aae5174..addf235ca 100644 --- a/third_party/python/repl.c +++ b/third_party/python/repl.c @@ -87,7 +87,7 @@ CompleteModule(const char *s, const char *p, linenoiseCompletions *c) PyObject *m, *f, *g, *i, *v, *n; plen = strlen(p); for (it = PyImport_Inittab; it->name; ++it) { - if (startswith(it->name, p)) { + if (startswithi(it->name, p)) { AddCompletion(c, xasprintf("%s%s", s, it->name + plen)); } } @@ -98,7 +98,7 @@ CompleteModule(const char *s, const char *p, linenoiseCompletions *c) while ((v = PyIter_Next(i))) { if ((n = PyObject_GetAttrString(v, "name"))) { if (((name = PyUnicode_AsUTF8AndSize(n, &namelen)) && - namelen >= plen && !bcmp(name, p, plen))) { + namelen >= plen && !memcasecmp(name, p, plen))) { AddCompletion(c, xasprintf("%s%s", s, name + plen)); } Py_DECREF(n); @@ -125,7 +125,7 @@ CompleteDict(const char *b, const char *q, const char *p, for (i = 0; PyDict_Next(o, &i, &k, &v);) { if ((v != Py_None && PyUnicode_Check(k) && (s = PyUnicode_AsUTF8AndSize(k, &m)) && - m >= q - p && !bcmp(s, p, q - p))) { + m >= q - p && !memcasecmp(s, p, q - p))) { AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); } } @@ -142,7 +142,10 @@ CompleteDir(const char *b, const char *q, const char *p, if ((i = PyObject_GetIter(d))) { while ((k = PyIter_Next(i))) { if (((s = PyUnicode_AsUTF8AndSize(k, &m)) && - m >= q - p && !bcmp(s, p, q - p))) { + m >= q - p && !memcasecmp(s, p, q - p) && + !(q - p == 0 && m > 4 && + (s[0+0] == '_' && s[0+1] == '_' && + s[m-1] == '_' && s[m-2] == '_')))) { AddCompletion(c, xasprintf("%.*s%.*s", p - b, b, m, s)); } Py_DECREF(k); diff --git a/third_party/quickjs/quickjs.mk b/third_party/quickjs/quickjs.mk index 3fa14c00a..4749a6bc8 100644 --- a/third_party/quickjs/quickjs.mk +++ b/third_party/quickjs/quickjs.mk @@ -154,7 +154,7 @@ o/$(MODE)/third_party/quickjs/qjs.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/quickjs/.qjs/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/quickjs/.qjs/.symtab o/$(MODE)/third_party/quickjs/qjsc.com.dbg: \ diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index 4094baec5..81b204bf0 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -83,7 +83,7 @@ o/$(MODE)/third_party/sqlite3/sqlite3.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/third_party/sqlite3/.sqlite3/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/third_party/sqlite3/.sqlite3/.symtab $(THIRD_PARTY_SQLITE3_A): \ diff --git a/third_party/zip/unix.c b/third_party/zip/unix.c index 7bf51a783..7bc237579 100644 --- a/third_party/zip/unix.c +++ b/third_party/zip/unix.c @@ -10,28 +10,16 @@ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html */ #include "libc/calls/struct/dirent.h" +#include "libc/calls/calls.h" +#include "libc/sysv/consts/s.h" #include "third_party/zip/zip.h" - -#ifndef UTIL /* the companion #endif is a bit of ways down ... */ - #include "libc/time/time.h" -#include "libc/sysv/consts/_posix.h" #include "libc/calls/struct/stat.macros.h" - -#if defined(MINIX) || defined(__mpexl) -# ifdef S_IWRITE -# undef S_IWRITE -# endif /* S_IWRITE */ -# define S_IWRITE S_IWUSR -#endif /* MINIX */ - -#if (!defined(S_IWRITE) && defined(S_IWUSR)) -# define S_IWRITE S_IWUSR -#endif - #include "libc/calls/calls.h" #include "libc/sysv/consts/dt.h" +#ifndef UTIL /* the companion #endif is a bit of ways down ... */ + #define PAD 0 #define PATH_END '/' diff --git a/third_party/zip/zip.c b/third_party/zip/zip.c index 10a74456a..b7624f230 100644 --- a/third_party/zip/zip.c +++ b/third_party/zip/zip.c @@ -34,6 +34,7 @@ #include "libc/log/log.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" +#include "libc/runtime/runtime.h" #include "third_party/bzip2/bzlib.h" #define MAXCOM 256 /* Maximum one-line comment size */ @@ -646,13 +647,6 @@ local void help() } } -static const char *GetPagerPath(char path[PATH_MAX]) { - const char *s; - if ((s = commandv("less", path))) return s; - if ((s = commandv("more", path))) return s; - return 0; -} - #ifdef VMSCLI void help_extended() #else @@ -660,10 +654,7 @@ local void help_extended() #endif /* Print extended help to stdout. */ { - extent i; /* counter for help array */ - - /* help array */ - const char *text = "\n\ + __paginate(1, "\n\ Extended Help for Zip\n\ \n\ See the Zip Manual for more detailed help\n\ @@ -986,27 +977,7 @@ More option highlights (see manual for additional options and details):\n\ -so show all available options on this system\n\ -X default=strip old extra fields, -X- keep old, -X strip most\n\ -ws wildcards don't span directory boundaries in paths\n\ -"; - - int pip[2]; - char *args[2] = {0}; - char pathbuf[PATH_MAX]; - if (isatty(0) && isatty(1) && (args[0] = GetPagerPath(pathbuf))) { - sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0); - close(0); - pipe(pip); - if (!fork()) { - close(pip[1]); - execv(args[0], args); - _Exit(127); - } - close(0); - write(pip[1], text, strlen(text)); - close(pip[1]); - wait(0); - } else { - fputs(text, stdout); - } +"); exit(0); } 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..9df4dace4 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 \ @@ -96,7 +97,7 @@ o/$(MODE)/tool/build/blinkenlights.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/build/.blinkenlights/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/build/.blinkenlights/.symtab o/$(MODE)/tool/build/ar.com.dbg: \ diff --git a/tool/build/compile.c b/tool/build/compile.c index 747a57b9d..4088fc917 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -811,7 +811,7 @@ int main(int argc, char *argv[]) { cmd = argv[optind]; if (!strchr(cmd, '/')) { - if (!(cmd = commandv(cmd, ccpath))) exit(127); + if (!(cmd = commandv(cmd, ccpath, sizeof(ccpath)))) exit(127); } s = basename(strdup(cmd)); diff --git a/tool/build/lib/demangle.c b/tool/build/lib/demangle.c index 5206651e3..257178598 100644 --- a/tool/build/lib/demangle.c +++ b/tool/build/lib/demangle.c @@ -44,7 +44,7 @@ void SpawnCxxFilt(void) { const char *cxxfilt; char path[PATH_MAX]; cxxfilt = firstnonnull(emptytonull(getenv("CXXFILT")), "c++filt"); - if (commandv(cxxfilt, path)) { + if (commandv(cxxfilt, path, sizeof(path))) { pipe2(pipefds[0], O_CLOEXEC); pipe2(pipefds[1], O_CLOEXEC); if (!(g_cxxfilt.pid = vfork())) { diff --git a/tool/build/lib/elfwriter.h b/tool/build/lib/elfwriter.h index 8a0d4583f..62b3277c1 100644 --- a/tool/build/lib/elfwriter.h +++ b/tool/build/lib/elfwriter.h @@ -73,7 +73,7 @@ void elfwriter_yoink(struct ElfWriter *, const char *, int); void elfwriter_setsection(struct ElfWriter *, struct ElfWriterSymRef, uint16_t); void elfwriter_zip(struct ElfWriter *, const char *, const char *, size_t, const void *, size_t, uint32_t, struct timespec, - struct timespec, struct timespec, bool, uint64_t); + struct timespec, struct timespec, bool, uint64_t, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/elfwriter_zip.c b/tool/build/lib/elfwriter_zip.c index 3501bb0d1..79e2e7971 100644 --- a/tool/build/lib/elfwriter_zip.c +++ b/tool/build/lib/elfwriter_zip.c @@ -136,7 +136,8 @@ static void EmitZipCdirHdr(unsigned char *p, const void *name, size_t namesize, void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name, size_t namesize, const void *data, size_t size, uint32_t mode, struct timespec mtim, struct timespec atim, - struct timespec ctim, bool nocompress, uint64_t imagebase) { + struct timespec ctim, bool nocompress, uint64_t imagebase, + size_t kZipCdirHdrLinkableSizeBootstrap) { z_stream zs; uint8_t era; uint32_t crc; @@ -159,7 +160,7 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name, if (S_ISREG(mode) && _istext(data, size)) { iattrs |= kZipIattrText; } - commentsize = kZipCdirHdrLinkableSize - (CFILE_HDR_SIZE + namesize); + commentsize = kZipCdirHdrLinkableSizeBootstrap - (CFILE_HDR_SIZE + namesize); dosmode = !(mode & 0200) ? kNtFileAttributeReadonly : 0; method = ShouldCompress(name, namesize, data, size, nocompress) ? kZipCompressionDeflate @@ -209,15 +210,15 @@ void elfwriter_zip(struct ElfWriter *elf, const char *symbol, const char *name, elfwriter_startsection(elf, gc(xasprintf("%s%s", ZIP_DIRECTORY_SECTION, name)), SHT_PROGBITS, SHF_ALLOC); - EmitZipCdirHdr((cfile = elfwriter_reserve(elf, kZipCdirHdrLinkableSize)), - name, namesize, crc, era, gflags, method, mtime, mdate, iattrs, - dosmode, mode, compsize, uncompsize, commentsize, mtim, atim, - ctim); + EmitZipCdirHdr( + (cfile = elfwriter_reserve(elf, kZipCdirHdrLinkableSizeBootstrap)), name, + namesize, crc, era, gflags, method, mtime, mdate, iattrs, dosmode, mode, + compsize, uncompsize, commentsize, mtim, atim, ctim); elfwriter_appendsym(elf, gc(xasprintf("%s%s", "zip+cdir:", name)), ELF64_ST_INFO(STB_LOCAL, STT_OBJECT), STV_DEFAULT, 0, - kZipCdirHdrLinkableSize); + kZipCdirHdrLinkableSizeBootstrap); elfwriter_appendrela(elf, kZipCfileOffsetOffset, lfilesym, R_X86_64_32, -imagebase); - elfwriter_commit(elf, kZipCdirHdrLinkableSize); + elfwriter_commit(elf, kZipCdirHdrLinkableSizeBootstrap); elfwriter_finishsection(elf); } diff --git a/tool/build/lib/ssemov.c b/tool/build/lib/ssemov.c index cff58591e..182e64fa3 100644 --- a/tool/build/lib/ssemov.c +++ b/tool/build/lib/ssemov.c @@ -121,11 +121,11 @@ static void MovqQqPq(struct Machine *m, uint32_t rde) { static void MovqVdqEqp(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), GetModrmRegisterWordPointerRead8(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } static void MovdVdqEd(struct Machine *m, uint32_t rde) { - memset(XmmRexrReg(m, rde), 0, 16); + bzero(XmmRexrReg(m, rde), 16); memcpy(XmmRexrReg(m, rde), GetModrmRegisterWordPointerRead4(m, rde), 4); } @@ -135,7 +135,7 @@ static void MovqPqEqp(struct Machine *m, uint32_t rde) { static void MovdPqEd(struct Machine *m, uint32_t rde) { memcpy(MmReg(m, rde), GetModrmRegisterWordPointerRead4(m, rde), 4); - memset(MmReg(m, rde) + 4, 0, 4); + bzero(MmReg(m, rde) + 4, 4); } static void MovdEdVdq(struct Machine *m, uint32_t rde) { @@ -168,7 +168,7 @@ static void MovntqMqPq(struct Machine *m, uint32_t rde) { static void MovqVqWq(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), GetModrmRegisterXmmPointerRead8(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } static void MovssVpsWps(struct Machine *m, uint32_t rde) { @@ -176,7 +176,7 @@ static void MovssVpsWps(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), XmmRexbRm(m, rde), 4); } else { memcpy(XmmRexrReg(m, rde), ComputeReserveAddressRead4(m, rde), 4); - memset(XmmRexrReg(m, rde) + 4, 0, 12); + bzero(XmmRexrReg(m, rde) + 4, 12); } } @@ -189,7 +189,7 @@ static void MovsdVpsWps(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), XmmRexbRm(m, rde), 8); } else { memcpy(XmmRexrReg(m, rde), ComputeReserveAddressRead8(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } } @@ -267,7 +267,7 @@ static void MovhpdMqVq(struct Machine *m, uint32_t rde) { static void MovqWqVq(struct Machine *m, uint32_t rde) { if (IsModrmRegister(rde)) { memcpy(XmmRexbRm(m, rde), XmmRexrReg(m, rde), 8); - memset(XmmRexbRm(m, rde) + 8, 0, 8); + bzero(XmmRexbRm(m, rde) + 8, 8); } else { memcpy(ComputeReserveAddressWrite8(m, rde), XmmRexrReg(m, rde), 8); } @@ -275,7 +275,7 @@ static void MovqWqVq(struct Machine *m, uint32_t rde) { static void Movq2dqVdqNq(struct Machine *m, uint32_t rde) { memcpy(XmmRexrReg(m, rde), MmRm(m, rde), 8); - memset(XmmRexrReg(m, rde) + 8, 0, 8); + bzero(XmmRexrReg(m, rde) + 8, 8); } static void Movdq2qPqUq(struct Machine *m, uint32_t rde) { diff --git a/tool/build/rle.c b/tool/build/rle.c index 7958262fb..5ca186e8b 100644 --- a/tool/build/rle.c +++ b/tool/build/rle.c @@ -21,6 +21,7 @@ #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/log/check.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -200,7 +201,8 @@ int Run(char **paths, size_t count) { rc = RunLengthCode(); } else { suffixlen = strlen(suffix_); - if (!IsTrustworthy() && strlen(paths[i]) + suffixlen + 1 > PATH_MAX) { + if (!IsTrustworthy() && + strlen(paths[i]) + suffixlen >= ARRAYLEN(pathbuf)) { return eoverflow(); } p = stpcpy(pathbuf, paths[i]); diff --git a/tool/build/runit.c b/tool/build/runit.c index b6a31568e..60e9e905e 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -113,10 +113,10 @@ char *g_prog; char *g_runitd; jmp_buf g_jmpbuf; uint16_t g_sshport; -char g_ssh[PATH_MAX]; char g_hostname[128]; uint16_t g_runitdport; volatile bool alarmed; +char g_ssh[PATH_MAX]; int __sys_execve(const char *, char *const[], char *const[]) hidden; @@ -505,7 +505,8 @@ int main(int argc, char *argv[]) { } CheckExists((g_runitd = argv[1])); CheckExists((g_prog = argv[2])); - CHECK_NOTNULL(commandv(firstnonnull(getenv("SSH"), "ssh"), g_ssh)); + CHECK_NOTNULL( + commandv(firstnonnull(getenv("SSH"), "ssh"), g_ssh, sizeof(g_ssh))); if (argc == 3) { /* hosts list empty */ return 0; diff --git a/tool/build/runitd.c b/tool/build/runitd.c index ae7d93228..71d4bb3c6 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -464,6 +464,7 @@ int main(int argc, char *argv[]) { /* __log_level = kLogDebug; */ GetOpts(argc, argv); for (i = 3; i < 16; ++i) close(i); + errno = 0; // poll()'ing /dev/null stdin file descriptor on xnu returns POLLNVAL?! if (IsWindows()) { CHECK_EQ(3, (g_bogusfd = open("/dev/null", O_RDONLY | O_CLOEXEC))); diff --git a/tool/build/strace.c b/tool/build/strace.c index c7a003eed..f5d59a59d 100644 --- a/tool/build/strace.c +++ b/tool/build/strace.c @@ -660,7 +660,7 @@ static void Flush(void) { static const char *GetErrnoName(int x) { const char *s; static char buf[16]; - if ((s = strerror_short(x))) return s; + if ((s = strerrno(x))) return s; FormatInt64(buf, x); return buf; } @@ -1195,6 +1195,5 @@ wontreturn void StraceMain(int argc, char *argv[]) { } int main(int argc, char *argv[]) { - __nomultics = true; StraceMain(argc, argv); } diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index cb28134e4..bdaa29025 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -34,6 +34,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "libc/zip.h" #include "third_party/getopt/getopt.h" #include "tool/build/lib/elfwriter.h" #include "tool/build/lib/stripcomponents.h" @@ -48,6 +49,7 @@ int64_t image_base_; int strip_components_; const char *path_prefix_; struct timespec timestamp; +size_t kZipCdirHdrLinkableSizeBootstrap; wontreturn void PrintUsage(int rc, FILE *f) { fprintf(f, "%s%s%s\n", "Usage: ", program_invocation_name, @@ -60,7 +62,8 @@ void GetOpts(int *argc, char ***argv) { int opt; yoink_ = "__zip_start"; image_base_ = IMAGE_BASE_VIRTUAL; - while ((opt = getopt(*argc, *argv, "?0nhBN:C:P:o:s:y:b:")) != -1) { + kZipCdirHdrLinkableSizeBootstrap = kZipCdirHdrLinkableSize; + while ((opt = getopt(*argc, *argv, "?0nhBL:N:C:P:o:s:y:b:")) != -1) { switch (opt) { case 'o': outpath_ = optarg; @@ -91,6 +94,9 @@ void GetOpts(int *argc, char ***argv) { case '0': nocompress_ = true; break; + case 'L': + kZipCdirHdrLinkableSizeBootstrap = strtoul(optarg, NULL, 0); + break; case '?': case 'h': PrintUsage(EXIT_SUCCESS, stdout); @@ -135,7 +141,8 @@ void ProcessFile(struct ElfWriter *elf, const char *path) { } } elfwriter_zip(elf, name, name, strlen(name), map, st.st_size, st.st_mode, - timestamp, timestamp, timestamp, nocompress_, image_base_); + timestamp, timestamp, timestamp, nocompress_, image_base_, + kZipCdirHdrLinkableSizeBootstrap); if (st.st_size) CHECK_NE(-1, munmap(map, st.st_size)); close(fd); } @@ -149,7 +156,8 @@ void PullEndOfCentralDirectoryIntoLinkage(struct ElfWriter *elf) { } void CheckFilenameKosher(const char *path) { - CHECK_LE(strlen(path), PATH_MAX); + CHECK_LE(kZipCfileHdrMinSize + strlen(path), + kZipCdirHdrLinkableSizeBootstrap); CHECK(!startswith(path, "/")); CHECK(!strstr(path, "..")); } diff --git a/tool/decode/dumpvdso.c b/tool/decode/dumpvdso.c new file mode 100644 index 000000000..e0f64fd46 --- /dev/null +++ b/tool/decode/dumpvdso.c @@ -0,0 +1,32 @@ +/*-*- 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/calls/calls.h" +#include "libc/elf/struct/ehdr.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/auxv.h" + +noasan int main(int argc, char *argv[]) { + int i = 0; + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); + if (isatty(1)) exit(1); + for (;;) { + write(1, ((char *)ehdr) + i++, 1); + } + return 0; +} diff --git a/tool/decode/elf.c b/tool/decode/elf.c index 4d9a5c4a3..4a6a4b6e3 100644 --- a/tool/decode/elf.c +++ b/tool/decode/elf.c @@ -27,8 +27,10 @@ #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" @@ -231,6 +233,21 @@ static void printelfsymboltable(void) { } } +static void printelfdynsymboltable(void) { + size_t i, symcount = 0; + Elf64_Sym *symtab = GetElfDynSymbolTable(elf, st->st_size, &symcount); + char *strtab = GetElfDynStringTable(elf, st->st_size); + char *shstrtab = GetElfSectionNameStringTable(elf, st->st_size); + if (symtab && strtab) { + printf("\n\n"); + printf("\t.org\t%#x\n", (intptr_t)symtab - (intptr_t)elf); + for (i = 0; i < symcount; ++i) { + printf(".Lsym%d:\n", i); + printelfsymbol(&symtab[i], strtab, shstrtab); + } + } +} + static char *getelfsymbolname(const Elf64_Ehdr *elf, size_t mapsize, const char *strtab, const char *shstrtab, const Elf64_Sym *sym) { @@ -324,12 +341,14 @@ int main(int argc, char *argv[]) { fprintf(stderr, "error: not an elf executable: %'s\n", path); exit(1); } + elf = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); startfile(); printelfehdr(); printelfsegmentheaders(); printelfsectionheaders(); printelfrelocations(); printelfsymboltable(); + printelfdynsymboltable(); munmap(elf, st->st_size); close(fd); return 0; diff --git a/tool/decode/ent.c b/tool/decode/ent.c index b6bf0e39b..856989371 100644 --- a/tool/decode/ent.c +++ b/tool/decode/ent.c @@ -151,7 +151,7 @@ int main(int argc, char *argv[]) } samp = binary ? "bit" : "byte"; - memset(ccount, 0, sizeof ccount); + bzero(ccount, sizeof ccount); /* Initialise for calculations */ diff --git a/tool/decode/lib/bitabuilder.c b/tool/decode/lib/bitabuilder.c index 874d5e797..1174607c1 100644 --- a/tool/decode/lib/bitabuilder.c +++ b/tool/decode/lib/bitabuilder.c @@ -58,7 +58,7 @@ bool bitabuilder_setbit(struct BitaBuilder *bb, size_t bit) { if (i > bb->n) { n = i + (i >> 2); if ((p2 = realloc(bb->p, n))) { - memset((char *)p2 + bb->n, 0, n - bb->n); + bzero((char *)p2 + bb->n, n - bb->n); bb->n = n; bb->p = p2; } else { diff --git a/tool/decode/lib/disassemblehex.c b/tool/decode/lib/disassemblehex.c index 8d0c5def2..4a7b0a964 100644 --- a/tool/decode/lib/disassemblehex.c +++ b/tool/decode/lib/disassemblehex.c @@ -44,7 +44,7 @@ void disassemblehex(uint8_t *data, size_t size, FILE *f) { if (i == size) break; } fprintf(f, "\t.%s\t", "byte"); - memset(glyphs, 0, sizeof(glyphs)); + bzero(glyphs, sizeof(glyphs)); } /* TODO(jart): Fix Emacs */ glyphs[col] = kCp437[ch == '"' || ch == '\\' || ch == '#' ? '.' : ch]; diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 2217aea8f..c47eb24ab 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -793,6 +793,7 @@ (message header)))) (progn + (define-key prog-mode-map (kbd "C-c C-h") 'cosmo-add-include) (define-key asm-mode-map (kbd "C-c C-h") 'cosmo-add-include) (define-key c-mode-base-map (kbd "C-c C-h") 'cosmo-add-include)) diff --git a/tool/net/demo/.init.lua b/tool/net/demo/.init.lua index acb360df4..304d0f89a 100644 --- a/tool/net/demo/.init.lua +++ b/tool/net/demo/.init.lua @@ -34,3 +34,7 @@ function OnHttpRequest() end SetHeader('Server', 'redbean!') end + +function Adder(x, y) + return x + y +end diff --git a/tool/net/demo/crashreport.lua b/tool/net/demo/crashreport.lua index 1cee29e69..3a46d1611 100644 --- a/tool/net/demo/crashreport.lua +++ b/tool/net/demo/crashreport.lua @@ -12,7 +12,7 @@ function dosomething(param1, x) SetHeader('Content-Type', 'text/plain; charset=utf-8') Write('preprae to crash... now\r\n') res = x / y - Write(string.format('42 / 0 is %d\r\n', res)) + Write('42 / 0 is %d\r\n' % {res}) end function start(param1) diff --git a/tool/net/demo/fetch.lua b/tool/net/demo/fetch.lua index 7833ab3e8..ff28742aa 100644 --- a/tool/net/demo/fetch.lua +++ b/tool/net/demo/fetch.lua @@ -2,7 +2,7 @@ local function WriteForm(url) Write('\r\n') - Write(string.format([[ + Write([[ redbean fetch demo \r\n') + +Write('

UNIX Directory Stream Demo

\r\n') +Write('\r\n') +Write('\r\n') +Write('\r\n') +Write('\r\n') + +dir = '.' +for name, kind, ino, off in assert(unix.opendir(dir)) do + Write('\r\n') + + Write('
name\r\n') +Write('type\r\n') +Write('ino\r\n') +Write('off\r\n') +Write('size\r\n') +Write('blocks\r\n') +Write('mode\r\n') +Write('uid\r\n') +Write('gid\r\n') +Write('dev\r\n') +Write('rdev\r\n') +Write('nlink\r\n') +Write('blksize\r\n') +Write('gen\r\n') +Write('flags\r\n') +Write('birthtim\r\n') +Write('mtim\r\n') +Write('atim\r\n') +Write('ctim\r\n') +Write('
') + Write(EscapeHtml(name)) + if kind == unix.DT_DIR then + Write('/') + end + Write('\r\n') + + Write('') + if kind == unix.DT_REG then Write('DT_REG') + elseif kind == unix.DT_DIR then Write('DT_DIR') + elseif kind == unix.DT_FIFO then Write('DT_FIFO') + elseif kind == unix.DT_CHR then Write('DT_CHR') + elseif kind == unix.DT_BLK then Write('DT_BLK') + elseif kind == unix.DT_LNK then Write('DT_LNK') + elseif kind == unix.DT_SOCK then Write('DT_SOCK') + else Write('DT_UNKNOWN') + end + Write('\r\n') + + Write('%d\r\n' % {ino}) + Write('%d\r\n' % {off}) + + st,err = unix.stat(dir..'/'..name, unix.AT_SYMLINK_NOFOLLOW) + if st then + + Write('%d\r\n' % {st:size()}) + Write('%d\r\n' % {st:blocks()}) + Write('%.7o\r\n' % {st:mode()}) + Write('%d\r\n' % {st:uid()}) + Write('%d\r\n' % {st:gid()}) + Write('%d\r\n' % {st:dev()}) + Write('%d,%d\r\n' % {unix.major(st:rdev()), unix.minor(st:rdev())}) + Write('%d\r\n' % {st:nlink()}) + Write('%d\r\n' % {st:blksize()}) + Write('%d\r\n' % {st:gen()}) + Write('%#x\r\n' % {st:flags()}) + + function WriteTime(unixsec,nanos) + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixsec) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d\r\n' % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) + end + + WriteTime(st:birthtim()) + WriteTime(st:mtim()) + WriteTime(st:atim()) + WriteTime(st:ctim()) + + else + Write('%s\r\n' % {err}) + end + +end +Write('
\r\n') diff --git a/tool/net/demo/unix-info.lua b/tool/net/demo/unix-info.lua index 17b2bec87..0be90483d 100644 --- a/tool/net/demo/unix-info.lua +++ b/tool/net/demo/unix-info.lua @@ -3,53 +3,58 @@ local unix = require 'unix' Write('\r\n') Write('redbean\r\n') Write('

UNIX Information Demo

\r\n') +Write('\r\n') Write('
\r\n') -Write('
getuid()\r\n') -Write(string.format('
%d\r\n', unix.getuid())) -Write('
getgid()\r\n') -Write(string.format('
%d\r\n', unix.getgid())) -Write('
getpid()\r\n') -Write(string.format('
%d\r\n', unix.getpid())) -Write('
getppid()\r\n') -Write(string.format('
%d\r\n', unix.getppid())) -Write('
getpgrp()\r\n') -Write(string.format('
%d\r\n', unix.getpgrp())) +Write('
unix.getuid()\r\n') +Write('
%d\r\n' % {unix.getuid()}) +Write('
unix.getgid()\r\n') +Write('
%d\r\n' % {unix.getgid()}) +Write('
unix.getpid()\r\n') +Write('
%d\r\n' % {unix.getpid()}) +Write('
unix.getppid()\r\n') +Write('
%d\r\n' % {unix.getppid()}) +Write('
unix.getpgrp()\r\n') +Write('
%d\r\n' % {unix.getpgrp()}) +Write('
unix.umask()\r\n') +mask = unix.umask(027) +unix.umask(mask) +Write('
%.4o\r\n' % {mask}) -Write('
getsid(0)\r\n') -sid, errno = unix.getsid(0) +Write('
unix.getsid(0)\r\n') +sid, err = unix.getsid(0) if sid then - Write(string.format('
%d\r\n', sid)) + Write('
%d\r\n' % {sid}) else - Write(string.format('
%s\r\n', EscapeHtml(unix.strerrno(errno)))) + Write('
%s\r\n' % {err}) end -Write('
gethostname()\r\n') -Write(string.format('
%s\r\n', EscapeHtml(unix.gethostname()))) -Write('
getcwd()\r\n') -Write(string.format('
%s\r\n', EscapeHtml(unix.getcwd()))) +Write('
unix.gethostname()\r\n') +Write('
%s\r\n' % {EscapeHtml(assert(unix.gethostname()))}) +Write('
unix.getcwd()\r\n') +Write('
%s\r\n' % {EscapeHtml(assert(unix.getcwd()))}) function PrintResourceLimit(name, id) - soft, hard, errno = unix.getrlimit(id) - Write(string.format('
getrlimit(%s)\r\n', name)) + soft, hard = unix.getrlimit(id) + Write('
getrlimit(%s)\r\n' % {name}) if soft then Write('
') Write('soft ') if soft == -1 then Write('∞') else - Write(string.format('%d', soft)) + Write('%d' % {soft}) end Write('
\r\n') Write('hard ') if hard == -1 then Write('∞') else - Write(string.format('%d', hard)) + Write('%d' % {hard}) end Write('\r\n') else - Write(string.format('
%s\r\n', EscapeHtml(unix.strerrno(errno)))) + Write('
%s\r\n' % {EscapeHtml(tostring(hard))}) end end PrintResourceLimit('RLIMIT_AS', unix.RLIMIT_AS) @@ -59,9 +64,9 @@ PrintResourceLimit('RLIMIT_FSIZE', unix.RLIMIT_FSIZE) PrintResourceLimit('RLIMIT_NPROC', unix.RLIMIT_NPROC) PrintResourceLimit('RLIMIT_NOFILE', unix.RLIMIT_NOFILE) -Write('
siocgifconf()\r\n') +Write('
unix.siocgifconf()\r\n') Write('
\r\n') -ifs, errno = unix.siocgifconf() +ifs, err = unix.siocgifconf() if ifs then for i = 1,#ifs do if ifs[i].netmask ~= 0 then @@ -69,12 +74,227 @@ if ifs then else cidr = 0 end - Write(string.format('%s %s/%d
\r\n', - EscapeHtml(ifs[i].name), - FormatIp(ifs[i].ip), - cidr)) + Write('%s %s/%d
\r\n' % {EscapeHtml(ifs[i].name), FormatIp(ifs[i].ip), cidr}) end else - Write(string.format('%s\r\n', EscapeHtml(unix.strerrno(errno)))) + Write('%s\r\n' % {err}) end -Write('
\r\n') + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DEBUG)\r\n') +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n') +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEPORT)\r\n') +if enabled then -- is nil on error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_KEEPALIVE)\r\n') +if enabled then -- is nil on error + Write('
%s\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NODELAY)\r\n') +if enabled then -- is nil on error + Write('
%s\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +secs, nanos = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVTIMEO)\r\n') +if secs then -- is nil on error + Write('
%d seconds + %d nanoseconds\r\n' % {secs, nanos}) +else + err = nanos + Write('
%s\r\n' % {err}) +end + +secs, nanos = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDTIMEO)\r\n') +if secs then -- is nil on error + Write('
%d seconds + %d nanoseconds\r\n' % {secs, nanos}) +else + err = nanos -- unix.Errno is always second result + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_DONTROUTE)\r\n') +if enabled then -- is nil if error + Write('
%d\r\n' % {enabled}) -- should be 0 or 1 +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDBUF)\r\n') +if bytes then -- is nil if error + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVBUF)\r\n') +if bytes then -- is nil if error + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN)\r\n') +if bytes then -- is nil if error + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_BROADCAST)\r\n') +if enabled then -- is nil if error + Write('
%d\r\n' % {enabled}) -- should be 1 or 0 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_CORK)\r\n') +if enabled then -- is nil if error + Write('
%d\r\n' % {enabled}) -- should be 1 or 0 +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_QUICKACK)\r\n') +if enabled then + Write('
%d\r\n' % {enabled}) +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_DEFER_ACCEPT)\r\n') +if enabled then + Write('
%s\r\n' % {enabled}) +else + Write('
%s\r\n' % {err}) +end + +enabled, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT)\r\n') +if err then + Write('
%s\r\n' % {err}) +else + Write('
%s\r\n' % {enabled}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_SNDLOWAT)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_RCVLOWAT)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPCNT)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_MAXSEG)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_SYNCNT)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_NOTSENT_LOWAT)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_WINDOW_CLAMP)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPIDLE)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +bytes, err = unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL) +Write('
unix.getsockopt(GetClientFd(), unix.SOL_TCP, unix.TCP_KEEPINTVL)\r\n') +if bytes then + Write('
%d\r\n' % {bytes}) +else + Write('
%s\r\n' % {err}) +end + +Write('
unix.environ()\r\n') +Write('
\r\n') +Write('
    \r\n') +env = unix.environ() +for i = 1,#env do + Write('
  • %s\r\n' % {EscapeHtml(env[i])}) +end +Write('
\r\n') diff --git a/tool/net/demo/unix-raise.lua b/tool/net/demo/unix-raise.lua new file mode 100755 index 000000000..acb36ca96 --- /dev/null +++ b/tool/net/demo/unix-raise.lua @@ -0,0 +1,11 @@ +#!/home/jart/bin/redbean -i +assert(unix.sigaction(unix.SIGUSR1, function(sig) + gotsigusr1 = true +end)) +gotsigusr1 = false +assert(unix.raise(unix.SIGUSR1)) +if gotsigusr1 then + print('hooray the signal was delivered') +else + print('oh no some other signal was handled') +end diff --git a/tool/net/demo/unix-rawsocket.lua b/tool/net/demo/unix-rawsocket.lua index f0b3d5147..f0f9aaf8f 100644 --- a/tool/net/demo/unix-rawsocket.lua +++ b/tool/net/demo/unix-rawsocket.lua @@ -1,4 +1,4 @@ -local unix = require "unix" +local unix = require 'unix' local function main() if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then @@ -32,8 +32,9 @@ local function main() -- steal client from redbean fd = GetClientFd() - rc, errno = unix.fork() + -- this function returns twice + pid, errno = unix.fork() if errno then SetStatus(400) SetHeader('Content-Type', 'text/html; charset=utf-8') @@ -44,7 +45,7 @@ local function main() Write(EncodeBase64(LoadAsset('/redbean.png'))) Write('">\r\n') Write('redbean unix demo\r\n') - Write(string.format(' %s\r\n', unix.strerrno(errno))) + Write(' %s\r\n' % {EscapeHtml(tostring(errno))}) Write('\r\n') Write([[

@@ -57,11 +58,12 @@ local function main() return end - if rc ~= 0 then + -- the parent process gets the pid + if pid ~= 0 then unix.close(fd) return end - + -- if pid is zero then we're the child -- turn into a daemon unix.umask(0) unix.setsid() @@ -103,34 +105,6 @@ local function main() unix.write(fd, 'became an autonomous daemon reparented on your init!\r\n') unix.write(fd, '

\r\n') - unix.write(fd, '

listing of current directory

\r\n') - dir, err = unix.opendir('.') - if dir then - unix.write(fd, '
    \r\n') - while true do - name, kind, ino, off = dir:read() - if not name then - break - end - unix.write(fd, '
  • ') - unix.write(fd, EscapeHtml(VisualizeControlCodes(name))) - if kind == unix.DT_DIR then - unix.write(fd, '/') - else - st, err = unix.stat(name) - if st then - unix.write(fd, string.format(' (%d bytes)', st:size())) - end - end - unix.write(fd, '\r\n') - end - unix.write(fd, '
\r\n') - else - unix.write(fd, '

\r\n') - unix.write(fd, string.format('failed: %s\r\n', EscapeHtml(VisualizeControlCodes(unix:strerror(err))))) - unix.write(fd, '

\r\n') - end - -- terminate unix.close(fd) unix.exit(0) diff --git a/tool/net/demo/unix-subprocess.lua b/tool/net/demo/unix-subprocess.lua index 13e14980a..04bf3fd28 100644 --- a/tool/net/demo/unix-subprocess.lua +++ b/tool/net/demo/unix-subprocess.lua @@ -2,57 +2,57 @@ -- and pipe its output to the http user local unix = require "unix" function main() + if GetHostOs() == 'WINDOWS' then + cmd = 'dir' + else + cmd = 'ls' + end syscall = 'commandv' - ls, errno = unix.commandv("ls") - if ls then - 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) - syscall = 'fork' - child, errno = unix.fork() - if child then - if child == 0 then - unix.close(1) - unix.dup(writer) - unix.close(writer) - unix.close(reader) - unix.sigaction(unix.SIGINT, oldint) - unix.sigaction(unix.SIGQUIT, oldquit) - unix.sigprocmask(unix.SIG_SETMASK, oldmask) - unix.execve(ls, {ls, "-Shal"}) - unix.exit(127) + ls = assert(unix.commandv(cmd)) + syscall = 'pipe' + reader, writer = assert(unix.pipe(unix.O_CLOEXEC)) + oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN)) + oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)) + oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, unix.Sigset(unix.SIGCHLD))) + syscall = 'fork' + child = assert(unix.fork()) + if child == 0 then + unix.close(0) + unix.open("/dev/null", unix.O_RDONLY) + unix.close(1) + unix.dup(writer) + unix.close(2) + unix.open("/dev/null", unix.O_RDONLY) + unix.sigaction(unix.SIGINT, oldint) + unix.sigaction(unix.SIGQUIT, oldquit) + unix.sigprocmask(unix.SIG_SETMASK, oldmask) + unix.execve(ls, {ls, '-Shal'}) + unix.exit(127) + else + unix.close(writer) + SetStatus(200) + SetHeader('Content-Type', 'text/plain') + while true do + data, err = unix.read(reader) + if data then + if data ~= '' then + Write(data) else - unix.close(writer) - SetStatus(200) - SetHeader('Content-Type', 'text/plain') - while true do - data, errno = unix.read(reader) - if data then - if data ~= "" then - Write(data) - else - break - end - elseif errno ~= unix.EINTR then - Log(kLogWarn, string.format('read() failed: %s', unix.strerror(errno))) - break - end - end - unix.close(reader) - unix.wait(-1) - unix.sigaction(unix.SIGINT, oldint) - unix.sigaction(unix.SIGQUIT, oldquit) - unix.sigprocmask(unix.SIG_SETMASK, oldmask) - return + break end + elseif err:errno() ~= unix.EINTR then + Log(kLogWarn, 'read() failed: %s' % {tostring(err)}) + break end end + unix.close(reader) + Log(kLogWarn, 'wait() begin') + unix.wait(-1) + Log(kLogWarn, 'wait() end') + unix.sigaction(unix.SIGINT, oldint) + unix.sigaction(unix.SIGQUIT, oldquit) + unix.sigprocmask(unix.SIG_SETMASK, oldmask) + return end - SetStatus(200) - SetHeader('Content-Type', 'text/plain') - Write(string.format('error %s calling %s()', unix.strerrno(errno), syscall)) end main() diff --git a/tool/net/demo/unix-webserver.lua b/tool/net/demo/unix-webserver.lua index 3b399c37c..b6837aae3 100644 --- a/tool/net/demo/unix-webserver.lua +++ b/tool/net/demo/unix-webserver.lua @@ -33,7 +33,7 @@ function main() pollfds = {} pollfds[mainfd] = unix.POLLIN - ifs, errno = unix.siocgifconf() + ifs = assert(unix.siocgifconf()) for i = 1,#ifs do if (IsLoopbackIp(mainip) and (IsPublicIp(ifs[i].ip) or IsPrivateIp(ifs[i].ip) or @@ -45,13 +45,11 @@ function main() server = unix.socket() unix.bind(server, ifs[i].ip) unix.listen(server) - ip, port = unix.getsockname(server) - addr = string.format('%s:%d', FormatIp(ip), port) - url = string.format('http://%s', addr) - Log(kLogInfo, string.format('listening on %s', addr)) - unix.write(mainfd, string.format( - 'listening on %s
\r\n', - url, url)) + ip, port = assert(unix.getsockname(server)) + addr = '%s:%d' % {FormatIp(ip), port} + url = 'http://%s' % {addr} + Log(kLogInfo, 'listening on %s' % {addr}) + unix.write(mainfd, 'listening on %s
\r\n' % {url, url}) pollfds[server] = unix.POLLIN | unix.POLLHUP servers[server] = true addrs[server] = addr @@ -67,7 +65,7 @@ function main() if fd == mainfd then data, errno = unix.read(mainfd) if not data then - Log(kLogInfo, string.format('got %s from parent client', unix.strerrno(errno))) + Log(kLogInfo, 'got %s from parent client' % {tostring(errno)}) -- prevent redbean core from writing a response unix.exit(1) end @@ -80,20 +78,19 @@ function main() -- echo it back for fun unix.write(mainfd, data) elseif servers[fd] then - unix.write(mainfd, string.format('preparing to accept from %d
\r\n', fd)) - client, clientip, clientport = unix.accept(fd) - unix.write(mainfd, string.format('preparing to accept from %d
\r\n', fd)) - addr = string.format('%s:%d', FormatIp(clientip), clientport) + unix.write(mainfd, 'preparing to accept from %d
\r\n' % {fd}) + client, clientip, clientport = assert(unix.accept(fd)) + addr = '%s:%d' % {FormatIp(clientip), clientport} addrs[client] = addr - unix.write(mainfd, string.format('got client %s
\r\n', addr)) + unix.write(mainfd, 'got client %s
\r\n' % {addr}) pollfds[client] = unix.POLLIN evs[server] = nil else - unix.write(mainfd, string.format('preparing to read from %d
\r\n', fd)) + unix.write(mainfd, 'preparing to read from %d
\r\n' % {fd}) data = unix.read(fd) - unix.write(mainfd, string.format('done reading from %d
\r\n', fd)) + unix.write(mainfd, 'done reading from %d
\r\n' % {fd}) if data and #data ~= 0 then - unix.write(mainfd, string.format('got %d bytes from %s
\r\n', #data, addrs[fd])) + unix.write(mainfd, 'got %d bytes from %s
\r\n' % {#data, addrs[fd]}) unix.write(fd, 'HTTP/1.0 200 OK\r\n' .. 'Date: '.. FormatHttpDateTime(GetDate()) ..'\r\n' .. 'Content-Type: text/html; charset=utf-8\r\n' .. diff --git a/tool/net/help.txt b/tool/net/help.txt index b48076d03..45bc29b64 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 @@ -73,57 +74,59 @@ FLAGS -P PATH pid file location -U INT daemon set user id -G INT daemon set group id - --strace enables system call tracing - --ftrace enables function call tracing + --strace enables system call tracing (see also -Z) + --ftrace enables function call tracing (see also -f) KEYBOARD - CTRL-D EXIT - CTRL-C CTRL-C EXIT - CTRL-E END - CTRL-A START - CTRL-B BACK - CTRL-F FORWARD - CTRL-L CLEAR - CTRL-H BACKSPACE - CTRL-D DELETE - CTRL-N NEXT HISTORY - CTRL-P PREVIOUS HISTORY - CTRL-R SEARCH HISTORY - CTRL-G CANCEL SEARCH - ALT-< BEGINNING OF HISTORY - ALT-> END OF HISTORY - ALT-F FORWARD WORD - ALT-B BACKWARD WORD - CTRL-K KILL LINE FORWARDS - CTRL-U KILL LINE BACKWARDS - ALT-H KILL WORD BACKWARDS - CTRL-W KILL WORD BACKWARDS - CTRL-ALT-H KILL WORD BACKWARDS - ALT-D KILL WORD FORWARDS - CTRL-Y YANK - ALT-Y ROTATE KILL RING AND YANK AGAIN - CTRL-T TRANSPOSE - ALT-T TRANSPOSE WORD - ALT-U UPPERCASE WORD - ALT-L LOWERCASE WORD - ALT-C CAPITALIZE WORD - CTRL-\ QUIT PROCESS - CTRL-S PAUSE OUTPUT - CTRL-Q UNPAUSE OUTPUT (IF PAUSED) - CTRL-Q ESCAPED INSERT - CTRL-ALT-F FORWARD EXPR - CTRL-ALT-B BACKWARD EXPR - ALT-RIGHT FORWARD EXPR - ALT-LEFT BACKWARD EXPR - ALT-SHIFT-B BARF EXPR - ALT-SHIFT-S SLURP EXPR - CTRL-SPACE SET MARK - CTRL-X CTRL-X GOTO MARK - CTRL-Z SUSPEND PROCESS - ALT-\ SQUEEZE ADJACENT WHITESPACE - PROTIP REMAP CAPS LOCK TO CTRL + CTRL-D EXIT + CTRL-C CTRL-C EXIT + CTRL-E END + CTRL-A START + CTRL-B BACK + CTRL-F FORWARD + CTRL-L CLEAR + CTRL-H BACKSPACE + CTRL-D DELETE + CTRL-N NEXT HISTORY + CTRL-P PREVIOUS HISTORY + CTRL-R SEARCH HISTORY + CTRL-G CANCEL SEARCH + ALT-< BEGINNING OF HISTORY + ALT-> END OF HISTORY + ALT-F FORWARD WORD + ALT-B BACKWARD WORD + CTRL-K KILL LINE FORWARDS + CTRL-U KILL LINE BACKWARDS + ALT-H KILL WORD BACKWARDS + CTRL-W KILL WORD BACKWARDS + CTRL-ALT-H KILL WORD BACKWARDS + ALT-D KILL WORD FORWARDS + CTRL-Y YANK + ALT-Y ROTATE KILL RING AND YANK AGAIN + CTRL-T TRANSPOSE + ALT-T TRANSPOSE WORD + ALT-U UPPERCASE WORD + ALT-L LOWERCASE WORD + ALT-C CAPITALIZE WORD + CTRL-\ QUIT PROCESS + CTRL-S PAUSE OUTPUT + CTRL-Q UNPAUSE OUTPUT (IF PAUSED) + CTRL-Q ESCAPED INSERT + CTRL-ALT-F FORWARD EXPR + CTRL-ALT-B BACKWARD EXPR + ALT-RIGHT FORWARD EXPR + ALT-LEFT BACKWARD EXPR + ALT-SHIFT-B BARF EXPR + ALT-SHIFT-S SLURP EXPR + CTRL-SPACE SET MARK + CTRL-X CTRL-X GOTO MARK + CTRL-Z SUSPEND PROCESS + ALT-\ SQUEEZE ADJACENT WHITESPACE + PROTIP REMAP CAPS LOCK TO CTRL +──────────────────────────────────────────────────────────────────────────────── + USAGE This executable is also a ZIP file that contains static assets. @@ -231,6 +234,8 @@ USAGE inside the binary. redbean also respects your privacy and won't phone home because your computer is its home. +──────────────────────────────────────────────────────────────────────────────── + SECURITY redbean uses a protocol polyglot for serving HTTP and HTTPS on @@ -246,1073 +251,1257 @@ SECURITY -VVV log ssl informational messages too -VVVV log ssl verbose details too - See https://redbean.dev for further details. + See http://redbean.dev for further details. +──────────────────────────────────────────────────────────────────────────────── + LUA SERVER PAGES - Any files with the extension .lua will be dynamically served by redbean. - Here's the simplest possible example: + Any files with the extension .lua will be dynamically served by redbean. + Here's the simplest possible example: - Write('Hello World') + Write('Hello World') - The Lua Server Page above should be able to perform at 700,000 responses - per second on a Core i9, without any sort of caching. If you want a Lua - handler that can do 1,000,000 responses per second, then try adding the - following global handler to your /.init.lua file: + The Lua Server Page above should be able to perform at 700,000 responses + per second on a Core i9, without any sort of caching. If you want a Lua + handler that can do 1,000,000 responses per second, then try adding the + following global handler to your /.init.lua file: - function OnHttpRequest() - Write('Hello World') - end + function OnHttpRequest() + Write('Hello World') + end - Here's an example of a more typical workflow for Lua Server Pages using - the redbean API: + Here's an example of a more typical workflow for Lua Server Pages using + the redbean API: - SetStatus(200) - SetHeader('Content-Type', 'text/plain; charset=utf-8') - Write('

Hello ') - Write(EscapeHtml(GetParam('name'))) + SetStatus(200) + SetHeader('Content-Type', 'text/plain; charset=utf-8') + Write('

Hello ') + Write(EscapeHtml(GetParam('name'))) - We didn't need the first two lines in the previous example, because - they're implied by redbean automatically if you don't set them. Responses - are also buffered until the script finishes executing. That enables - redbean to make HTTP as easy as possible. In the future, API capabilities - will be expanded to make possible things like websockets. + We didn't need the first two lines in the previous example, because + they're implied by redbean automatically if you don't set them. Responses + are also buffered until the script finishes executing. That enables + redbean to make HTTP as easy as possible. In the future, API capabilities + will be expanded to make possible things like websockets. - redbean embeds the Lua standard library. You can use packages such as io - to persist and share state across requests and connections, as well as the - StoreAsset function, and the lsqlite3 module. + redbean embeds the Lua standard library. You can use packages such as io + to persist and share state across requests and connections, as well as the + StoreAsset function, and the lsqlite3 module. - Your Lua interpreter begins its life in the main process at startup in the - .init.lua, which is likely where you'll want to perform all your expensive - one-time operations like importing modules. Then, as requests roll in, - isolated processes are cloned from the blueprint you created. + Your Lua interpreter begins its life in the main process at startup in the + .init.lua, which is likely where you'll want to perform all your expensive + one-time operations like importing modules. Then, as requests roll in, + isolated processes are cloned from the blueprint you created. -SPECIAL PATHS +──────────────────────────────────────────────────────────────────────────────── + +REPL - / - redbean will generate a zip central directory listing for this - page, and this page only, but only if there isn't an /index.lua or - /index.html file defined. + 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. - /.init.lua - This script is run once in the main process at startup. This lets - you modify the state of the Lua interpreter before connection - processes are forked off. For example, it's a good idea to do - expensive one-time computations here. You can also use this file - to call the ProgramFOO() functions below. The init module load - happens after redbean's arguments and zip assets have been parsed, - but before calling functions like socket() and fork(). Note that - this path is a hidden file so that it can't be unintentionally run - by the network client. + 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 + the REPL is a Lua REPL it's not included in a redbean-static builds. - /.reload.lua - This script is run from the main process when SIGHUP is received. - This only applies to redbean when running in daemon mode. Any - changes that are made to the Lua interpreter state will be - inherited by future forked connection processes. Note that this - path is a hidden file so that it can't be unintentionally run by - the network client. + redbean uses the same keyboard shortcuts as GNU Readline and Emacs. + Some of its keyboard commands (listed in a previous section) were + inspired by Paredit. - /.lua/... - Your Lua modules go in this directory. The way it works is redbean - sets Lua's package.path to /zip/.lua/?.lua;/zip/.lua/?/init.lua by - default. Cosmopolitan Libc lets system calls like open read from - the ZIP structure, if the filename is prefixed with /zip/. So this - works like magic. + A history of your commands is saved to `~/.redbean_history`. - /redbean.png - If it exists, it'll be used as the / listing page icon, embedded - as a base64 URI. + 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. - /usr/share/ssl/root - This directory contains your root certificate authorities. It is - needed so the Fetch() HTTPS client API can verify that a remote - certificate was signed by a third party. You can add your own - certificate files to this directory within the ZIP executable. - If you enable HTTPS client verification then redbean will check - that HTTPS clients (a) have a certificate and (b) it was signed. + redbean.com -i binarytrees.lua 15 + When the `-i` flag is passed (for interpreter 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 is useful for testing since redbean extensions and + modules for the Lua language, are still made available. You can also + write redbean scripts with shebang lines: + + #!/usr/bin/redbean -i + print('hello world') + + However operating systems like Linux usually require that script + interperters be in the local executable format. You can "assimilate" + and install your redbean using the following commands: + + zip -d redbean.com .ape # remove the ape header + ./redbean.com -h >/dev/null # assimilate the binary + sudo cp redbean.com /usr/bin/redbean + + By following the above steps, redbean can be installed systemwide for + multiple user accounts. It's also possible to chmod the binary to have + setuid privileges, provided it's configured to drop privileges in the + most appropriate manner; see the UNIX section for further details. + + +──────────────────────────────────────────────────────────────────────────────── + +LUA ENHANCEMENTS + + We've made some enhancements to the Lua language that should make it + more comfortable for C/C++ and Python developers. Some of these + + - redbean supports a printf modulus operator, like Python. For + example, you can say `"hello %s" % {"world"}` instead of + `string.format("hello %s", "world")`. + + - redbean supports octal (base 8) integer literals. For example + `0644 == 420` is the case in redbean, whereas in upstream Lua + `0644 == 644` would be the case. + + - redbean supports binary (base 2) integer literals. For example + `0b1010 == 10` is the case in redbean, whereas in upstream Lua + `0b1010` would result in an error. + + - redbean supports the GNU syntax for the ASCII ESC character in + string literals. For example, `"\e"` is the same as `"\x1b"`. + + +──────────────────────────────────────────────────────────────────────────────── + GLOBALS - argv: 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. + 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' + +──────────────────────────────────────────────────────────────────────────────── + +SPECIAL PATHS + + / + redbean will generate a zip central directory listing for this + page, and this page only, but only if there isn't an /index.lua or + /index.html file defined. + + /.init.lua + This script is run once in the main process at startup. This lets + you modify the state of the Lua interpreter before connection + processes are forked off. For example, it's a good idea to do + expensive one-time computations here. You can also use this file + to call the ProgramFOO() functions below. The init module load + happens after redbean's arguments and zip assets have been parsed, + but before calling functions like socket() and fork(). Note that + this path is a hidden file so that it can't be unintentionally run + by the network client. + + /.reload.lua + This script is run from the main process when SIGHUP is received. + This only applies to redbean when running in daemon mode. Any + changes that are made to the Lua interpreter state will be + inherited by future forked connection processes. Note that this + path is a hidden file so that it can't be unintentionally run by + the network client. + + /.lua/... + Your Lua modules go in this directory. The way it works is redbean + sets Lua's package.path to /zip/.lua/?.lua;/zip/.lua/?/init.lua by + default. Cosmopolitan Libc lets system calls like open read from + the ZIP structure, if the filename is prefixed with /zip/. So this + works like magic. + + /redbean.png + If it exists, it'll be used as the / listing page icon, embedded + as a base64 URI. + + /usr/share/zoneinfo + This directory contains a subset of the timezone database. + Your `TZ` environment variable controls which one of these + files is used by functions such as unix.localtime(). + + /usr/share/ssl/root + This directory contains your root certificate authorities. It is + needed so the Fetch() HTTPS client API can verify that a remote + certificate was signed by a third party. You can add your own + certificate files to this directory within the ZIP executable. + If you enable HTTPS client verification then redbean will check + that HTTPS clients (a) have a certificate and (b) it was signed. + +──────────────────────────────────────────────────────────────────────────────── + HOOKS - OnHttpRequest() - If this function is defined in the global scope by your /.init.lua - then redbean will call it at the ealiest possible moment to - hand over control for all messages (with the exception of OPTIONS - *). See functions like Route which asks redbean to do its default - thing from the handler. + OnHttpRequest() + If this function is defined in the global scope by your /.init.lua + then redbean will call it at the ealiest possible moment to + hand over control for all messages (with the exception of OPTIONS + *). See functions like Route which asks redbean to do its default + thing from the handler. - OnClientConnection(ip:int,port:int,serverip:int,serverport:int) → bool - If this function is defined it'll be called from the main process - each time redbean accepts a new client connection. If it returns - true then redbean will close the connection without calling fork. + OnClientConnection(ip:int,port:int,serverip:int,serverport:int) → bool + If this function is defined it'll be called from the main process + each time redbean accepts a new client connection. If it returns + true then redbean will close the connection without calling fork. - OnProcessCreate(pid:int,ip:int,port:int,serverip:int,serverport:int) - If this function is defined it'll be called from the main process - each time redbean forks a connection handler worker process. The - ip/port of the remote client is provided, along with the ip/port - of the listening interface that accepted the connection. This may - be used to create a server activity dashboard, in which case the - data provider handler should set SetHeader('Connection','Close'). - This won't be called in uniprocess mode. + OnProcessCreate(pid:int,ip:int,port:int,serverip:int,serverport:int) + If this function is defined it'll be called from the main process + each time redbean forks a connection handler worker process. The + ip/port of the remote client is provided, along with the ip/port + of the listening interface that accepted the connection. This may + be used to create a server activity dashboard, in which case the + data provider handler should set SetHeader('Connection','Close'). + This won't be called in uniprocess mode. - OnProcessDestroy(pid:int) - If this function is defined it'll be called from the main process - each time redbean reaps a child connection process using wait4(). - This won't be called in uniprocess mode. + OnProcessDestroy(pid:int) + If this function is defined it'll be called from the main process + each time redbean reaps a child connection process using wait4(). + This won't be called in uniprocess mode. - OnServerStart() - If this function is defined it'll be called from the main process - right before the main event loop starts. + OnServerStart() + If this function is defined it'll be called from the main process + right before the main event loop starts. - OnServerStop() - If this function is defined it'll be called from the main process - after all the connection processes have been reaped and exit() is - ready to be called. + OnServerStop() + If this function is defined it'll be called from the main process + after all the connection processes have been reaped and exit() is + ready to be called. - OnWorkerStart() - If this function is defined it'll be called from the child worker - process after it's been forked and before messages are handled. - This won't be called in uniprocess mode. + OnWorkerStart() + If this function is defined it'll be called from the child worker + process after it's been forked and before messages are handled. + This won't be called in uniprocess mode. - OnWorkerStop() - If this function is defined it'll be called from the child worker - process once _exit() is ready to be called. This won't be called - in uniprocess mode. + OnWorkerStop() + If this function is defined it'll be called from the child worker + process once _exit() is ready to be called. This won't be called + in uniprocess mode. +──────────────────────────────────────────────────────────────────────────────── + FUNCTIONS - Write(data:str) - Appends data to HTTP response payload buffer. This is buffered - independently of headers. - - SetStatus(code:int[,reason:str]) - Starts an HTTP response, specifying the parameters on its first - line. reason is optional since redbean can fill in the appropriate - text for well-known magic numbers, e.g. 200, 404, etc. This method - will reset the response and is therefore mutually exclusive with - ServeAsset and other Serve* functions. If this function isn't - called, then the default behavior is to send 200 OK. - - SetHeader(name:str,value:str) - Appends HTTP header to response header buffer. name is - case-insensitive and restricted to non-space ASCII. value is a - UTF-8 string that must be encodable as ISO-8859-1. Leading and - trailing whitespace is trimmed automatically. Overlong characters - are canonicalized. C0 and C1 control codes are forbidden, with the - exception of tab. This function automatically calls SetStatus(200, - "OK") if a status has not yet been set. As SetStatus and Serve* - functions reset the response, SetHeader needs to be called after - SetStatus and Serve* functions are called. The header buffer is - independent of the payload buffer. Neither is written to the wire - until the Lua Server Page has finished executing. This function - disallows the setting of certain headers such as Content-Range and - Date, which are abstracted by the transport layer. In such cases, - consider calling ServeAsset. - - SetCookie(name:str,value:str[,options:table]) - Appends Set-Cookie HTTP header to the response header buffer. - Several Set-Cookie headers can be added to the same response. - __Host- and __Secure- prefixes are supported and may set or - overwrite some of the options (for example, specifying __Host- - prefix sets the Secure option to true, sets the path to "/", and - removes the Domain option). The following options can be used (their - lowercase equivalents are supported as well): - - Expires: sets the maximum lifetime of the cookie as an HTTP-date - timestamp. Can be specified as a Date in the RFC1123 (string) - format or as a UNIX timestamp (number of seconds). - - MaxAge: sets number of seconds until the cookie expires. A zero - or negative number will expire the cookie immediately. If both - Expires and MaxAge are set, MaxAge has precedence. - - Domain: sets the host to which the cookie will be sent. - - Path: sets the path that must be present in the request URL, or - the client will not send the Cookie header. - - Secure: (bool) requests the cookie to be only send to the - server when a request is made with the https: scheme. - - HttpOnly: (bool) forbids JavaScript from accessing the cookie. - - SameSite: (Strict, Lax, or None) controls whether a cookie is - sent with cross-origin requests, providing some protection - against cross-site request forgery attacks. - - GetParam(name:str) → value:str - Returns first value associated with name. name is handled in a - case-sensitive manner. This function checks Request-URL parameters - first. Then it checks application/x-www-form-urlencoded from the - message body, if it exists, which is common for HTML forms sending - POST requests. If a parameter is supplied matching name that has - no value, e.g. foo in ?foo&bar=value, then the returned value will - be nil, whereas for ?foo=&bar=value it would be "". To - differentiate between no-equal and absent, use the HasParam - function. The returned value is decoded from ISO-8859-1 (only in - the case of Request-URL) and we assume that percent-encoded - characters were supplied by the client as UTF-8 sequences, which - are returned exactly as the client supplied them, and may - therefore may contain overlong sequences, control codes, NUL - characters, and even numbers which have been banned by the IETF. - It is the responsibility of the caller to impose further - restrictions on validity, if they're desired. - - EscapeHtml(str) → str - Escapes HTML entities: The set of entities is &><"' which become - &><"'. This function is charset agnostic and - will not canonicalize overlong encodings. It is assumed that a - UTF-8 string will be supplied. See escapehtml.c. - - LaunchBrowser([path:str]) - Launches web browser on local machine with URL to this redbean - server. This function may be called from your /.init.lua. - - CategorizeIp(ip:uint32) → str - Returns a string describing an IP address. This is currently Class - A granular. It can tell you if traffic originated from private - networks, ARIN, APNIC, DOD, etc. - - DecodeBase64(ascii:str) → binary:str - Turns ASCII into binary, in a permissive way that ignores - characters outside the base64 alphabet, such as whitespace. See - decodebase64.c. - - DecodeLatin1(iso-8859-1:str) → utf-8:str - Turns ISO-8859-1 string into UTF-8. - - EncodeBase64(binary:str) → ascii:str - Turns binary into ASCII. This can be used to create HTML data: - URIs that do things like embed a PNG file in a web page. See - encodebase64.c. - - EncodeJson(value[,options:table]) → json:str - Turns passed Lua value into a JSON string. Tables with non-zero - length (as reported by `#`) are encoded as arrays with non-array - elements ignored. Empty tables are encoded as empty arrays. All - other tables are encoded as objects with numerical keys - converted to strings (so `{[3]=1}` is encoded as `{"3":1}`). - The following options can be used: - - 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. - - EncodeLua(value[,options:table]) → json:str - Turns passed Lua value into a Lua string. The following options - can be used: - - 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 - Turns UTF-8 into ISO-8859-1 string. - - EscapeFragment(str) → str - Escapes URL #fragment. The allowed characters are - -/?.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. - Please note that '& can still break HTML and that '() can still - break CSS URLs. This function is charset agnostic and will not - canonicalize overlong encodings. It is assumed that a UTF-8 string - will be supplied. See kescapefragment.c. - - EscapeHost(str) → str - Escapes URL host. See kescapeauthority.c - - EscapeLiteral(str) → str - Escapes JavaScript or JSON string literal content. The caller is - responsible for adding the surrounding quotation marks. This - implementation \uxxxx sequences for all non-ASCII sequences. HTML - entities are also encoded, so the output doesn't need EscapeHtml. - This function assumes UTF-8 input. Overlong encodings are - canonicalized. Invalid input sequences are assumed to be - ISO-8859-1. The output is UTF-16 since that's what JavaScript - uses. For example, some individual codepoints such as emoji - characters will encode as multiple \uxxxx sequences. Ints that are - impossible to encode as UTF-16 are substituted with the \xFFFD - replacement character. See escapejsstringliteral.c. - - EscapeParam(str) → str - Escapes URL parameter name or value. The allowed characters are - -.*_0-9A-Za-z and everything else gets %XX encoded. This function - is charset agnostic and will not canonicalize overlong encodings. - It is assumed that a UTF-8 string will be supplied. See - kescapeparam.c. - - EscapePass(str) → str - Escapes URL password. See kescapeauthority.c. - - EscapePath(str) → str - Escapes URL path. This is the same as EscapeSegment except slash - is allowed. The allowed characters are -.~_@:!$&'()*+,;=0-9A-Za-z/ - and everything else gets %XX encoded. Please note that '& can - still break HTML, so the output may need EscapeHtml too. Also note - that '() can still break CSS URLs. This function is charset - agnostic and will not canonicalize overlong encodings. It is - assumed that a UTF-8 string will be supplied. See kescapepath.c. - - EscapeSegment(str) → str - Escapes URL path segment. This is the same as EscapePath except - slash isn't allowed. The allowed characters are - -.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. - Please note that '& can still break HTML, so the output may need - EscapeHtml too. Also note that '() can still break CSS URLs. This - function is charset agnostic and will not canonicalize overlong - encodings. It is assumed that a UTF-8 string will be supplied. See - kescapesegment.c. - - EscapeUser(str) → str - Escapes URL username. See kescapeauthority.c. - - Fetch(url:str[,body:str|{method=value:str,body=value:str,...}]) - → status:int,{header:str=value:str,...},body:str - Sends an HTTP/HTTPS request to the specified URL. If only the URL is - provided, then a GET request is sent. If both URL and body parameters - are specified, then a POST request is sent. If any other method needs - to be specified (for example, PUT or DELETE), then passing a table as - the second value allows setting method and body values as well other - options: - - method (default = "GET"): sets the method to be used for the - request. The specified method is converted to uppercase. - - body (default = ""): sets the body value to be sent. - - followredirect (default = true): forces temporary and permanent - redirects to be followed. This behavior can be disabled by - passing `false`. - - maxredirects (default = 5): sets the number of allowed redirects - to minimize looping due to misconfigured servers. When the number - is exceeded, the result of the last redirect is returned. - When the redirect is being followed, the same method and body values - are being sent in all cases except when 303 status is returned. In - that case the method is set to GET and the body is removed before the - redirect is followed. Note that if these (method/body) values are - provided as table fields, they will be modified in place. - - FormatHttpDateTime(seconds:int) → rfc1123:str - Converts UNIX timestamp to an RFC1123 string that looks like this: - Mon, 29 Mar 2021 15:37:13 GMT. See formathttpdatetime.c. - - FormatIp(uint32) → str - Turns integer like 0x01020304 into a string like 1.2.3.4. See also - ParseIp for the inverse operation. - - GetAssetComment(path:str) → str - Returns comment text associated with asset in the ZIP central - directory. Also available as GetComment (deprecated). - - GetAssetMode(path:str) → int - Returns UNIX-style octal mode for ZIP asset (or local file if the - -D flag is used) - - GetAssetSize(path:str) → int - Returns byte size of uncompressed contents of ZIP asset (or local - file if the -D flag is used) - - GetBody() → str - Returns the request message body if present or an empty string. - Also available as GetPayload (deprecated). - - GetCookie(name:str) → str - Returns cookie value. - - GetCryptoHash(name:str,payload:str[,key:str]) → str - Returns value of the specified cryptographic hash function. If the - key is provided, then HMAC value of the same function is returned. - The name can be one of the following strings: MD5, SHA1, SHA224, - SHA256, SHA384, SHA512, and BLAKE2B256. - - GetRemoteAddr() → ip:uint32,port:uint16 - Returns client ip4 address and port, e.g. 0x01020304,31337 would - represent 1.2.3.4:31337. This is the same as GetClientAddr except - it will use the ip:port from the X-Forwarded-For header, only if - IsPrivateIp or IsLoopbackIp return true. When multiple addresses - are present in the header, the last/right-most address is used. - - GetClientAddr() → ip:uint32,port:uint16 - Returns client socket ip4 address and port, e.g. 0x01020304,31337 - would represent 1.2.3.4:31337. Please consider using GetRemoteAddr - instead, since the latter takes into consideration reverse proxy - scenarios. - - GetClientFd() → int - Returns file descriptor being used for client connection. - This is useful for scripts that want to use unix:fork(). - - IsClientUsingSsl() → bool - Returns true if client connection has begun being managed by - the MbedTLS security layer. This is an important thing to - consider if a script is taking control of GetClientFd() - - GetServerAddr() → ip:uint32,port:uint16 - Returns address to which listening server socket is bound, e.g. - 0x01020304,8080 would represent 1.2.3.4:8080. If -p 0 was supplied - as the listening port, then the port in this string will be - whatever number the operating system assigned. - - GetDate() → seconds:int - Returns date associated with request that's used to generate the - Date header, which is now, give or take a second. The returned - value is a UNIX timestamp. - - GetHeader(name:str) → value:str - Returns HTTP header. name is case-insensitive. The header value is - returned as a canonical UTF-8 string, with leading and trailing - whitespace trimmed, which was decoded from ISO-8859-1, which is - guaranteed to not have C0/C1 control sequences, with the exception - of the tab character. Leading and trailing whitespace is - automatically removed. In the event that the client suplies raw - UTF-8 in the HTTP message headers, the original UTF-8 sequence can - be losslessly restored by counter-intuitively recoding the - returned string back to Latin1. If the requested header is defined - by the RFCs as storing comma-separated values (e.g. Allow, - Accept-Encoding) and the field name occurs multiple times in the - message, then this function will fold those multiple entries into - a single string. - - GetHeaders() → {name:str=value:str,...} - Returns HTTP headers as dictionary mapping header key strings to - their UTF-8 decoded values. The ordering of headers from the - request message is not preserved. Whether or not the same key can - repeat depends on whether or not it's a standard header, and if - so, if it's one of the ones that the RFCs define as repeatable. - See khttprepeatable.c. Those headers will not be folded. Standard - headers which aren't on that list, will be overwritten with the - last-occurring one during parsing. Extended headers are always - passed through exactly as they're received. Please consider using - GetHeader API if possible since it does a better job abstracting - these issues. - - GetLogLevel() → int - Returns logger verbosity level. Likely return values are kLogDebug - > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. - - GetHost() → str - Returns host associated with request. This will be the Host - header, if it's supplied. Otherwise it's the bind address. - - GetHostOs() → str - Returns string that describes the host OS. - - GetMonospaceWidth(str|char) → int - Returns monospace display width of string. This is useful for - fixed-width formatting. For example, CJK characters typically take - up two cells. This function takes into consideration combining - characters, which are discounted, as well as control codes and - ANSI escape sequences. - - GetMethod() → str - Returns HTTP method. Normally this will be GET, HEAD, or POST in - which case redbean normalizes this value to its uppercase form. - Anything else that the RFC classifies as a "token" string is - accepted too, which might contain characters like &". - - GetParams() → {{name:str[,value:str]},...} - Returns name=value parameters from Request-URL and - application/x-www-form-urlencoded message body in the order they - were received. This may contain duplicates. The inner array will - have either one or two items, depending on whether or not the - equals sign was used. - - GetPath() → str - Returns the Request-URL path. This is guaranteed to begin with - "/". It is further guaranteed that no "//" or "/." exists in the - path. The returned value is returned as a UTF-8 string which was - decoded from ISO-8859-1. We assume that percent-encoded characters - were supplied by the client as UTF-8 sequences, which are returned - exactly as the client supplied them, and may therefore may contain - overlong sequences, control codes, NUL characters, and even - numbers which have been banned by the IETF. redbean takes those - things into consideration when performing path safety checks. It - is the responsibility of the caller to impose further restrictions - on validity, if they're desired. - - GetEffectivePath() → str - Returns path as it was resolved by the routing algorithms, which - might contain the virtual host prepended if used. - - GetScheme() → str - Returns scheme from Request-URL, if any. - - GetSslIdentity() → str - Returns certificate subject or PSK identity from the current SSL - session. `nil` is returned for regular (non-SSL) connections. - - GetStatus() → int - Returns current status (as set by an earlier SetStatus call) or - `nil` if the status hasn't been set yet. - - GetTime() → seconds:number - Returns current time as a UNIX timestamp with 0.0001s precision. - - GetUrl() → str - Returns the effective Request-URL as an ASCII string, where - illegal characters or UTF-8 is guaranteed to be percent encoded, - and has been normalized to include either the Host or - X-Forwarded-Host headers, if they exist, and possibly a scheme too - if redbean is being used as an HTTP proxy server. In the future - this API might change to return an object instead. - - GetHttpVersion() → int - Returns the request HTTP protocol version, which can be 9 for - HTTP/0.9, 10 for HTTP/1.0, or 11 for HTTP/1.1. Also available - as GetVersion (deprecated). - - GetRandomBytes([length:int]) → str - Returns string with the specified number of random bytes (1..256). - If no length is specified, then a string of length 16 is returned. - - GetRedbeanVersion() → int - Returns the Redbean version in the format 0xMMmmpp, with major (MM), - minor (mm), and patch (pp) versions encoded. The version value 1.4 - would be represented as 0x010400. - - GetZipPaths([prefix:str]) → {path:str,...} - Returns paths of all assets in the zip central directory, prefixed - by a slash. If prefix parameter is provided, then only paths that - start with the prefix (case sensitive) are returned. - - HasParam(name:str) → bool - Returns true if parameter with name was supplied in either the - Request-URL or an application/x-www-form-urlencoded message body. - - HidePath(prefix:str) - Programs redbean / listing page to not display any paths beginning - with prefix. This function should only be called from /.init.lua. - - IsPublicIp(uint32) → bool - Returns true if IP address is not a private network (10.0.0.0/8, - 172.16.0.0/12, 192.168.0.0/16) and is not localhost (127.0.0.0/8). - Note: we intentionally regard TEST-NET IPs as public. - - IsPrivateIp(uint32) → bool - Returns true if IP address is part of a private network - (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). - - IsLoopbackClient() → bool - Returns true if the client IP address (returned by GetRemoteAddr) - is part of the localhost network (127.0.0.0/8). - - IsPrivateClient() → bool - Returns true if the client IP address (returned by GetRemoteAddr) - is part of the localhost network (127.0.0.0/8) or a private network - (10.0.0.0/8, etc.) - - IsLoopbackIp(uint32) → bool - Returns true if IP address is part of the localhost network - (127.0.0.0/8). - - IsCompressed(path:str) → bool - Returns true if ZIP artifact at path is stored on disk using - DEFLATE compression. - - IndentLines(str[,int]) → str - Adds spaces to beginnings of multiline string. If the int - parameter is not supplied then 1 space will be added. - - LoadAsset(path:str) → str - Returns contents of file as string. The asset may be sourced from - either the zip (decompressed) or the local filesystem if the -D - flag was used. If slurping large file into memory is a concern, - then consider using ServeAsset which can serve directly off disk. - - StoreAsset(path:str,data:str[,mode:int]) - Stores asset to executable's ZIP central directory. This currently - happens in an append-only fashion and is still largely in the - proof-of-concept stages. Currently only supported on Linux, XNU, - and FreeBSD. - - Log(level:int,message:str) - Emits message string to log, if level is less than or equal to - GetLogLevel. If redbean is running in interactive mode, then this - will log to the console. If redbean is running as a daemon or the - -L LOGFILE flag is passed, then this will log to the file. - Reasonable values for level are kLogDebug > kLogVerbose > kLogInfo - > kLogWarn > kLogError > kLogFatal. The logger emits timestamps in - the local timezone with microsecond precision. If log entries are - emitted more frequently than once per second, then the log entry - will display a delta timestamp, showing how much time has elapsed - since the previous log entry. This behavior is useful for quickly - measuring how long various portions of your code take to execute. - - ParseHttpDateTime(rfc1123:str) → seconds:int - Converts RFC1123 string that looks like this: Mon, 29 Mar 2021 - 15:37:13 GMT to a UNIX timestamp. See parsehttpdatetime.c. - - ParseUrl(str) → URL - Parses URL, returning object having the following fields: scheme, - user, pass, host, port, path, params, fragment. This parser is - charset agnostic. Percent encoded bytes are decoded for all - fields. Returned values might contain things like NUL characters, - spaces, control codes, and non-canonical encodings. Absent can be - discerned from empty by checking if the pointer is set. There's no - failure condition for this routine. This is a permissive parser. - This doesn't normalize path segments like `.` or `..` so use - IsAcceptablePath() to check for those. No restrictions are imposed - beyond that which is strictly necessary for parsing. All the data - that is provided will be consumed to the one of the fields. Strict - conformance is enforced on some fields more than others, like - scheme, since it's the most non-deterministically defined field of - them all. Please note this is a URL parser, not a URI parser. - Which means we support everything everything the URI spec says we - should do except for the things we won't do, like tokenizing path - segments into an array and then nesting another array beneath each - of those for storing semicolon parameters. So this parser won't - make SIP easy. What it can do is parse HTTP URLs and most URIs - like data:opaque, better in fact than most things which claim to - be URI parsers. - - EncodeUrl(URL) → str - This function is the inverse of ParseUrl. The output will always - be correctly formatted. The exception is if illegal characters are - supplied in the scheme field, since there's no way of escaping - those. Opaque parts are escaped as though they were paths, since - many URI parsers won't understand things like an unescaped - question mark in path. - - ParseIp(str) → int - Converts IPv4 address string to integer, e.g. "1.2.3.4" → - 0x01020304, or returns -1 for invalid inputs. See also FormatIp - for the inverse operation. - - ProgramAddr(str) - Configures the address on which to listen. Can be used multiple - times to set more than one address. - - ProgramBrand(str) - Changes HTTP Server header, as well as the

title on the / - listing page. The brand string needs to be a UTF-8 value that's - encodable as ISO-8859-1. If the brand is changed to something - other than redbean, then the promotional links will be removed - from the listing page too. This function should only be called - from /.init.lua. - - ProgramCache(seconds:int) - Configures Cache-Control and Expires header generation for static - asset serving. A negative value will disable the headers. Zero - means don't cache. Greater than zero asks public proxies and - browsers to cache for a given number of seconds. This should only - be called from /.init.lua. - - ProgramCertificate(pem:str) - Same as the -C flag if called from .init.lua, e.g. - ProgramCertificate(LoadAsset("/.sign.crt")) for zip loading or - ProgramCertificate(Slurp("/etc/letsencrypt.lol/fullchain.pem")) for - local file system only. - - ProgramHeader(name:str,value:str) - Appends HTTP header to the header buffer for all responses (whereas - SetHeader only appends a header to the current response buffer). - name is case-insensitive and restricted to non-space ASCII. value - is a UTF-8 string that must be encodable as ISO-8859-1. Leading and - trailing whitespace is trimmed automatically. Overlong characters - are canonicalized. C0 and C1 control codes are forbidden, with the - exception of tab. The header buffer is independent of the payload - buffer. This function disallows the setting of certain headers such - as Content-Range and Date, which are abstracted by the transport - layer. - - ProgramPort(uint16) - Hard-codes the port number on which to listen, which can be any - number in the range 1..65535, or alternatively 0 to ask the - operating system to choose a port, which may be revealed later on - by GetServerAddr or the -z flag to stdout. - - ProgramPrivateKey(pem:str) - Same as the -K flag if called from .init.lua, e.g. - ProgramPrivateKey(LoadAsset("/.sign.key")) for zip loading or - ProgramPrivateKey(Slurp("/etc/letsencrypt/fullchain.pem")) for - local file system only. - - ProgramRedirect(code:int,src:str,location:str) - Configures fallback routing for paths which would otherwise return - 404 Not Found. If code is 0 then the path is rewritten internally - as an accelerated redirect. If code is 301, 302, 307, or 308 then - a redirect response will be sent to the client. This should only - be called from /.init.lua. - - ProgramSslTicketLifetime(seconds:int) - Defaults to 86400 (24 hours). This may be set to ≤0 to disable - SSL tickets. It's a good idea to use these since it increases - handshake performance 10x and eliminates a network round trip. - - EvadeDragnetSurveillance(bool) - If this option is programmed then redbean will not transmit a - Server Name Indicator (SNI) when performing Fetch() requests. - - ProgramSslPresharedKey(key:str,identity:str) - This function can be used to enable the PSK ciphersuites - which simplify SSL and enhance its performance in controlled - environments. `key` may contain 1..32 bytes of random binary - data and identity is usually a short plaintext string. The - first time this function is called, the preshared key will - be added to both the client and the server SSL configs. If - it's called multiple times, then the remaining keys will be - added to the server, which is useful if you want to assign - separate keys to each client, each of which needs a separate - identity too. If this function is called multiple times with - the same identity string, then the latter call will overwrite - the prior. If a preshared key is supplied and no certificates - or key-signing-keys are programmed, then redbean won't bother - auto-generating any serving certificates and will instead use - only PSK ciphersuites. - - ProgramSslCiphersuite(name:str) - See https://redbean.dev/ for further details. - - IsDaemon() → bool - Returns true if -d flag was passed to redbean. - - ProgramUid(int) - Same as the -U flag if called from .init.lua for setuid() - - ProgramGid(int) - Same as the -G flag if called from .init.lua for setgid() - - ProgramDirectory(str) - Same as the -D flag if called from .init.lua for overlaying local - file system directories. This may be called multiple times. The - first directory programmed is preferred. These currently do not - show up in the index page listing. - - ProgramLogMessages(bool) - Same as the -m flag if called from .init.lua for logging message - headers only. - - ProgramLogBodies(bool) - Same as the -b flag if called from .init.lua for logging message - bodies as part of POST / PUT / etc. requests. - - ProgramLogPath(str) - Same as the -L flag if called from .init.lua for setting the log - file path on the local file system. It's created if it doesn't - exist. This is called before de-escalating the uesr / group id. - The file is opened in append only mode. If the disk runs out of - space then redbean will truncate the log file if has access to - change the log file after daemonizing. - - ProgramPidPath(str) - Same as the -P flag if called from .init.lua for setting the pid - file path on the local file system. It's useful for reloading - daemonized redbean using `kill -HUP $(cat /var/run/redbean.pid)` - or terminating redbean with `kill $(cat /var/run/redbean.pid)` - which will gracefully terminate all clients. Sending the TERM - signal twice will cause a forceful shutdown, which might make - someone with a slow internet connection who's downloading big - files unhappy. - - ProgramUniprocess([bool]) → bool - Same as the -u flag if called from .init.lua. Can be used to - configure the uniprocess mode. The current value is returned. - - Slurp(filename:str) → str - Reads file data from local file system. - - Sleep(seconds:number) - Sleeps the specified number of seconds (can be fractional). The - smallest interval is a microsecond. - - Route([host:str,[path:str]]) - Instructs redbean to follow the normal HTTP serving path. This - function is useful when writing an OnHttpRequest handler, since - that overrides the serving path entirely. So if the handler - decides it doesn't want to do anything, it can simply call this - function, to hand over control back to the redbean core. By - default, the host and path arguments are supplied from the - resolved GetUrl value. This handler always resolves, since it will - generate a 404 Not Found response if redbean couldn't find an - appropriate endpoint. - - RouteHost([host:str,[path:str]]) → bool - This is the same as Route, except it only implements the subset of - request routing needed for serving virtual-hosted assets, where - redbean tries to prefix the path with the hostname when looking up - a file. This function returns true if the request was resolved. If - it was resolved, then your OnHttpRequest request handler can still - set additional headers. - - RoutePath([path:str]) → bool - This is the same as Route, except it only implements the subset of - request routing needed for serving assets. This function returns - true if the request was resolved. If it was resolved, then your - OnHttpRequest request handler can still set additional headers. - Note that the asset needs to have "read other" permissions; - otherwise this function logs a warning and returns 403 Forbidden. - If this is undesirable, use GetAssetMode and ServeAsset to bypass - the check. - - ServeAsset(path:str) - Instructs redbean to serve static asset at path. This function - causes what would normally happen outside a dynamic handler to - happen. The asset can be sourced from either the zip or local - filesystem if -D is used. This function is mutually exclusive with - SetStatus and other Serve* functions. - - ServeError(code:int[,reason:str]) - Instructs redbean to serve a boilerplate error page. This takes - care of logging the error, setting the reason phrase, and adding a - payload. This function is mutually exclusive with SetStatus and - other Serve* functions. - - ServeRedirect(code:int,location:str) - Instructs redbean to return the specified redirect code along with - the Location header set. This function is mutually exclusive with - SetStatus and other Serve* functions. - - SetLogLevel(level:int) - Sets logger verbosity. Reasonable values for level are kLogDebug > - kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. This is - reset at the end of the http request, so it can be used to disable - access log and message logging. - - VisualizeControlCodes(str) → str - Replaces C0 control codes with their UNICODE pictures - representation. This function also canonicalizes overlong - encodings. C1 control codes are replaced with a JavaScript-like - escape sequence. - - Underlong(str) → str - Canonicalizes overlong encodings. - - Crc32(initial:int,data:str) → int - Computes 32-bit CRC-32 used by zip/zlib/gzip/etc. - - Crc32c(initial:int,data:str) → int - Computes 32-bit Castagnoli Cyclic Redundancy Check. - - Md5(str) → str - Computes MD5 checksum, returning 16 bytes of binary. - - Sha1(str) → str - Computes SHA1 checksum, returning 20 bytes of binary. - - Sha224(str) → str - Computes SHA224 checksum, returning 28 bytes of binary. - - Sha256(str) → str - Computes SHA256 checksum, returning 32 bytes of binary. - - Sha384(str) → str - Computes SHA384 checksum, returning 48 bytes of binary. - - Sha512(str) → str - Computes SHA512 checksum, returning 64 bytes of binary. - - Bsf(x:int) → int - Returns position of first bit set. Passing 0 will raise an error. - Same as the Intel x86 instruction BSF. - - Bsr(x:int) → int - Returns binary logarithm of 𝑥. Passing 0 will raise an error. Same - as the Intel x86 instruction BSR. - - Popcnt(x:int) → int - Returns number of bits set in integer. - - Rdtsc() → int - Returns CPU timestamp counter. - - Lemur64() → int - Returns fastest pseudorandom non-cryptographic random number. This - linear congruential generator passes practrand and bigcrush. - - Rand64() → int - Returns nondeterministic pseudorandom non-cryptographic number. This - linear congruential generator passes practrand and bigcrush. This - generator is safe across fork(), threads, and signal handlers. - - Rdrand() → int - Returns 64-bit hardware random integer from RDRND instruction, with - automatic fallback to getrandom() if not available. - - Rdseed() → int - Returns 64-bit hardware random integer from RDSEED instruction, with - automatic fallback to RDRND and getrandom() if not available. + Write(data:str) + Appends data to HTTP response payload buffer. This is buffered + independently of headers. + + SetStatus(code:int[,reason:str]) + Starts an HTTP response, specifying the parameters on its first + line. reason is optional since redbean can fill in the appropriate + text for well-known magic numbers, e.g. 200, 404, etc. This method + will reset the response and is therefore mutually exclusive with + ServeAsset and other Serve* functions. If this function isn't + called, then the default behavior is to send 200 OK. + + SetHeader(name:str,value:str) + Appends HTTP header to response header buffer. name is + case-insensitive and restricted to non-space ASCII. value is a + UTF-8 string that must be encodable as ISO-8859-1. Leading and + trailing whitespace is trimmed automatically. Overlong characters + are canonicalized. C0 and C1 control codes are forbidden, with the + exception of tab. This function automatically calls SetStatus(200, + "OK") if a status has not yet been set. As SetStatus and Serve* + functions reset the response, SetHeader needs to be called after + SetStatus and Serve* functions are called. The header buffer is + independent of the payload buffer. Neither is written to the wire + until the Lua Server Page has finished executing. This function + disallows the setting of certain headers such as Content-Range and + Date, which are abstracted by the transport layer. In such cases, + consider calling ServeAsset. + + SetCookie(name:str,value:str[,options:table]) + Appends Set-Cookie HTTP header to the response header buffer. + Several Set-Cookie headers can be added to the same response. + __Host- and __Secure- prefixes are supported and may set or + overwrite some of the options (for example, specifying __Host- + prefix sets the Secure option to true, sets the path to "/", and + removes the Domain option). The following options can be used (their + lowercase equivalents are supported as well): + - Expires: sets the maximum lifetime of the cookie as an HTTP-date + timestamp. Can be specified as a Date in the RFC1123 (string) + format or as a UNIX timestamp (number of seconds). + - MaxAge: sets number of seconds until the cookie expires. A zero + or negative number will expire the cookie immediately. If both + Expires and MaxAge are set, MaxAge has precedence. + - Domain: sets the host to which the cookie will be sent. + - Path: sets the path that must be present in the request URL, or + the client will not send the Cookie header. + - Secure: (bool) requests the cookie to be only send to the + server when a request is made with the https: scheme. + - HttpOnly: (bool) forbids JavaScript from accessing the cookie. + - SameSite: (Strict, Lax, or None) controls whether a cookie is + sent with cross-origin requests, providing some protection + against cross-site request forgery attacks. + + GetParam(name:str) → value:str + Returns first value associated with name. name is handled in a + case-sensitive manner. This function checks Request-URL parameters + first. Then it checks application/x-www-form-urlencoded from the + message body, if it exists, which is common for HTML forms sending + POST requests. If a parameter is supplied matching name that has + no value, e.g. foo in ?foo&bar=value, then the returned value will + be nil, whereas for ?foo=&bar=value it would be "". To + differentiate between no-equal and absent, use the HasParam + function. The returned value is decoded from ISO-8859-1 (only in + the case of Request-URL) and we assume that percent-encoded + characters were supplied by the client as UTF-8 sequences, which + are returned exactly as the client supplied them, and may + therefore may contain overlong sequences, control codes, NUL + characters, and even numbers which have been banned by the IETF. + It is the responsibility of the caller to impose further + restrictions on validity, if they're desired. + + EscapeHtml(str) → str + Escapes HTML entities: The set of entities is &><"' which become + &><"'. This function is charset agnostic and + will not canonicalize overlong encodings. It is assumed that a + UTF-8 string will be supplied. See escapehtml.c. + + LaunchBrowser([path:str]) + Launches web browser on local machine with URL to this redbean + server. This function may be called from your /.init.lua. + + CategorizeIp(ip:uint32) → str + Returns a string describing an IP address. This is currently Class + A granular. It can tell you if traffic originated from private + networks, ARIN, APNIC, DOD, etc. + + DecodeBase64(ascii:str) → binary:str + Turns ASCII into binary, in a permissive way that ignores + characters outside the base64 alphabet, such as whitespace. See + decodebase64.c. + + DecodeLatin1(iso-8859-1:str) → utf-8:str + Turns ISO-8859-1 string into UTF-8. + + EncodeBase64(binary:str) → ascii:str + Turns binary into ASCII. This can be used to create HTML data: + URIs that do things like embed a PNG file in a web page. See + encodebase64.c. + + EncodeJson(value[,options:table]) → json:str + Turns passed Lua value into a JSON string. Tables with non-zero + length (as reported by `#`) are encoded as arrays with non-array + elements ignored. Empty tables are encoded as empty arrays. All + other tables are encoded as objects with numerical keys + converted to strings (so `{[3]=1}` is encoded as `{"3":1}`). + The following options can be used: + - 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: sets numeric format to be used, which can be 'g', + 'f', or 'a' [experimental api] + + EncodeLua(value[,options:table]) → json:str + Turns passed Lua value into a Lua string. The following options + can be used: + - 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. + + EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str + Turns UTF-8 into ISO-8859-1 string. + + EscapeFragment(str) → str + Escapes URL #fragment. The allowed characters are + -/?.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. + Please note that '& can still break HTML and that '() can still + break CSS URLs. This function is charset agnostic and will not + canonicalize overlong encodings. It is assumed that a UTF-8 string + will be supplied. See kescapefragment.c. + + EscapeHost(str) → str + Escapes URL host. See kescapeauthority.c + + EscapeLiteral(str) → str + Escapes JavaScript or JSON string literal content. The caller is + responsible for adding the surrounding quotation marks. This + implementation \uxxxx sequences for all non-ASCII sequences. HTML + entities are also encoded, so the output doesn't need EscapeHtml. + This function assumes UTF-8 input. Overlong encodings are + canonicalized. Invalid input sequences are assumed to be + ISO-8859-1. The output is UTF-16 since that's what JavaScript + uses. For example, some individual codepoints such as emoji + characters will encode as multiple \uxxxx sequences. Ints that are + impossible to encode as UTF-16 are substituted with the \xFFFD + replacement character. See escapejsstringliteral.c. + + EscapeParam(str) → str + Escapes URL parameter name or value. The allowed characters are + -.*_0-9A-Za-z and everything else gets %XX encoded. This function + is charset agnostic and will not canonicalize overlong encodings. + It is assumed that a UTF-8 string will be supplied. See + kescapeparam.c. + + EscapePass(str) → str + Escapes URL password. See kescapeauthority.c. + + EscapePath(str) → str + Escapes URL path. This is the same as EscapeSegment except slash + is allowed. The allowed characters are -.~_@:!$&'()*+,;=0-9A-Za-z/ + and everything else gets %XX encoded. Please note that '& can + still break HTML, so the output may need EscapeHtml too. Also note + that '() can still break CSS URLs. This function is charset + agnostic and will not canonicalize overlong encodings. It is + assumed that a UTF-8 string will be supplied. See kescapepath.c. + + EscapeSegment(str) → str + Escapes URL path segment. This is the same as EscapePath except + slash isn't allowed. The allowed characters are + -.~_@:!$&'()*+,;=0-9A-Za-z and everything else gets %XX encoded. + Please note that '& can still break HTML, so the output may need + EscapeHtml too. Also note that '() can still break CSS URLs. This + function is charset agnostic and will not canonicalize overlong + encodings. It is assumed that a UTF-8 string will be supplied. See + kescapesegment.c. + + EscapeUser(str) → str + Escapes URL username. See kescapeauthority.c. + + Fetch(url:str[,body:str|{method=value:str,body=value:str,...}]) + → status:int,{header:str=value:str,...},body:str + Sends an HTTP/HTTPS request to the specified URL. If only the URL is + provided, then a GET request is sent. If both URL and body parameters + are specified, then a POST request is sent. If any other method needs + to be specified (for example, PUT or DELETE), then passing a table as + the second value allows setting method and body values as well other + options: + - method (default = "GET"): sets the method to be used for the + request. The specified method is converted to uppercase. + - body (default = ""): sets the body value to be sent. + - followredirect (default = true): forces temporary and permanent + redirects to be followed. This behavior can be disabled by + passing `false`. + - maxredirects (default = 5): sets the number of allowed redirects + to minimize looping due to misconfigured servers. When the number + is exceeded, the result of the last redirect is returned. + When the redirect is being followed, the same method and body values + are being sent in all cases except when 303 status is returned. In + that case the method is set to GET and the body is removed before the + redirect is followed. Note that if these (method/body) values are + provided as table fields, they will be modified in place. + + FormatHttpDateTime(seconds:int) → rfc1123:str + Converts UNIX timestamp to an RFC1123 string that looks like this: + Mon, 29 Mar 2021 15:37:13 GMT. See formathttpdatetime.c. + + FormatIp(uint32) → str + Turns integer like 0x01020304 into a string like 1.2.3.4. See also + ParseIp for the inverse operation. + + GetAssetComment(path:str) → str + Returns comment text associated with asset in the ZIP central + directory. Also available as GetComment (deprecated). + + GetAssetMode(path:str) → int + Returns UNIX-style octal mode for ZIP asset (or local file if the + -D flag is used) + + GetAssetSize(path:str) → int + Returns byte size of uncompressed contents of ZIP asset (or local + file if the -D flag is used) + + GetBody() → str + Returns the request message body if present or an empty string. + Also available as GetPayload (deprecated). + + GetCookie(name:str) → str + Returns cookie value. + + GetCryptoHash(name:str,payload:str[,key:str]) → str + Returns value of the specified cryptographic hash function. If the + key is provided, then HMAC value of the same function is returned. + The name can be one of the following strings: MD5, SHA1, SHA224, + SHA256, SHA384, SHA512, and BLAKE2B256. + + GetRemoteAddr() → ip:uint32,port:uint16 + Returns client ip4 address and port, e.g. 0x01020304,31337 would + represent 1.2.3.4:31337. This is the same as GetClientAddr except + it will use the ip:port from the X-Forwarded-For header, only if + IsPrivateIp or IsLoopbackIp return true. When multiple addresses + are present in the header, the last/right-most address is used. + + GetClientAddr() → ip:uint32,port:uint16 + Returns client socket ip4 address and port, e.g. 0x01020304,31337 + would represent 1.2.3.4:31337. Please consider using GetRemoteAddr + instead, since the latter takes into consideration reverse proxy + scenarios. + + GetClientFd() → int + Returns file descriptor being used for client connection. + This is useful for scripts that want to use unix:fork(). + + IsClientUsingSsl() → bool + Returns true if client connection has begun being managed by + the MbedTLS security layer. This is an important thing to + consider if a script is taking control of GetClientFd() + + GetServerAddr() → ip:uint32,port:uint16 + Returns address to which listening server socket is bound, e.g. + 0x01020304,8080 would represent 1.2.3.4:8080. If -p 0 was supplied + as the listening port, then the port in this string will be + whatever number the operating system assigned. + + GetDate() → seconds:int + Returns date associated with request that's used to generate the + Date header, which is now, give or take a second. The returned + value is a UNIX timestamp. + + GetHeader(name:str) → value:str + Returns HTTP header. name is case-insensitive. The header value is + returned as a canonical UTF-8 string, with leading and trailing + whitespace trimmed, which was decoded from ISO-8859-1, which is + guaranteed to not have C0/C1 control sequences, with the exception + of the tab character. Leading and trailing whitespace is + automatically removed. In the event that the client suplies raw + UTF-8 in the HTTP message headers, the original UTF-8 sequence can + be losslessly restored by counter-intuitively recoding the + returned string back to Latin1. If the requested header is defined + by the RFCs as storing comma-separated values (e.g. Allow, + Accept-Encoding) and the field name occurs multiple times in the + message, then this function will fold those multiple entries into + a single string. + + GetHeaders() → {name:str=value:str,...} + Returns HTTP headers as dictionary mapping header key strings to + their UTF-8 decoded values. The ordering of headers from the + request message is not preserved. Whether or not the same key can + repeat depends on whether or not it's a standard header, and if + so, if it's one of the ones that the RFCs define as repeatable. + See khttprepeatable.c. Those headers will not be folded. Standard + headers which aren't on that list, will be overwritten with the + last-occurring one during parsing. Extended headers are always + passed through exactly as they're received. Please consider using + GetHeader API if possible since it does a better job abstracting + these issues. + + GetLogLevel() → int + Returns logger verbosity level. Likely return values are kLogDebug + > kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. + + GetHost() → str + Returns host associated with request. This will be the Host + header, if it's supplied. Otherwise it's the bind address. + + GetHostOs() → str + Returns string that describes the host OS. + + This can return: + + - `"LINUX"` + - `"METAL"` + - `"WINDOWS"` + - `"XNU"` + - `"NETBSD"` + - `"FREEBSD"` + - `"OPENBSD"` + + GetMonospaceWidth(str|char) → int + Returns monospace display width of string. This is useful for + fixed-width formatting. For example, CJK characters typically take + up two cells. This function takes into consideration combining + characters, which are discounted, as well as control codes and + ANSI escape sequences. + + GetMethod() → str + Returns HTTP method. Normally this will be GET, HEAD, or POST in + which case redbean normalizes this value to its uppercase form. + Anything else that the RFC classifies as a "token" string is + accepted too, which might contain characters like &". + + GetParams() → {{name:str[,value:str]},...} + Returns name=value parameters from Request-URL and + application/x-www-form-urlencoded message body in the order they + were received. This may contain duplicates. The inner array will + have either one or two items, depending on whether or not the + equals sign was used. + + GetPath() → str + Returns the Request-URL path. This is guaranteed to begin with + "/". It is further guaranteed that no "//" or "/." exists in the + path. The returned value is returned as a UTF-8 string which was + decoded from ISO-8859-1. We assume that percent-encoded characters + were supplied by the client as UTF-8 sequences, which are returned + exactly as the client supplied them, and may therefore may contain + overlong sequences, control codes, NUL characters, and even + numbers which have been banned by the IETF. redbean takes those + things into consideration when performing path safety checks. It + is the responsibility of the caller to impose further restrictions + on validity, if they're desired. + + GetEffectivePath() → str + Returns path as it was resolved by the routing algorithms, which + might contain the virtual host prepended if used. + + GetScheme() → str + Returns scheme from Request-URL, if any. + + GetSslIdentity() → str + Returns certificate subject or PSK identity from the current SSL + session. `nil` is returned for regular (non-SSL) connections. + + GetStatus() → int + Returns current status (as set by an earlier SetStatus call) or + `nil` if the status hasn't been set yet. + + GetTime() → seconds:number + Returns current time as a UNIX timestamp with 0.0001s precision. + + GetUrl() → str + Returns the effective Request-URL as an ASCII string, where + illegal characters or UTF-8 is guaranteed to be percent encoded, + and has been normalized to include either the Host or + X-Forwarded-Host headers, if they exist, and possibly a scheme too + if redbean is being used as an HTTP proxy server. In the future + this API might change to return an object instead. + + GetHttpVersion() → int + Returns the request HTTP protocol version, which can be 9 for + HTTP/0.9, 10 for HTTP/1.0, or 11 for HTTP/1.1. Also available + as GetVersion (deprecated). + + GetRandomBytes([length:int]) → str + Returns string with the specified number of random bytes (1..256). + If no length is specified, then a string of length 16 is returned. + + GetRedbeanVersion() → int + Returns the Redbean version in the format 0xMMmmpp, with major (MM), + minor (mm), and patch (pp) versions encoded. The version value 1.4 + would be represented as 0x010400. + + GetZipPaths([prefix:str]) → {path:str,...} + Returns paths of all assets in the zip central directory, prefixed + by a slash. If prefix parameter is provided, then only paths that + start with the prefix (case sensitive) are returned. + + HasParam(name:str) → bool + Returns true if parameter with name was supplied in either the + Request-URL or an application/x-www-form-urlencoded message body. + + HidePath(prefix:str) + Programs redbean / listing page to not display any paths beginning + with prefix. This function should only be called from /.init.lua. + + IsPublicIp(uint32) → bool + Returns true if IP address is not a private network (10.0.0.0/8, + 172.16.0.0/12, 192.168.0.0/16) and is not localhost (127.0.0.0/8). + Note: we intentionally regard TEST-NET IPs as public. + + IsPrivateIp(uint32) → bool + Returns true if IP address is part of a private network + (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). + + IsLoopbackIp(uint32) → bool + Returns true if IP address is part of the localhost network + (127.0.0.0/8). + + IsCompressed(path:str) → bool + Returns true if ZIP artifact at path is stored on disk using + DEFLATE compression. + + IndentLines(str[,int]) → str + Adds spaces to beginnings of multiline string. If the int + parameter is not supplied then 1 space will be added. + + LoadAsset(path:str) → str + Returns contents of file as string. The asset may be sourced from + either the zip (decompressed) or the local filesystem if the -D + flag was used. If slurping large file into memory is a concern, + then consider using ServeAsset which can serve directly off disk. + + StoreAsset(path:str,data:str[,mode:int]) + Stores asset to executable's ZIP central directory. This currently + happens in an append-only fashion and is still largely in the + proof-of-concept stages. Currently only supported on Linux, XNU, + and FreeBSD. + + Log(level:int,message:str) + Emits message string to log, if level is less than or equal to + GetLogLevel. If redbean is running in interactive mode, then this + will log to the console. If redbean is running as a daemon or the + -L LOGFILE flag is passed, then this will log to the file. + Reasonable values for level are kLogDebug > kLogVerbose > kLogInfo + > kLogWarn > kLogError > kLogFatal. The logger emits timestamps in + the local timezone with microsecond precision. If log entries are + emitted more frequently than once per second, then the log entry + will display a delta timestamp, showing how much time has elapsed + since the previous log entry. This behavior is useful for quickly + measuring how long various portions of your code take to execute. + + ParseHttpDateTime(rfc1123:str) → seconds:int + Converts RFC1123 string that looks like this: Mon, 29 Mar 2021 + 15:37:13 GMT to a UNIX timestamp. See parsehttpdatetime.c. + + ParseUrl(str) → URL + Parses URL, returning object having the following fields: scheme, + user, pass, host, port, path, params, fragment. This parser is + charset agnostic. Percent encoded bytes are decoded for all + fields. Returned values might contain things like NUL characters, + spaces, control codes, and non-canonical encodings. Absent can be + discerned from empty by checking if the pointer is set. There's no + failure condition for this routine. This is a permissive parser. + This doesn't normalize path segments like `.` or `..` so use + IsAcceptablePath() to check for those. No restrictions are imposed + beyond that which is strictly necessary for parsing. All the data + that is provided will be consumed to the one of the fields. Strict + conformance is enforced on some fields more than others, like + scheme, since it's the most non-deterministically defined field of + them all. Please note this is a URL parser, not a URI parser. + Which means we support everything everything the URI spec says we + should do except for the things we won't do, like tokenizing path + segments into an array and then nesting another array beneath each + of those for storing semicolon parameters. So this parser won't + make SIP easy. What it can do is parse HTTP URLs and most URIs + like data:opaque, better in fact than most things which claim to + be URI parsers. + + EncodeUrl(URL) → str + This function is the inverse of ParseUrl. The output will always + be correctly formatted. The exception is if illegal characters are + supplied in the scheme field, since there's no way of escaping + those. Opaque parts are escaped as though they were paths, since + many URI parsers won't understand things like an unescaped + question mark in path. + + ParseIp(str) → int + Converts IPv4 address string to integer, e.g. "1.2.3.4" → + 0x01020304, or returns -1 for invalid inputs. See also FormatIp + for the inverse operation. + + ProgramAddr(str) + Configures the address on which to listen. Can be used multiple + times to set more than one address. + + ProgramBrand(str) + Changes HTTP Server header, as well as the

title on the / + listing page. The brand string needs to be a UTF-8 value that's + encodable as ISO-8859-1. If the brand is changed to something + other than redbean, then the promotional links will be removed + from the listing page too. This function should only be called + from /.init.lua. + + ProgramCache(seconds:int) + Configures Cache-Control and Expires header generation for static + asset serving. A negative value will disable the headers. Zero + means don't cache. Greater than zero asks public proxies and + browsers to cache for a given number of seconds. This should only + be called from /.init.lua. + + ProgramCertificate(pem:str) + Same as the -C flag if called from .init.lua, e.g. + ProgramCertificate(LoadAsset("/.sign.crt")) for zip loading or + ProgramCertificate(Slurp("/etc/letsencrypt.lol/fullchain.pem")) for + local file system only. + + ProgramHeader(name:str,value:str) + Appends HTTP header to the header buffer for all responses (whereas + SetHeader only appends a header to the current response buffer). + name is case-insensitive and restricted to non-space ASCII. value + is a UTF-8 string that must be encodable as ISO-8859-1. Leading and + trailing whitespace is trimmed automatically. Overlong characters + are canonicalized. C0 and C1 control codes are forbidden, with the + exception of tab. The header buffer is independent of the payload + buffer. This function disallows the setting of certain headers such + as Content-Range and Date, which are abstracted by the transport + layer. + + ProgramPort(uint16) + Hard-codes the port number on which to listen, which can be any + number in the range 1..65535, or alternatively 0 to ask the + operating system to choose a port, which may be revealed later on + by GetServerAddr or the -z flag to stdout. + + ProgramPrivateKey(pem:str) + Same as the -K flag if called from .init.lua, e.g. + ProgramPrivateKey(LoadAsset("/.sign.key")) for zip loading or + ProgramPrivateKey(Slurp("/etc/letsencrypt/fullchain.pem")) for + local file system only. + + ProgramRedirect(code:int,src:str,location:str) + Configures fallback routing for paths which would otherwise return + 404 Not Found. If code is 0 then the path is rewritten internally + as an accelerated redirect. If code is 301, 302, 307, or 308 then + a redirect response will be sent to the client. This should only + be called from /.init.lua. + + ProgramSslTicketLifetime(seconds:int) + Defaults to 86400 (24 hours). This may be set to ≤0 to disable + SSL tickets. It's a good idea to use these since it increases + handshake performance 10x and eliminates a network round trip. + + EvadeDragnetSurveillance(bool) + If this option is programmed then redbean will not transmit a + Server Name Indicator (SNI) when performing Fetch() requests. + + ProgramSslPresharedKey(key:str,identity:str) + This function can be used to enable the PSK ciphersuites + which simplify SSL and enhance its performance in controlled + environments. `key` may contain 1..32 bytes of random binary + data and identity is usually a short plaintext string. The + first time this function is called, the preshared key will + be added to both the client and the server SSL configs. If + it's called multiple times, then the remaining keys will be + added to the server, which is useful if you want to assign + separate keys to each client, each of which needs a separate + identity too. If this function is called multiple times with + the same identity string, then the latter call will overwrite + the prior. If a preshared key is supplied and no certificates + or key-signing-keys are programmed, then redbean won't bother + auto-generating any serving certificates and will instead use + only PSK ciphersuites. + + ProgramSslCiphersuite(name:str) + See https://redbean.dev/ for further details. + + IsDaemon() → bool + Returns true if -d flag was passed to redbean. + + ProgramUid(int) + Same as the -U flag if called from .init.lua for setuid() + + ProgramGid(int) + Same as the -G flag if called from .init.lua for setgid() + + ProgramDirectory(str) + Same as the -D flag if called from .init.lua for overlaying local + file system directories. This may be called multiple times. The + first directory programmed is preferred. These currently do not + show up in the index page listing. + + ProgramLogMessages(bool) + Same as the -m flag if called from .init.lua for logging message + headers only. + + ProgramLogBodies(bool) + Same as the -b flag if called from .init.lua for logging message + bodies as part of POST / PUT / etc. requests. + + ProgramLogPath(str) + Same as the -L flag if called from .init.lua for setting the log + file path on the local file system. It's created if it doesn't + exist. This is called before de-escalating the uesr / group id. + The file is opened in append only mode. If the disk runs out of + space then redbean will truncate the log file if has access to + change the log file after daemonizing. + + ProgramPidPath(str) + Same as the -P flag if called from .init.lua for setting the pid + file path on the local file system. It's useful for reloading + daemonized redbean using `kill -HUP $(cat /var/run/redbean.pid)` + or terminating redbean with `kill $(cat /var/run/redbean.pid)` + which will gracefully terminate all clients. Sending the TERM + signal twice will cause a forceful shutdown, which might make + someone with a slow internet connection who's downloading big + files unhappy. + + ProgramUniprocess([bool]) → bool + Same as the -u flag if called from .init.lua. Can be used to + configure the uniprocess mode. The current value is returned. + + Slurp(filename:str) → str + Reads file data from local file system. + + Sleep(seconds:number) + Sleeps the specified number of seconds (can be fractional). The + smallest interval is a microsecond. + + Route([host:str,[path:str]]) + Instructs redbean to follow the normal HTTP serving path. This + function is useful when writing an OnHttpRequest handler, since + that overrides the serving path entirely. So if the handler + decides it doesn't want to do anything, it can simply call this + function, to hand over control back to the redbean core. By + default, the host and path arguments are supplied from the + resolved GetUrl value. This handler always resolves, since it will + generate a 404 Not Found response if redbean couldn't find an + appropriate endpoint. + + RouteHost([host:str,[path:str]]) → bool + This is the same as Route, except it only implements the subset of + request routing needed for serving virtual-hosted assets, where + redbean tries to prefix the path with the hostname when looking up + a file. This function returns true if the request was resolved. If + it was resolved, then your OnHttpRequest request handler can still + set additional headers. + + RoutePath([path:str]) → bool + This is the same as Route, except it only implements the subset of + request routing needed for serving assets. This function returns + true if the request was resolved. If it was resolved, then your + OnHttpRequest request handler can still set additional headers. + Note that the asset needs to have "read other" permissions; + otherwise this function logs a warning and returns 403 Forbidden. + If this is undesirable, use GetAssetMode and ServeAsset to bypass + the check. + + ServeAsset(path:str) + Instructs redbean to serve static asset at path. This function + causes what would normally happen outside a dynamic handler to + happen. The asset can be sourced from either the zip or local + filesystem if -D is used. This function is mutually exclusive with + SetStatus and other Serve* functions. + + ServeError(code:int[,reason:str]) + Instructs redbean to serve a boilerplate error page. This takes + care of logging the error, setting the reason phrase, and adding a + payload. This function is mutually exclusive with SetStatus and + other Serve* functions. + + ServeRedirect(code:int,location:str) + Instructs redbean to return the specified redirect code along with + the Location header set. This function is mutually exclusive with + SetStatus and other Serve* functions. + + SetLogLevel(level:int) + Sets logger verbosity. Reasonable values for level are kLogDebug > + kLogVerbose > kLogInfo > kLogWarn > kLogError > kLogFatal. This is + reset at the end of the http request, so it can be used to disable + access log and message logging. + + VisualizeControlCodes(str) → str + Replaces C0 control codes with their UNICODE pictures + representation. This function also canonicalizes overlong + encodings. C1 control codes are replaced with a JavaScript-like + escape sequence. + + Underlong(str) → str + Canonicalizes overlong encodings. + + Crc32(initial:int,data:str) → int + Computes 32-bit CRC-32 used by zip/zlib/gzip/etc. + + Crc32c(initial:int,data:str) → int + Computes 32-bit Castagnoli Cyclic Redundancy Check. + + Md5(str) → str + Computes MD5 checksum, returning 16 bytes of binary. + + Sha1(str) → str + Computes SHA1 checksum, returning 20 bytes of binary. + + Sha224(str) → str + Computes SHA224 checksum, returning 28 bytes of binary. + + Sha256(str) → str + Computes SHA256 checksum, returning 32 bytes of binary. + + Sha384(str) → str + Computes SHA384 checksum, returning 48 bytes of binary. + + Sha512(str) → str + Computes SHA512 checksum, returning 64 bytes of binary. + + Bsf(x:int) → int + Returns position of first bit set. Passing 0 will raise an error. + Same as the Intel x86 instruction BSF. + + Bsr(x:int) → int + Returns binary logarithm of 𝑥. Passing 0 will raise an error. Same + as the Intel x86 instruction BSR. + + Popcnt(x:int) → int + Returns number of bits set in integer. + + Rdtsc() → int + Returns CPU timestamp counter. - GetCpuCore() → int - Returns 0-indexed CPU core on which process is currently scheduled. + Lemur64() → int + Returns fastest pseudorandom non-cryptographic random number. This + linear congruential generator passes practrand and bigcrush. - GetCpuNode() → int - Returns 0-indexed NUMA node on which process is currently scheduled. + Rand64() → int + Returns nondeterministic pseudorandom non-cryptographic number. This + linear congruential generator passes practrand and bigcrush. This + generator is safe across fork(), threads, and signal handlers. - Decimate(data) → int - Shrinks byte buffer in half using John Costella's magic kernel. - This downscales data 2x using an eight-tap convolution, e.g. + Rdrand() → int + Returns 64-bit hardware random integer from RDRND instruction, with + automatic fallback to getrandom() if not available. - >: Decimate(b'\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00') - b'\\xff\\x00\\xff\\x00\\xff\\x00' + Rdseed() → int + Returns 64-bit hardware random integer from RDSEED instruction, with + automatic fallback to RDRND and getrandom() if not available. - This is very fast if SSSE3 is available (Intel 2004+ / AMD 2011+). + GetCpuCount() → int + Returns CPU core count or 0 if it couldn't be determined. - MeasureEntropy(data) → float - Returns Shannon entropy of array. This gives you an idea of - the density of information. Cryptographic random should be in - the ballpark of 7.9 whereas plaintext will be more like 4.5. + GetCpuCore() → int + Returns 0-indexed CPU core on which process is currently scheduled. + GetCpuNode() → int + Returns 0-indexed NUMA node on which process is currently scheduled. + + Decimate(data) → int + Shrinks byte buffer in half using John Costella's magic kernel. + This downscales data 2x using an eight-tap convolution, e.g. + + >: Decimate(b'\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00') + b'\\xff\\x00\\xff\\x00\\xff\\x00' + + This is very fast if SSSE3 is available (Intel 2004+ / AMD 2011+). + + MeasureEntropy(data) → float + Returns Shannon entropy of array. This gives you an idea of + the density of information. Cryptographic random should be in + the ballpark of 7.9 whereas plaintext will be more like 4.5. + + Benchmark(func[, count[, maxattempts]]) + └─→ nanos:real, ticks:int, overhead-ticks:int, tries:int + + Performs microbenchmark. + + The first value returned is the average number of nanoseconds that + `func` needed to execute. Nanoseconds are computed from RDTSC tick + counts, using an approximation that's measured beforehand with the + unix.clock_gettime() function. + + The `ticks` result is the canonical average number of clock ticks. + + This subroutine will subtract whatever the overhead happens to be + for benchmarking a function that does nothing. This overhead value + will be reported in the result. + + `tries` indicates if your microbenchmark needed to be repeated, + possibly because your system is under load and the benchmark was + preempted by the operating system, or moved to a different core. + + oct(int) + └─→ str + + Formats string as octal integer literal string. If the provided + value is zero, the result will be `"0"`. Otherwise the resulting + value will be the zero-prefixed octal string. The result is + currently modulo 2^64. Negative numbers are converted to unsigned. + + hex(int) + └─→ str + + Formats string as hexadecimal integer literal string. If the + provided value is zero, the result will be `"0"`. Otherwise the + resulting value will be the `"0x"`-prefixed hex string. The result + is currently modulo 2^64. Negative numbers are converted to + unsigned. + + bin(int) + └─→ str + + Formats string as binary integer literal string. If the provided + value is zero, the result will be `"0"`. Otherwise the resulting + value will be the `"0b"`-prefixed binary str. The result is + currently modulo 2^64. Negative numbers are converted to unsigned. + + +──────────────────────────────────────────────────────────────────────────────── + +CONSTANTS + + kLogDebug + Integer for debug logging level. See Log. + + kLogVerbose + Integer for verbose logging level, which is less than kLogDebug. + + kLogInfo + Integer for info logging level, which is less than kLogVerbose. + + kLogWarn + Integer for warn logging level, which is less than kLogVerbose. + + kLogError + Integer for error logging level, which is less than kLogWarn. + + kLogFatal + Integer for fatal logging level, which is less than kLogError. + Logging anything at this level will result in a backtrace and + process exit. + +──────────────────────────────────────────────────────────────────────────────── + LSQLITE3 MODULE - Please refer to the LuaSQLite3 Documentation. + Please refer to the LuaSQLite3 Documentation. - For example, you could put the following in your /.init.lua file: + For example, you could put the following in your /.init.lua file: - sqlite3 = require "lsqlite3" - db = sqlite3.open_memory() - db:exec[[ - CREATE TABLE test ( - id INTEGER PRIMARY KEY, - content TEXT - ); - INSERT INTO test (content) VALUES ('Hello World'); - INSERT INTO test (content) VALUES ('Hello Lua'); - INSERT INTO test (content) VALUES ('Hello Sqlite3'); - ]] + sqlite3 = require "lsqlite3" + db = sqlite3.open_memory() + db:exec[[ + CREATE TABLE test ( + id INTEGER PRIMARY KEY, + content TEXT + ); + INSERT INTO test (content) VALUES ('Hello World'); + INSERT INTO test (content) VALUES ('Hello Lua'); + INSERT INTO test (content) VALUES ('Hello Sqlite3'); + ]] - Then, your Lua server pages or OnHttpRequest handler may perform SQL - queries by accessing the db global. The performance is good too, at about - 400k qps. + Then, your Lua server pages or OnHttpRequest handler may perform SQL + queries by accessing the db global. The performance is good too, at about + 400k qps. - for row in db:nrows("SELECT * FROM test") do - Write(row.id.." "..row.content.."
") - end + for row in db:nrows("SELECT * FROM test") do + Write(row.id.." "..row.content.."
") + end - redbean supports a subset of what's defined in the upstream LuaSQLite3 - project. Most of the unsupported APIs relate to pointers and database - notification hooks. + redbean supports a subset of what's defined in the upstream LuaSQLite3 + project. Most of the unsupported APIs relate to pointers and database + notification hooks. - redbean also currently disables SQLite features which don't make sense for - production serving, such as ALTER, VACUUM, ANALYZE, etc. For that reason - we provide an APE build of the SQLite shell which you can use to - administrate your redbean database. See the sqlite3.com download above. + redbean also currently disables SQLite features which don't make sense for + production serving, such as ALTER, VACUUM, ANALYZE, etc. For that reason + we provide an APE build of the SQLite shell which you can use to + administrate your redbean database. See the sqlite3.com download above. +──────────────────────────────────────────────────────────────────────────────── + RE MODULE - This module exposes an API for POSIX regular expressions which enable you - to validate input, search for substrings, extract pieces of strings, etc. - Here's a usage example: + This module exposes an API for POSIX regular expressions which enable you + to validate input, search for substrings, extract pieces of strings, etc. + Here's a usage example: - # Example IPv4 Address Regular Expression (see also ParseIP) - p = re.compile([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]]) - m,a,b,c,d = p:search(𝑠) - if m then - print("ok", tonumber(a), tonumber(b), tonumber(c), tonumber(d)) - else - print("not ok") - end + # Example IPv4 Address Regular Expression (see also ParseIP) + p = re.compile([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]]) + m,a,b,c,d = p:search(𝑠) + if m then + print("ok", tonumber(a), tonumber(b), tonumber(c), tonumber(d)) + else + print("not ok") + end - re.search(regex:str,text:str[,flags:int]) → [match[,group_1,...]] - Shortcut for re.compile plus regex_t*:search. + re.search(regex:str,text:str[,flags:int]) → [match[,group_1,...]] + Shortcut for re.compile plus regex_t*:search. - re.compile(regex:str[,flags:int]) → regex_t* - Compiles regular expression, using the POSIX extended syntax. This - has an O(2^𝑛) cost, so it's a good idea to do this from your - /.init.lua file. Flags may contain re.BASIC, re.ICASE, re.NOSUB, - and/or re.NEWLINE. See also regcomp() from libc. + re.compile(regex:str[,flags:int]) → regex_t* + Compiles regular expression, using the POSIX extended syntax. This + has an O(2^𝑛) cost, so it's a good idea to do this from your + /.init.lua file. Flags may contain re.BASIC, re.ICASE, re.NOSUB, + and/or re.NEWLINE. See also regcomp() from libc. - regex_t*:search(text:str[,flags:int]) → [match[,group_1,...]] - Executes regular expression. This has an O(𝑛) cost. This returns - nothing (nil) if the pattern doesn't match anything. Otherwise it - pushes the matched substring and any parenthesis-captured values - too. Flags may contain re.NOTBOL or re.NOTEOL to indicate whether - or not text should be considered at the start and/or end of a - line. + regex_t*:search(text:str[,flags:int]) → [match[,group_1,...]] + Executes regular expression. This has an O(𝑛) cost. This returns + nothing (nil) if the pattern doesn't match anything. Otherwise it + pushes the matched substring and any parenthesis-captured values + too. Flags may contain re.NOTBOL or re.NOTEOL to indicate whether + or not text should be considered at the start and/or end of a + line. - re.BASIC - Use this flag if you prefer the default POSIX regex syntax. We use - extended regex notation by default. For example, an extended - regular expression for matching an IP address might look like - ([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*) whereas with basic syntax - it would look like \([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\). - This flag may only be used with re.compile and re.search. + re.BASIC + Use this flag if you prefer the default POSIX regex syntax. We use + extended regex notation by default. For example, an extended + regular expression for matching an IP address might look like + ([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*) whereas with basic syntax + it would look like \([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\). + This flag may only be used with re.compile and re.search. - re.ICASE - Use this flag to make your pattern case ASCII case-insensitive. - This means [a-z] will mean the same thing as [A-Za-z]. This flag - may only be used with re.compile and re.search. + re.ICASE + Use this flag to make your pattern case ASCII case-insensitive. + This means [a-z] will mean the same thing as [A-Za-z]. This flag + may only be used with re.compile and re.search. - re.NEWLINE - Use this flag to change the handling of NEWLINE (\x0a) characters. - When this flag is set, (1) a NEWLINE shall not be matched by a "." - or any form of a non-matching list, (2) a "^" shall match the - zero-length string immediately after a NEWLINE (regardless of - re.NOTBOL), and (3) a "$" shall match the zero-length string - immediately before a NEWLINE (regardless of re.NOTEOL). + re.NEWLINE + Use this flag to change the handling of NEWLINE (\x0a) characters. + When this flag is set, (1) a NEWLINE shall not be matched by a "." + or any form of a non-matching list, (2) a "^" shall match the + zero-length string immediately after a NEWLINE (regardless of + re.NOTBOL), and (3) a "$" shall match the zero-length string + immediately before a NEWLINE (regardless of re.NOTEOL). - re.NOSUB - Causes re.search to only report success and failure. This is - reported via the API by returning empty string for success. This - flag may only be used with re.compile and re.search. + re.NOSUB + Causes re.search to only report success and failure. This is + reported via the API by returning empty string for success. This + flag may only be used with re.compile and re.search. - re.NOTBOL - The first character of the string pointed to by string is not the - beginning of the line. This flag may only be used with re.search - and regex_t*:search. + re.NOTBOL + The first character of the string pointed to by string is not the + beginning of the line. This flag may only be used with re.search + and regex_t*:search. - re.NOTEOL - The last character of the string pointed to by string is not the - end of the line. This flag may only be used with re.search and - regex_t*:search. + re.NOTEOL + The last character of the string pointed to by string is not the + end of the line. This flag may only be used with re.search and + regex_t*:search. +──────────────────────────────────────────────────────────────────────────────── + MAXMIND MODULE - This module may be used to get city/country/asn/etc from IPs, e.g. + This module may be used to get city/country/asn/etc from IPs, e.g. - -- .init.lua - maxmind = require "maxmind" - asndb = maxmind.open('/usr/local/share/maxmind/GeoLite2-ASN.mmdb') + -- .init.lua + maxmind = require 'maxmind' + asndb = maxmind.open('/usr/local/share/maxmind/GeoLite2-ASN.mmdb') - -- request handler - as = asndb:lookup(GetRemoteAddr()) - if as then - asnum = as:get("autonomous_system_number") - asorg = as:get("autonomous_system_organization") - Write(EscapeHtml(asnum)) - Write(' ') - Write(EscapeHtml(asorg)) - end + -- request handler + as = asndb:lookup(GetRemoteAddr()) + if as then + asnum = as:get('autonomous_system_number') + asorg = as:get('autonomous_system_organization') + Write(EscapeHtml(asnum)) + Write(' ') + Write(EscapeHtml(asorg)) + end - For further details, please see maxmind.lua in redbean-demo.com. + For further details, please see maxmind.lua in redbean-demo.com. + +──────────────────────────────────────────────────────────────────────────────── + 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 calls follow: + This module exposes the low-level System Five system call interface. + This module works on all supported platforms, including Windows NT. - rc, errno = unix.foo(...) - - Where the underlying call returning an rc of -1 will map to a Lua - value of nil, and if the system call doesn't return an error number - then errno will be nil and rc will be non-nil. To see which errnos are - possible for which system calls, please see the comprehensive index at - the bottom of this section. - - Your system calls are: - - unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int] - - Reads from file descriptor. - - unix.write(fd:int, data[, offset]) → rc: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[, dirfd:int]]) + ├─→ fd:int + └─→ nil, 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(unix.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 + - `O_CREAT` create file if it doesn't exist - `O_TRUNC` automatic ftruncate(fd,0) if exists - - `O_CLOEXEC`: automatic close() upon execve() - - `O_EXCL`: exclusive access (see below) - - `O_APPEND`: open file for append only + - `O_CLOEXEC` automatic close() upon execve() + - `O_EXCL` exclusive access (see below) + - `O_APPEND` open file for append only + - `O_NONBLOCK` asks read/write to fail with EAGAIN rather than block - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD) - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT) - `O_TMPFILE` try to make temp more secure (Linux and Windows only) @@ -1323,6 +1512,7 @@ UNIX MODULE - `O_VERIFY` it's complicated (zero on non-FreeBSD) - `O_SHLOCK` it's complicated (zero on non-BSD) - `O_EXLOCK` it's complicated (zero on non-BSD) + - `O_NOATIME` don't record access time (zero on non-Linux) - `O_RANDOM` hint random access intent (zero on non-Windows) - `O_SEQUENTIAL` hint sequential access intent (zero on non-Windows) - `O_COMPRESSED` ask fs to abstract compression (zero on non-Windows) @@ -1343,39 +1533,116 @@ UNIX MODULE already. If it does exist then `nil` is returned along with `errno` set to `EEXIST`. - unix.close(fd:int) → rc:int[, errno:int] + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + Returns `ENOENT` if `path` doesn't exist. + + Returns `ENOTDIR` if `path` contained a directory component that + wasn't a directory. + + unix.close(fd:int) + ├─→ true + └─→ nil, unix.Errno Closes file descriptor. - unix.exit([exitcode]) → ⊥ + unix.read(fd:int[, bufsiz:str[, offset:int]]) + ├─→ data:str + └─→ nil, unix.Errno + + Reads from file descriptor. + + unix.write(fd:int, data:str[, offset:int]) + ├─→ wrotebytes:int + └─→ nil, unix.Errno + + Writes to file descriptor. + + unix.exit([exitcode:int]) + └─→ ⊥ Invokes `_Exit(exitcode)` on the process. This will immediately halt the current process. Memory will be freed. File descriptors will be closed. Any open connections it owns will be reset. This function never returns. - unix.fork() → childpid|0:int[, errno:int] + unix.environ() + └─→ {str,...} - Creates a new process mitosis style. This returns twice. The - parent process gets the nonzero pid. The child gets zero. + Returns raw environment variables. - unix.commandv(prog:str) → path:str[, errno:int] + This allocates and constructs the C/C++ `environ` variable as a Lua + table consisting of string keys and string values. - 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. + This data structure preserves casing. On Windows NT, by convention, + environment variable keys are treated in a case-insensitive way. It + is the responsibility of the caller to consider this. - unix.execve(prog:str[, args:List<*>, env:Map]) → errno:int + This data structure preserves valueless variables. It's possible on + both UNIX and Windows to have an environment variable without an + equals, even though it's unusual. + + This data structure preserves duplicates. For example, on Windows, + there's some irregular uses of environment variables such as how the + command prompt inserts multiple environment variables with empty + string as keys, for its internal bookkeeping. + + unix.fork() + ├─┬─→ 0 + │ └─→ childpid:int + └─→ nil, unix.Errno + + Creates a new process mitosis style. + + This system call returns twice. The parent process gets the nonzero + pid. The child gets zero. + + Here's some benchmarks for fork() performance across platforms: + + Linux 5.4 fork l: 97,200𝑐 31,395𝑛𝑠 [metal] + FreeBSD 12 fork l: 236,089𝑐 78,841𝑛𝑠 [vmware] + Darwin 20.6 fork l: 295,325𝑐 81,738𝑛𝑠 [metal] + NetBSD 9 fork l: 5,832,027𝑐 1,947,899𝑛𝑠 [vmware] + OpenBSD 6.8 fork l: 13,241,940𝑐 4,422,103𝑛𝑠 [vmware] + Windows10 fork l: 18,802,239𝑐 6,360,271𝑛𝑠 [metal] + + One of the benefits of using fork() is it creates an isolation + barrier between the different parts of your app. This can lead to + enhanced reliability and security. For example, redbean uses fork so + it can wipe your ssl keys from memory before handing over control to + request handlers that process untrusted input. It also ensures that + if your Lua app crashes, it won't take down the server as a whole. + Hence it should come as no surprise that fork() would go slower on + operating systems that have more security features. So depending on + your use case, you can choose the operating system that suits you. + + unix.commandv(prog:str) + ├─→ path:str + └─→ nil, unix.Errno + + Performs `$PATH` lookup of executable. + + 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<*>]) + └─→ nil, unix.Errno Exits current process, replacing it with a new instance of the specified program. `prog` needs to be an absolute path, see commandv(). `env` defaults to to the current `environ`. Here's a basic usage example: - unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {PATH="/bin"}) + unix.execve("/bin/ls", {"/bin/ls", "-hal"}, {"PATH=/bin"}) unix.exit(127) `prog` needs to be the resolved pathname of your executable. You @@ -1385,19 +1652,25 @@ UNIX MODULE should be `prog`. Values are coerced to strings. This parameter defaults to `{prog}`. - `env` is a key/value table. Keys that aren't strings are - ignored. Values are coerced to strings. This parameter defaults - to environ() in a way that avoids copying. + `env` is a string list table. Values are coerced to strings. No + ordering requirement is imposed. By convention, each string has its + key and value divided by an equals sign without spaces. If this + paremeter is not specified, it'll default to the C/C++ `environ` + variable which is inherited from the shell that launched redbean. + It's the responsibility of the user to supply a sanitized environ + when spawning untrusted processes. - execve() function is normally called after fork() returns 0. If - that isn't the case, then your redbean worker will be destroyed. + execve() is normally called after fork() returns 0. If that isn't + the case, then your redbean worker will be destroyed. This function never returns on success. `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 + └─→ nil, unix.Errno Duplicates file descriptor. @@ -1408,20 +1681,31 @@ UNIX MODULE `flags` can have `O_CLOEXEC` which means the returned file descriptors will be automatically closed upon execve(). - unix.pipe([flags]) → reader, writer, errno:int + unix.pipe([flags:int]) + ├─→ reader:int, writer:int + └─→ nil, unix.Errno Creates fifo which enables communication between processes. - Returns two file descriptors: one for reading and one for - writing. `flags` can have `O_CLOEXEC`. On error, `reader` and - `writer` will be `nil` and `errno` will be set to non-nil. + + `flags` can have any of + + - `O_CLOEXEC`: Automatically close file descriptor upon execve() + + - `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking + + - `O_DIRECT`: Enable packet mode w/ atomic reads and writes, so long + as they're no larger than `PIPE_BUF` (guaranteed to be 512+ bytes) + with support limited to Linux, Windows NT, FreeBSD, and NetBSD. + + Returns two file descriptors: one for reading and one for writing. Here's an example of how pipe(), fork(), dup(), etc. may be used to serve an HTTP response containing the output of a subprocess. local unix = require "unix" - ls = unix.commandv("ls") - reader, writer = unix.pipe() - if unix.fork() == 0 then + ls = assert(unix.commandv("ls")) + reader, writer = assert(unix.pipe()) + if assert(unix.fork()) == 0 then unix.close(1) unix.dup(writer) unix.close(writer) @@ -1432,19 +1716,25 @@ UNIX MODULE unix.close(writer) SetHeader('Content-Type', 'text/plain') while true do - data = unix.read(reader) - if data ~= "" then - Write(data) - else + data, err = unix.read(reader) + if data then + if data ~= "" then + Write(data) + else + break + end + elseif err:errno() ~= EINTR then + Log(kLogWarn, tostring(err)) break end end - unix.close(reader) - unix.wait() + assert(unix.close(reader)) + assert(unix.wait()) end - unix.wait([pid:int, options:int]) - → pid:int, wstatus:int, nil, errno:int + unix.wait([pid:int[, options:int]]) + ├─→ pid:int, wstatus:int, unix.Rusage + └─→ nil, unix.Errno Waits for subprocess to terminate. @@ -1467,97 +1757,246 @@ UNIX MODULE -- wait for zombies -- traditional technique for SIGCHLD handlers while true do - pid, wstatus, errno = unix.wait(-1, unix.WNOHANG) + pid, status = unix.wait(-1, unix.WNOHANG) if pid then - if unix.WIFEXITED(wstatus) then + if unix.WIFEXITED(status) then print('child', pid, 'exited with', - unix.WEXITSTATUS(wstatus)) - elseif unix.WIFSIGNALED(wstatus) then + unix.WEXITSTATUS(status)) + elseif unix.WIFSIGNALED(status) then print('child', pid, 'crashed with', - unix.strsignal(unix.WTERMSIG(wstatus))) + unix.strsignal(unix.WTERMSIG(status))) end - elseif errno == unix.ECHILD then - print('no more zombies') + elseif status:errno() == unix.ECHILD then + Log(kLogDebug, 'no more zombies') break - elseif errno == unix.ECHILD then - print('wait failed', unix.strerror(errno)) + else + Log(kLogWarn, tostring(err)) break end end - unix.getpid() → pid + unix.WIFEXITED(wstatus:int) + └─→ bool + + Returns true if process exited cleanly. + + unix.WEXITSTATUS(wstatus:int) + └─→ exitcode:uint8 + + Returns code passed to exit() assuming `WIFEXITED(wstatus)` is true. + + unix.WIFSIGNALED(wstatus:int) + └─→ bool + + Returns true if process terminated due to a signal. + + unix.WTERMSIG(wstatus:int) + └─→ sig:uint8 + + Returns signal that caused process to terminate assuming + `WIFSIGNALED(wstatus)` is true. + + unix.getpid() + └─→ pid:int Returns process id of current process. - unix.getppid() → pid + This function does not fail. + + unix.getppid() + └─→ pid:int Returns process id of parent process. - unix.kill(pid, sig) → rc:int[, errno:int] + This function does not fail. - Returns process id of current process. + unix.kill(pid:int, sig:int) + ├─→ true + └─→ nil, unix.Errno - unix.raise(sig) → rc:int[, errno:int] + Sends signal to process(es). + + The impact of this action can be terminating the process, or + interrupting it to request something happen. + + `pid` can be: + + - `pid > 0` signals one process by id + - `== 0` signals all processes in current process group + - `-1` signals all processes possible (except init) + - `< -1` signals all processes in -pid process group + + `sig` can be: + + - `0` checks both if pid exists and we can signal it + - `SIGINT` sends ctrl-c keyboard interrupt + - `SIGQUIT` sends backtrace and exit signal + - `SIGTERM` sends shutdown signal + - etc. + + Windows NT only supports the kill() signals required by the ANSI C89 + standard, which are `SIGINT` and `SIGQUIT`. All other signals on the + Windows platform that are sent to another process via kill() will be + treated like `SIGKILL`. + + unix.raise(sig:int) + ├─→ rc:int + └─→ nil, unix.Errno Triggers signal in current process. + This is pretty much the same as `kill(getpid(), sig)`. - unix.access(path:str, how) → rc:int[, errno:int] + unix.access(path:str, how:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, 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. + 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) → rc:int[, errno:int] + `flags` may have any of: - Makes directory. `mode` should be octal, e.g. `0755`. + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. - unix.chdir(path:str) → rc:int[, errno:int] + unix.mkdir(path:str[, mode:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno + + Makes directory. + + `path` is the path of the directory you wish to create. + + `mode` is octal permission bits, e.g. `0755`. + + Fails with `EEXIST` if `path` already exists, whether it be a + directory or a file. + + Fails with `ENOENT` if the parent directory of the directory you + want to create doesn't exist. For making `a/really/long/path/` + consider using makedirs() instead. + + Fails with `ENOTDIR` if a parent directory component existed that + wasn't a directory. + + Fails with `EACCES` if the parent directory doesn't grant write + permission to the current user. + + Fails with `ENAMETOOLONG` if the path is too long. + + unix.makedirs(path:str[, mode:int]) + ├─→ true + └─→ nil, unix.Errno + + Makes directories. + + `path` is the path of the directory you wish to create. + + `mode` is octal permission bits, e.g. `0755`. + + Unlike mkdir() this convenience wrapper will automatically create + parent parent directories as needed. + + unix.chdir(path:str) + ├─→ true + └─→ nil, unix.Errno Changes current directory to `path`. - unix.unlink(path:str) → rc:int[, errno:int] + unix.unlink(path:str[, dirfd:int]) + ├─→ true + └─→ nil, unix.Errno Removes file at `path`. - unix.rmdir(path:str) → rc:int[, errno:int] + If `path` refers to a symbolic link, the link is removed. + + Returns `EISDIR` if `path` refers to a directory. See rmdir(). + + unix.rmdir(path:str[, dirfd:int]) + ├─→ true + └─→ nil, unix.Errno Removes empty directory at `path`. - unix.chroot(path:str) → rc:int[, errno:int] + Returns `ENOTDIR` if `path` isn't a directory, or a path component + in `path` exists yet wasn't a directory. - Changes root directory. Raises `ENOSYS` on Windows. + unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int]) + ├─→ true + └─→ nil, unix.Errno - unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] + Renames file or directory. - Renames file. - - unix.link(existingpath:str, newpath:str) → rc:int[, errno:int] + unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]]) + ├─→ true + └─→ nil, unix.Errno Creates hard link, so your underlying inode has two names. - unix.symlink(target:str, linkpath:str) → rc:int[, errno:int] + unix.symlink(target:str, linkpath:str[, newdirfd:int]) + ├─→ true + └─→ nil, unix.Errno - Creates soft link, or a symbolic link. + Creates symbolic link. - unix.realpath(filename:str) → abspath:str[, errno:int] + On Windows NT a symbolic link is called a "reparse point" and can + only be created from an administrator account. Your redbean will + automatically request the appropriate permissions. + + unix.readlink(path:str[, dirfd:int]) + ├─→ content:str + └─→ nil, unix.Errno + + Reads contents of symbolic link. + + Note that broken links are supported on all platforms. A symbolic + link can contain just about anything. It's important to not assume + that `content` will be a valid filename. + + On Windows NT, this function transliterates `\` to `/` and + furthermore prefixes `//?/` to WIN32 DOS-style absolute paths, + thereby assisting with simple absolute filename checks in addition + to enabling one to exceed the traditional 260 character limit. + + unix.realpath(path:str) + ├─→ path:str + └─→ nil, unix.Errno Returns absolute path of filename, with `.` and `..` components removed, and symlinks will be resolved. - unix.chown(path:str, uid, gid) → rc:int[, errno:int] + unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno - Changes user and gorup on file. + Changes user and group on file. - unix.chmod(path:str, mode) → rc:int[, errno:int] + Returns `ENOSYS` on Windows NT. + + unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]]) + ├─→ true + └─→ nil, unix.Errno Changes mode bits on file. - unix.getcwd(path:str, mode) → rc:int[, errno:int] + On Windows NT the chmod system call only changes the read-only + status of a file. + + unix.getcwd() + ├─→ path:str + └─→ nil, unix.Errno Returns current working directory. - unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] + On Windows NT, this function transliterates `\` to `/` and + furthermore prefixes `//?/` to WIN32 DOS-style absolute paths, + thereby assisting with simple absolute filename checks in addition + to enabling one to exceed the traditional 260 character limit. + + unix.fcntl(fd:int, cmd:int, ...) + ├─→ ... + └─→ nil, unix.Errno Manipulates file descriptor. @@ -1565,119 +2004,313 @@ UNIX MODULE lets you query and/or change the status of file descriptors. For example, it's possible using this to change `FD_CLOEXEC`. - POSIX advisory locks can be controlled by setting `cmd` to - `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. + [work in progress] POSIX advisory locks can be controlled by setting + `cmd` to `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. - unix.getsid(pid) → sid, errno:int + unix.getsid(pid:int) + ├─→ sid:int + └─→ nil, unix.Errno Gets session id. - unix.getpgrp() → pgid, errno:int + unix.getpgrp() + ├─→ pgid:int + └─→ nil, unix.Errno Gets process group id. - unix.setpgrp() → pgid, errno:int + unix.setpgrp() + ├─→ pgid:int + └─→ nil, unix.Errno Sets process group id. This is the same as `setpgid(0,0)`. - unix.setpgid(pid, pgid) → pgid, errno:int + unix.setpgid(pid:int, pgid:int) + ├─→ true + └─→ nil, unix.Errno Sets process group id the modern way. - unix.getpgid(pid) → pgid, errno:int + unix.getpgid(pid:int) + ├─→ pgid:int + └─→ nil, unix.Errno Gets process group id the modern wayp. - unix.setsid() → sid, errno:int + unix.setsid() + ├─→ sid:int + └─→ nil, unix.Errno Sets session id. This function can be used to create daemons. - unix.getuid() → uid:int + Fails with `ENOSYS` on Windows NT. - Gets user id. + unix.getuid() + └─→ uid:int - unix.getgid() → gid:int + Gets real user id. - Sets group id. + On Windows this system call is polyfilled by running GetUserNameW() + through Knuth's multiplicative hash. - unix.setuid(uid:int) → rc:int[, errno:int] + This function does not fail. + + unix.getgid() + └─→ gid:int + + Sets real group id. + + On Windows this system call is polyfilled as getuid(). + + This function does not fail. + + unix.geteuid() + └─→ uid:int + + Gets effective user id. + + For example, if your redbean is a setuid binary, then getuid() will + return the uid of the user running the program, and geteuid() shall + return zero which means root, assuming that's the file owning user. + + On Windows this system call is polyfilled as getuid(). + + This function does not fail. + + unix.getegid() + └─→ gid:int + + Gets effective group id. + + On Windows this system call is polyfilled as getuid(). + + This function does not fail. + + unix.chroot(path:str) + ├─→ true + └─→ nil, unix.Errno + + Changes root directory. + + Returns `ENOSYS` on Windows NT. + + unix.setuid(uid:int) + ├─→ true + └─→ nil, unix.Errno Sets user id. - unix.setgid(gid:int) → rc:int[, errno:int] + One use case for this function is dropping root privileges. Should + you ever choose to run redbean as root and decide not to use the + `-G` and `-U` flags, you can replicate that behavior in the Lua + processes you spawn as follows: + + ok, err = unix.setgid(1000) -- check your /etc/groups + if not ok then Log(kLogFatal, tostring(err)) end + ok, err = unix.setuid(1000) -- check your /etc/passwd + if not ok then Log(kLogFatal, tostring(err)) end + + If your goal is to relinquish privileges because redbean is a setuid + binary, then things are more straightforward: + + ok, err = unix.setgid(unix.getgid()) + if not ok then Log(kLogFatal, tostring(err)) end + ok, err = unix.setuid(unix.getuid()) + if not ok then Log(kLogFatal, tostring(err)) end + + See also the setresuid() function and be sure to refer to your local + system manual about the subtleties of changing user id in a way that + isn't restorable. + + Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. + + unix.setgid(gid:int) + ├─→ true + └─→ nil, unix.Errno Sets group id. - unix.umask(mask) → oldmask:int + Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. + + unix.setresuid(real:int, effective:int, saved:int) + ├─→ true + └─→ nil, unix.Errno + + Sets real, effective, and saved user ids. + + If any of the above parameters are -1, then it's a no-op. + + Returns `ENOSYS` on Windows NT. + Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. + + unix.setresgid(real:int, effective:int, saved:int) + ├─→ true + └─→ nil, unix.Errno + + Sets real, effective, and saved group ids. + + If any of the above parameters are -1, then it's a no-op. + + Returns `ENOSYS` on Windows NT. + Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. + + unix.umask(newmask:int) + └─→ oldmask:int Sets file permission mask and returns the old one. + This is used to remove bits from the `mode` parameter of functions + like open() and mkdir(). The masks typically used are 027 and 022. + Those masks ensure that, even if a file is created with 0666 bits, + it'll be turned into 0640 or 0644 so that users other than the owner + can't modify it. + + To read the mask without changing it, try doing this: + + mask = unix.umask(027) + unix.umask(mask) + + On Windows NT this is a no-op and `mask` is returned. + + This function does not fail. + unix.syslog(priority:int, msg:str) Generates a log message, which will be distributed by syslogd. - `priority` is a bitmask containing the facility value and the - level value. If no facility value is ORed into priority, then - the default value set by openlog() is used. If set to NULL, the - program name is used. Level is one of `LOG_EMERG`, `LOG_ALERT`, - `LOG_CRIT`, `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`, - `LOG_DEBUG`. + `priority` is a bitmask containing the facility value and the level + value. If no facility value is ORed into priority, then the default + value set by openlog() is used. If set to NULL, the program name is + used. Level is one of `LOG_EMERG`, `LOG_ALERT`, `LOG_CRIT`, + `LOG_ERR`, `LOG_WARNING`, `LOG_NOTICE`, `LOG_INFO`, `LOG_DEBUG`. 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, nanos:int + └─→ nil, unix.Errno - Returns nanosecond precision timestamp from the system. + Returns nanosecond precision timestamp from system, e.g. - `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`, + >: unix.clock_gettime() + 1651137352 774458779 + >: Benchmark(unix.clock_gettime) + 126 393 571 1 - unix.nanosleep(seconds:int[, nanos:int]) - → remseconds:int, remnanos:int, errno:int + `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`: linux-only + + Returns `EINVAL` if clock isn't supported on platform. + + This function only fails if `clock` is invalid. + + This function goes fastest on Linux and Windows. + + unix.nanosleep(seconds:int, nanos:int) + ├─→ remseconds:int, remnanos:int + └─→ nil, unix.Errno Sleeps with nanosecond precision. + Returns `EINTR` if a signal was received while waiting. + unix.sync() - unix.fsync(fd:int) → rc:int[, errno:int] - unix.fdatasync(fd:int) → rc:int[, errno:int] + unix.fsync(fd:int) + ├─→ true + └─→ nil, unix.Errno + unix.fdatasync(fd:int) + ├─→ true + └─→ nil, unix.Errno These functions are used to make programs slower by asking the operating system to flush data to the physical medium. - unix.seek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] + unix.lseek(fd:int, offset:int[, whence:int]) + ├─→ newposbytes:int + └─→ nil, unix.Errno Seeks to file position. - `whence` can be one of `SEEK_SET`, `SEEK_CUR`, or `SEEK_END`. + `whence` can be one of: - unix.truncate(path:str, length) → rc:int[, errno:int] - unix.ftruncate(fd:int, length) → rc:int[, errno:int] + - `SEEK_SET`: Sets the file position to `offset` + - `SEEK_CUR`: Sets the file position to `position + offset` + - `SEEK_END`: Sets the file position to `filesize + offset` + + Returns the new position relative to the start of the file. + + unix.truncate(path:str[, length:int]) + ├─→ true + └─→ nil, unix.Errno Reduces or extends underlying physical medium of file. If file was originally larger, content >length is lost. - unix.socket([family[, type[, protocol]]]) → fd:int[, errno:int] + `length` defaults to zero. - `SOCK_CLOEXEC` may be or'd into type - `family` defaults to `AF_INET` but can be `AF_UNIX` - `type` defaults to `SOCK_STREAM` but can be `SOCK_DGRAM` - `protocol` defaults to `IPPROTO_TCP` but can be `IPPROTO_UDP` + unix.ftruncate(fd:int[, length:int]) + ├─→ true + └─→ nil, unix.Errno - unix.socketpair([family[, type[, protocol]]]) → fd1, fd2, errno:int + 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 + └─→ nil, unix.Errno + + `family` defaults to `AF_INET` and can be: + + - `AF_UNIX` + - `AF_INET` + + `type` defaults to `SOCK_STREAM` and can be: + + - `SOCK_STREAM` + - `SOCK_DGRAM` + - `SOCK_RAW` + - `SOCK_RDM` + - `SOCK_SEQPACKET` + + `protocol` defaults to `IPPROTO_TCP` and can be: + + - `IPPROTO_IP` + - `IPPROTO_ICMP` + - `IPPROTO_TCP` + - `IPPROTO_UDP` + - `IPPROTO_RAW` + + `SOCK_CLOEXEC` may be bitwise or'd into `type`. + + unix.socketpair([family:int[, type:int[, protocol:int]]]) + ├─→ fd1:int, fd2:int + └─→ nil, unix.Errno `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]) → rc:int[, errno:int] + unix.bind(fd:int[, ip:uint32, port:uint16]) + ├─→ true + └─→ nil, unix.Errno Binds socket. @@ -1685,20 +2318,21 @@ UNIX MODULE wanted to listen on `1.2.3.4:31337` you could do any of these unix.bind(sock, 0x01020304, 31337) + unix.bind(sock, ParseIp('1.2.3.4'), 31337) unix.bind(sock, 1 << 24 | 0 << 16 | 0 << 8 | 1, 31337) `ip` and `port` both default to zero. The meaning of bind(0, 0) is to listen on all interfaces with a kernel-assigned ephemeral port number, that can be retrieved and used as follows: - local sock = unix.socket() -- create ipv4 tcp socket - unix.bind(sock) -- all interfaces arbitrary port - ip, port = unix.getsockname(sock) + sock = assert(unix.socket()) -- create ipv4 tcp socket + assert(unix.bind(sock)) -- all interfaces ephemeral port + ip, port = assert(unix.getsockname(sock)) print("listening on ip", FormatIp(ip), "port", port) - unix.listen(sock) - unix.accept(sock) + assert(unix.listen(sock)) + assert(unix.accept(sock)) while true do - client, clientip, clientport = unix.accept(sock) + client, clientip, clientport = assert(unix.accept(sock)) print("got client ip", FormatIp(clientip), "port", clientport) unix.close(client) end @@ -1706,12 +2340,101 @@ 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}, ...} + └─→ nil, unix.Errno Returns list of network adapter addresses. - unix.poll({fd:int=events:int, ...}[, timeoutms:int]) - → {fd:int=revents:int, ...}[, errno:int] + unix.getsockopt(fd:int, level:int, optname:int) → ... + unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno + + Tunes networking parameters. + + `level` and `optname` may be one of the following. The ellipses type + signature above changes depending on which options are used. + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ value:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, value:bool) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_TYPE` + - `SOL_SOCKET` + `SO_DEBUG` + - `SOL_SOCKET` + `SO_ACCEPTCONN` + - `SOL_SOCKET` + `SO_BROADCAST` + - `SOL_SOCKET` + `SO_REUSEADDR` + - `SOL_SOCKET` + `SO_REUSEPORT` + - `SOL_SOCKET` + `SO_KEEPALIVE` + - `SOL_SOCKET` + `SO_DONTROUTE` + - `SOL_TCP` + `TCP_NODELAY` + - `SOL_TCP` + `TCP_CORK` + - `SOL_TCP` + `TCP_QUICKACK` + - `SOL_TCP` + `TCP_FASTOPEN_CONNECT` + - `SOL_TCP` + `TCP_DEFER_ACCEPT` + - `SOL_IP` + `IP_HDRINCL` + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ value:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, value:int) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_SNDBUF` + - `SOL_SOCKET` + `SO_RCVBUF` + - `SOL_SOCKET` + `SO_RCVLOWAT` + - `SOL_SOCKET` + `SO_SNDLOWAT` + - `SOL_TCP` + `TCP_KEEPIDLE` + - `SOL_TCP` + `TCP_KEEPINTVL` + - `SOL_TCP` + `TCP_FASTOPEN` + - `SOL_TCP` + `TCP_KEEPCNT` + - `SOL_TCP` + `TCP_MAXSEG` + - `SOL_TCP` + `TCP_SYNCNT` + - `SOL_TCP` + `TCP_NOTSENT_LOWAT` + - `SOL_TCP` + `TCP_WINDOW_CLAMP` + - `SOL_IP` + `IP_TOS` + - `SOL_IP` + `IP_MTU` + - `SOL_IP` + `IP_TTL` + + unix.getsockopt(fd:int, level:int, optname:int) + ├─→ secs:int, nsecs:int + └─→ nil, unix.Errno + unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int]) + ├─→ true + └─→ nil, unix.Errno + + - `SOL_SOCKET` + `SO_RCVTIMEO`: If this option is specified then + your stream socket will have a read() / recv() timeout. If the + specified interval elapses without receiving data, then EAGAIN + shall be returned by read. If this option is used on listening + sockets, it'll be inherited by accepted sockets. Your redbean + already does this for GetClientFd() based on the `-t` flag. + + - `SOL_SOCKET` + `SO_SNDTIMEO`: This is the same as `SO_RCVTIMEO` + but it applies to the write() / send() functions. + + unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER) + ├─→ seconds:int, enabled:bool + └─→ nil, unix.Errno + unix.setsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER, secs:int, enabled:bool) + ├─→ true + └─→ nil, unix.Errno + + This `SO_LINGER` parameter can be used to make close() a blocking + call. Normally when the kernel returns immediately when it receives + close(). Sometimes it's desirable to have extra assurance on errors + happened, even if it comes at the cost of performance. + + Returns `EINVAL` if settings other than the above are used. + + Returns `ENOSYS` if setting isn't supported by the host o/s. + + unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) + ├─→ {[fd:int]=revents:int, ...} + └─→ nil, unix.Errno Checks for events on a set of file descriptors. @@ -1724,19 +2447,32 @@ 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 + └─→ nil, unix.Errno Returns hostname of system. - unix.listen(fd:int[, backlog]) → rc:int[, errno:int] + unix.listen(fd:int[, backlog:int]) + ├─→ true + └─→ nil, 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, ip:uint32, port:uint16 + └─→ nil, unix.Errno 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) + ├─→ true + └─→ nil, unix.Errno Connects a TCP socket to a remote host. @@ -1744,66 +2480,188 @@ 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, port:uint16 + └─→ nil, unix.Errno Retrieves the local address of a socket. - unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] + unix.getpeername(fd:int) + ├─→ ip:uint32, port:uint16 + └─→ nil, unix.Errno Retrieves the remote address of a socket. - unix.recv(fd:int[, bufsiz[, flags]]) → data, errno:int + unix.recv(fd:int[, bufsiz:int[, flags:int]]) + ├─→ data:str + └─→ nil, unix.Errno - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + `flags` can have: - unix.recvfrom(fd:int[, bufsiz[, flags]]) → data, ip, port, errno:int + - `MSG_WAITALL` + - `MSG_DONTROUTE` + - `MSG_PEEK` + - `MSG_OOB` - `flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. + unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) + ├─→ data:str, ip:uint32, port:uint16 + └─→ nil, unix.Errno - unix.send(fd:int, data[, flags]) → sent, errno:int + `flags` can have: + + - `MSG_WAITALL` + - `MSG_DONTROUTE` + - `MSG_PEEK` + - `MSG_OOB` + + unix.send(fd:int, data:str[, flags:int]) + ├─→ sent:int + └─→ nil, unix.Errno This is the same as `write` except it has a `flags` argument - that's intended for sockets. `flags` can have `MSG_OOB`, - `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + that's intended for sockets. - unix.sendto(fd:int, data, ip, port[, flags]) → sent, errno:int + `flags` may have any of: + + - `MSG_OOB` + - `MSG_DONTROUTE` + - `MSG_NOSIGNAL` + + unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) + ├─→ sent:int + └─→ nil, unix.Errno This is useful for sending messages over UDP sockets to specific - addresses. The `flags` parameter can have `MSG_OOB`, - `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. + addresses. - unix.shutdown(fd:int, how:int) → rc:int[, errno:int] + `flags` may have any of: - Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or - `SHUT_RDWR`. + - `MSG_OOB` + - `MSG_DONTROUTE` + - `MSG_NOSIGNAL` - unix.sigprocmask(how[, mask]) → oldmask, errno:int + unix.shutdown(fd:int, how:int) + ├─→ true + └─→ nil, unix.Errno - `how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` + Partially closes socket. - unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) - → oldhandler:func|int, flags:int, mask:int, errno:int + `how` is set to one of: - `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua - function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. - `flags` can have `SA_RESTART`, `SA_RESETHAND`, etc. Example: + - `SHUT_RD`: sends a tcp half close for reading + - `SHUT_WR`: sends a tcp half close for writing + - `SHUT_RDWR` - unix = require "unix" - unix.sigaction(unix.SIGUSR1, function(sig) - print(string.format("got %s", unix.strsignal(sig))) - end) - unix.sigprocmask(unix.SIG_SETMASK, -1) - unix.raise(unix.SIGUSR1) - unix.sigsuspend() + This system call currently has issues on Macintosh, so portable code + should log rather than assert failures reported by shutdown(). + + unix.sigprocmask(how:int, newmask:unix.Sigset) + ├─→ oldmask:unix.Sigset + └─→ nil, unix.Errno + + 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 unix.Sigset() object (see section below). + + For example, to temporarily block `SIGTERM` and `SIGINT` so critical + work won't be interrupted, sigprocmask() can be used as follows: + + newmask = unix.Sigset(unix.SIGTERM) + oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, newmask)) + -- do something... + assert(unix.sigprocmask(unix.SIG_SETMASK, oldmask)) + + unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]]) + ├─→ oldhandler:func|int, flags:int, mask:unix.Sigset + └─→ nil, unix.Errno + + `sig` can be one of: + + - `unix.SIGINT` + - `unix.SIGQUIT` + - `unix.SIGTERM` + - etc. + + `handler` can be: + + - Lua function + - `unix.SIG_IGN` + - `unix.SIG_DFL` + + `flags` can have: + + - `unix.SA_RESTART`: Enables BSD signal handling semantics. Normally + i/o entrypoints check for pending signals to deliver. If one gets + delivered during an i/o call, the normal behavior is to cancel the + i/o operation and return -1 with `EINTR` in errno. If you use the + `SA_RESTART` flag then that behavior changes, so that any function + that's been annotated with @restartable will not return `EINTR` + and will instead resume the i/o operation. This makes coding + easier but it can be an anti-pattern if not used carefully, since + poor usage can easily result in latency issues. It also requires + one to do more work in signal handlers, so special care needs to + be given to which C library functions are @asyncsignalsafe. + + - `unix.SA_RESETHAND`: Causes signal handler to be single-shot. This + means that, upon entry of delivery to a signal handler, it's reset + to the `SIG_DFL` handler automatically. You may use the alias + `SA_ONESHOT` for this flag, which means the same thing. + + - `unix.SA_NODEFER`: Disables the reentrancy safety check on your signal + handler. Normally that's a good thing, since for instance if your + `SIGSEGV` signal handler happens to segfault, you're going to want + your process to just crash rather than looping endlessly. But in + some cases it's desirable to use `SA_NODEFER` instead, such as at + times when you wish to `longjmp()` out of your signal handler and + back into your program. This is only safe to do across platforms + for non-crashing signals such as `SIGCHLD` and `SIGINT`. Crash + handlers should use Xed instead to recover execution, because on + Windows a `SIGSEGV` or `SIGTRAP` crash handler might happen on a + separate stack and/or a separate thread. You may use the alias + `SA_NOMASK` for this flag, which means the same thing. + + - `unix.SA_NOCLDWAIT`: Changes `SIGCHLD` so the zombie is gone and + you can't call wait() anymore; similar but may still deliver the + SIGCHLD. + + - `unix.SA_NOCLDSTOP`: Lets you set `SIGCHLD` handler that's only + notified on exit/termination and not notified on `SIGSTOP`, + `SIGTSTP`, `SIGTTIN`, `SIGTTOU`, or `SIGCONT`. + + Example: + + assert(unix.sigaction(unix.SIGUSR1, function(sig) + gotsigusr1 = true + end)) + gotsigusr1 = false + assert(unix.raise(unix.SIGUSR1)) + ok, err = unix.sigsuspend() + assert(err:errno == unix.EINTR) + if gotsigusr1 + print('hooray the signal was delivered') + else + print('oh no some other signal was handled') + end It's a good idea to not do too much work in a signal handler. - unix.sigsuspend([mask]) → errno + unix.sigsuspend([mask:Sigmask]) + └─→ nil, unix.Errno Waits for signal to be delivered. - unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) - → intsec, intns, valsec, valns, errno:int + The signal mask is temporarily replaced with `mask` during this + system call. `mask` specifies which signals should be blocked. + + unix.setitimer(which[, intervalsec, intns, valuesec, valuens]) + ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int + └─→ nil, unix.Errno Causes `SIGALRM` signals to be generated at some point(s) in the future. The `which` parameter should be `ITIMER_REAL`. @@ -1811,11 +2669,11 @@ UNIX MODULE Here's an example of how to create a 400 ms interval timer: ticks = 0 - unix.sigaction(unix.SIGALRM, function(sig) - print(string.format("tick no. %d", ticks)) + assert(unix.sigaction(unix.SIGALRM, function(sig) + print('tick no. %d' % {ticks}) ticks = ticks + 1 - end) - unix.setitimer(unix.ITIMER_REAL, 0, 400000, 0, 400000) + end)) + assert(unix.setitimer(unix.ITIMER_REAL, 0, 400e6, 0, 400e6)) while true do unix.sigsuspend() end @@ -1825,20 +2683,23 @@ UNIX MODULE unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) - unix.strerrno(errno:int) → str - - Turns `errno` code into its symbolic name, e.g. `"EINTR"`. - - unix.strerror(errno:int) → str - - Turns `errno` code into a longer string describing the error. - unix.strsignal(sig:int) → str - Turns platform-specific `sig` code into its name, e.g. - `strsignal(9)` always returns `"SIGKILL"`. + Turns platform-specific `sig` code into its symbolic name. - unix.setrlimit(resource:int, soft:int[, hard:int]) → rc:int[, errno:int] + For example: + + >: unix.strsignal(9) + "SIGKILL" + >: unix.strsignal(unix.SIGKILL) + "SIGKILL" + + Please note that signal numbers are normally different across + supported platforms, and the constants should be preferred. + + unix.setrlimit(resource:int, soft:int[, hard:int]) + ├─→ true + └─→ nil, unix.Errno Changes resource limit. @@ -1869,419 +2730,1321 @@ 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] + `hard` defaults to whatever was specified in `soft`. - Returns information about resource limit. + unix.getrlimit(resource:int) + ├─→ soft:int, hard:int + └─→ nil, unix.Errno - unix.stat(x) → UnixStat*, errno:int + Returns information about resource limits for current process. - 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.getrusage([who:int]) + ├─→ unix.Rusage + └─→ nil, unix.Errno - unix.opendir(path:str) → UnixDir*, errno:int + Returns information about resource usage for current process, e.g. + + >: unix.getrusage() + {utime={0, 53644000}, maxrss=44896, minflt=545, oublock=24, nvcsw=9} + + `who` defaults to `RUSAGE_SELF` and can be any of: + + - `RUSAGE_SELF`: current process + - `RUSAGE_THREAD`: current thread + - `RUSAGE_CHILDREN`: not supported on Windows NT + - `RUSAGE_BOTH`: not supported on non-Linux + + See the unix.Rusage section below for details on returned fields. + + unix.pledge([promises:str]) + ├─→ true + └─→ nil, unix.Errno + + Restrict system operations. + + This can be used to sandbox your redbean workers. It allows finer + customization compared to the `-S` flag. + + By default exit and exit_group are always allowed. This is useful + for processes that perform pure computation and interface with the + parent via shared memory. + + Currently only available on OpenBSD and Linux. On Linux, the default + action when your policy is violated is to return `EPERM`. On OpenBSD + the kernel will kill the process. + + `promises` is a string that may include any of the following groups + delimited by spaces. + + stdio + + Allows clock_getres, clock_gettime, close, dup, dup2, dup3, + fchdir, fstat, fsync, ftruncate, getdents, getegid, getrandom, + geteuid, getgid, getgroups, getitimer, getpgid, getpgrp, getpid, + getppid, getresgid, getresuid, getrlimit, getsid, gettimeofday, + getuid, lseek, madvise, brk, mmap, mprotect, munmap, nanosleep, + pipe, pipe2, poll, pread, preadv, pwrite, pwritev, read, readv, + recvfrom, recvmsg, select, sendmsg, sendto, setitimer, shutdown, + sigaction, sigprocmask, sigreturn, socketpair, umask, wait4, + write, writev. + + rpath + + Allows chdir, getcwd, openat, fstatat, faccessat, readlinkat, + lstat, chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + + wpath + + Allows getcwd, openat, fstatat, faccessat, readlinkat, lstat, + chmod, fchmod, fchmodat, chown, fchown, fchownat, fstat. + + cpath + + Allows rename, renameat, link, linkat, symlink, symlinkat, unlink, + unlinkat, mkdir, mkdirat, rmdir. + + dpath + + Allows mknod + + tmppath + + Allows lstat, chmod, chown, unlink, fstat. + + inet + + Allows socket, listen, bind, connect, accept4, accept, + getpeername, getsockname, setsockopt, getsockopt. + + fattr + + Allows utimes, utimensat, chmod, fchmod, fchmodat, chown, + fchownat, lchown, fchown, utimes. + + unix + + Allows socket, listen, bind, connect, accept4, accept, + getpeername, getsockname, setsockopt, getsockopt. + + dns + + Allows sendto, recvfrom, socket, connect. + + proc + + Allows fork, vfork, kill, getpriority, setpriority, setrlimit, + setpgid, setsid. + + exec + + Allows execve. + + id + + Allows setuid, setreuid, setresuid, setgid, setregid, setresgid, + setgroups, setrlimit, getpriority, setpriority. + + unix.gmtime(unixts:int) + ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str + └─→ nil,unix.Errno + + Breaks down UNIX timestamp into Zulu Time numbers. + + - `mon` 1 ≤ mon ≤ 12 + - `mday` 1 ≤ mday ≤ 31 + - `hour` 0 ≤ hour ≤ 23 + - `min` 0 ≤ min ≤ 59 + - `sec` 0 ≤ sec ≤ 60 + - `gmtoff` ±93600 seconds + - `wday` 0 ≤ wday ≤ 6 + - `yday` 0 ≤ yday ≤ 365 + - `dst` 1 if daylight savings, 0 if not, -1 if not unknown + + unix.localtime(unixts:int) + ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str + └─→ nil,unix.Errno + + Breaks down UNIX timestamp into local time numbers, e.g. + + >: unix.localtime(unix.clock_gettime()) + 2022 4 28 2 14 22 -25200 4 117 1 "PDT" + + This follows the same API as gmtime() which has further details. + + Your redbean ships with a subset of the time zone database. + + - `/zip/usr/share/zoneinfo/Honolulu` Z-10 + - `/zip/usr/share/zoneinfo/Anchorage` Z -9 + - `/zip/usr/share/zoneinfo/GST` Z -8 + - `/zip/usr/share/zoneinfo/Boulder` Z -6 + - `/zip/usr/share/zoneinfo/Chicago` Z -5 + - `/zip/usr/share/zoneinfo/New_York` Z -4 + - `/zip/usr/share/zoneinfo/UTC` Z +0 + - `/zip/usr/share/zoneinfo/GMT` Z +0 + - `/zip/usr/share/zoneinfo/London` Z +1 + - `/zip/usr/share/zoneinfo/Berlin` Z +2 + - `/zip/usr/share/zoneinfo/Israel` Z +3 + - `/zip/usr/share/zoneinfo/India` Z +5 + - `/zip/usr/share/zoneinfo/Beijing` Z +8 + - `/zip/usr/share/zoneinfo/Japan` Z +9 + - `/zip/usr/share/zoneinfo/Sydney` Z+10 + + You can control which timezone is used using the `TZ` environment + variable. If your time zone isn't included in the above list, you + can simply copy it inside your redbean. The same is also the case + for future updates to the database, which can be swapped out when + needed, without having to recompile. + + unix.stat(path:str[, flags:int[, dirfd:int]]) + ├─→ unix.Stat + └─→ nil, unix.Errno + + Gets information about file or directory. + + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + unix.fstat(fd:int) + ├─→ unix.Stat + └─→ nil, unix.Errno + + Gets information about opened file descriptor. + + `fd` should be a file descriptor that was opened using + `unix.open(path, O_RDONLY|O_DIRECTORY)`. + + `flags` may have any of: + + - `AT_SYMLINK_NOFOLLOW`: do not follow symbolic links. + + `dirfd` defaults to to `unix.AT_FDCWD` and may optionally be set to + a directory file descriptor to which `path` is relative. + + A common use for fstat() is getting the size of a file. For example: + + fd = assert(unix.open("hello.txt", unix.O_RDONLY)) + st = assert(unix.fstat(fd)) + Log(kLogInfo, 'hello.txt is %d bytes in size' % {st:size()}) + unix.close(fd) + + unix.opendir(path:str) + ├─→ state:unix.Dir + └─→ nil, unix.Errno Opens directory for listing its contents. - unix.opendir(fd:int) → UnixDir*, errno:int + For example, to print a simple directory listing: - Opens directory for listing its contents, using a file - descriptor from `open(path, O_RDONLY|O_DIRECTORY)`. + Write('
    \r\n') + for name, kind, ino, off in assert(unix.opendir(dir)) do + if name ~= '.' and name ~= '..' then + Write('
  • %s\r\n' % {EscapeHtml(name)}) + end + end + Write('
\r\n') - UnixDir* Object + unix.fdopendir(fd:int) + ├─→ next:function, state:unix.Dir + └─→ nil, unix.Errno - UnixDir:close() → rc:int[, errno:int] + Opens directory for listing its contents, via an fd. - may be called multiple times - called by the garbage collector too + `fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The + returned unix.Dir ownership takes ownership of the file descriptor + and will close it automatically when garbage collected. - UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] + +──────────────────────────────────────────────────────────────────────────────── + + UNIX DIR OBJECT + + unix.Dir objects are created by opendir() or fdopendir(). The + following methods are available: + + unix.Dir:close() + ├─→ true + └─→ nil, unix.Errno + + Closes directory stream object and associated its file descriptor. + + This is called automatically by the garbage collector. + + This may be called multiple times. + + unix.Dir:read() + ├─→ name:str, kind:int, ino:int, off:int + └─→ nil + + Reads entry from directory stream. 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_REG`: file is a regular file + - `DT_DIR`: file is a directory + - `DT_BLK`: file is a block device + - `DT_LNK`: file is a symbolic link + - `DT_CHR`: file is a character device + - `DT_FIFO`: file is a named pipe + - `DT_SOCK`: file is a named socket + - `DT_UNKNOWN` - EOPNOTSUPP if using /zip/ - EOPNOTSUPP if IsWindows() + Note: This function also serves as the `__call` metamethod, so that + unix.Dir objects may be used as a for loop iterator. - UnixDir:tell() → offset:int + unix.Dir:fd() + ├─→ fd:int + └─→ nil, unix.Errno + + Returns file descriptor of open directory object. + + Returns `EOPNOTSUPP` if using a `/zip/...` path. + Returns `EOPNOTSUPP` if using Windows NT. + + unix.Dir:tell() + ├─→ off:int + └─→ nil, unix.Errno Returns current arbitrary offset into stream. - UnixDir:rewind() + unix.Dir:rewind() Resets stream back to beginning. - UnixStat* object. - UnixStat:size() → bytes:int +──────────────────────────────────────────────────────────────────────────────── + + UNIX RUSAGE OBJECT - Size of file in bytes. + unix.Rusage objects are created by wait() or getrusage(). The + following accessor methods are available. - UnixStat:mode() → mode:int + unix.Rusage:utime() + └─→ seconds:int, nanos:int - Contains file type and permissions. + Returns amount of CPU consumed in userspace. - For example, `0010644` is what you might see for a file and - `0040755` is what you might see for a directory. + It's always the case that `0 ≤ nanos < 1e9`. - To determine the file type: + On Windows NT this is collected from GetProcessTimes(). - - `(st:mode() & 0170000) == 0010000` means fifo or pipe - - `(st:mode() & 0170000) == 0020000` means character device - - `(st:mode() & 0170000) == 0040000` means directory - - `(st:mode() & 0170000) == 0060000` means block device - - `(st:mode() & 0170000) == 0100000` means regular file - - `(st:mode() & 0170000) == 0120000` means symbolic link - - `(st:mode() & 0170000) == 0140000` means socket + unix.Rusage:stime() + └─→ seconds:int, nanos:int - UnixStat:atim() → secs:int, nanos:int + Returns amount of CPU consumed in kernelspace. - Size of file in bytes. + It's always the case that `0 ≤ 𝑥 < 1e9`. - UnixStat:uid() → int + On Windows NT this is collected from GetProcessTimes(). - User ID of file owner. + unix.Rusage:maxrss() + └─→ kilobytes:int - UnixStat:gid() → int + Returns amount of physical memory used at peak consumption. - Group ID of file owner. + On Windows NT this is collected from + NtProcessMemoryCountersEx::PeakWorkingSetSize / 1024. - UnixStat:mtim() → secs:int, nanos:int + unid.Rusage:idrss() + └─→ integralkilobytes:int - Last modified time. + Returns integral private memory consumption w.r.t. scheduled ticks. - UnixStat:birthtim() → secs:int, nanos:int + If you chart memory usage over the lifetime of your process, then + this would be the space filled in beneath the chart. The frequency + of kernel scheduling is defined as unix.CLK_TCK. Each time a tick + happens, the kernel samples your process's memory usage, by adding + it to this value. You can derive the average consumption from this + value by computing how many ticks are in `utime + stime`. - Creation time. Note that on Linux this is the mimimum of - atom/mtim/ctim. + Currently only available on FreeBSD and NetBSD. - UnixStat:ctim() → secs:int, nanos:int + unix.Rusage:ixrss() + └─→ integralkilobytes:int - Complicated time. Means time file status was last changed on - UNIX. Means creation time on Windows. + Returns integral shared memory consumption w.r.t. scheduled ticks. - UnixStat:blocks() → int + If you chart memory usage over the lifetime of your process, then + this would be the space filled in beneath the chart. The frequency + of kernel scheduling is defined as unix.CLK_TCK. Each time a tick + happens, the kernel samples your process's memory usage, by adding + it to this value. You can derive the average consumption from this + value by computing how many ticks are in `utime + stime`. - Number of blocks used by storage medium. + Currently only available on FreeBSD and NetBSD. - UnixStat:blksize() → int + unis.Rusage:isrss() + └─→ integralkilobytes:int - Block size is usually 4096 for file system files. + Returns integral stack memory consumption w.r.t. scheduled ticks. - UnixStat:dev() → int - UnixStat:ino() → int - UnixStat:rdev() → int + If you chart memory usage over the lifetime of your process, then + this would be the space filled in beneath the chart. The frequency + of kernel scheduling is defined as unix.CLK_TCK. Each time a tick + happens, the kernel samples your process's memory usage, by adding + it to this value. You can derive the average consumption from this + value by computing how many ticks are in `utime + stime`. - Here are your error numbers: + This is only applicable to redbean if its built with MODE=tiny, + because redbean likes to allocate its own deterministic stack. - - `EINVAL`: Invalid argument. Raised by [pretty much everything]. + Currently only available on FreeBSD and NetBSD. - - `ENOSYS`: System call not available on this platform. On Windows - this is raised by chroot(), setuid(), setgid(). + unix.Rusage:minflt() + └─→ count:int - - `ENOENT`: no such file or directory. Raised by access(), - alloc_hugepages(), bind(), chdir(), chmod(), chown(), chroot(), - clock_getres(), execve(), opendir(), inotify_add_watch(), kcmp(), - link(), mkdir(), mknod(), msgget(), open(), readlink(), rename(), - rmdir(), semget(), shmget(), stat(), swapon(), symlink(), - truncate(), unlink(), utime(), utimensat(). + Returns number of minor page faults. - - `ESRCH`: No such process. Raised by getpriority(), getrlimit(), - getsid(), ioprio_set(), kill(), setpgid(), tkill(), utimensat(), + This number indicates how many times redbean was preempted by the + kernel to memcpy() a 4096-byte page. This is one of the tradeoffs + fork() entails. This number is usually tinier, when your binaries + are tinier. - - `EINTR`: The greatest of all errnos; crucial for building real - time reliable software. Raised by accept(), clock_nanosleep(), - close(), connect(), dup(), fcntl(), flock(), getrandom(), - nanosleep(), open(), pause(), poll(), ptrace(), read(), recv(), - select(), send(), sigsuspend(), sigwaitinfo(), truncate(), - wait(), write() + Not available on Windows NT. - - `EIO`: Raised by access() acct() chdir() chmod() chown() chroot() - close() copy_file_range() execve() fallocate() fsync() ioperm() - link() madvise() mbind() pciconfig_read() ptrace() read() - readlink() sendfile() statfs() symlink() sync_file_range() - truncate() unlink() write() + unix.Rusage:majflt() + └─→ count:int - - `ENXIO`: No such device or address. Raised by lseek(), open(), - prctl() + Returns number of major page faults. - - `E2BIG`: Argument list too long. Raised by execve(), msgop(), - sched_setattr(), semop() + This number indicates how many times redbean was preempted by the + kernel to perform i/o. For example, you might have used mmap() to + load a large file into memory lazily. - - `ENOEXEC`: exec format error. Raised by execve(), kexec_load(), - uselib() - - - `EBADF`: bad file descriptor; cf. EBADFD. Raised by accept(), - access(), bind(), chdir(), chmod(), chown(), close(), connect(), - copy_file_range(), dup(), fcntl(), flock(), fsync(), futimesat(), - opendir(), getpeername(), getsockname(), getsockopt(), - inotify_add_watch(), inotify_rm_watch(), ioctl(), kcmp(), - kexec_load(), link(), listen(), llseek(), lseek(), mkdir(), - mknod(), mmap(), open(), prctl(), read(), readahead(), - readlink(), recv(), rename(), select(), send(), shutdown(), - splice(), stat(), symlink(), sync(), sync_file_range(), - timerfd_create(), truncate(), unlink(), utimensat(), write(), - - - `ECHILD`: no child process. Raised by wait(), waitpid(), - waitid(), wait3(), wait4() - - - `EAGAIN`: resource temporarily unavailable (e.g. SO_RCVTIMEO - expired, too many processes, too much memory locked, read or - write with O_NONBLOCK needs polling, etc.). Raised by accept(), - connect(), eventfd(), fcntl(), fork(), getrandom(), mincore(), - mlock(), mmap(), mremap(), msgop(), poll(), read(), select(), - send(), setresuid(), setreuid(), setuid(), sigwaitinfo(), - splice(), tee(), timer_create(), timerfd_create(), tkill(), - write(), - - - `ENOMEM`: We require more vespene gas. Raised by access(), - bind(), chdir(), chmod(), chown(), chroot(), clone(), - copy_file_range(), create_module(), eventfd(), execve(), - fanotify_init(), fork(), getgroups(), getrlimit(), - inotify_add_watch(), inotify_init(), ioperm(), kexec_load(), - link(), mbind(), memfd_create(), mincore(), mkdir(), mknod(), - mlock(), mmap(), mprotect(), mremap(), msgget(), msgop(), - msync(), open(), poll(), readlink(), recv(), rename(), rmdir(), - select(), semget(), send(), shmget(), sigaltstack(), splice(), - stat(), subpage_prot(), swapon(), symlink(), sync_file_range(), - tee(), timer_create(), timerfd_create(), unlink(). - - - `EACCES`: Permission denied. Raised by access(), bind(), bpf(), - chdir(), chmod(), chown(), chroot(), clock_getres(), connect(), - execve(), fcntl(), getpriority(), inotify_add_watch(), link(), - mkdir(), mknod(), mmap(), mprotect(), msgctl(), msgget(), - msgop(), open(), prctl(), ptrace(), readlink(), rename(), - rmdir(), semget(), send(), setpgid(), shmget(), socket(), stat(), - symlink(), truncate(), unlink(), uselib(), utime(), utimensat(), - - - `EPERM`: Operation not permitted. Raised by accept(), chmod(), - chown(), chroot(), copy_file_range(), execve(), fallocate(), - fanotify_init(), fcntl(), futex(), get_robust_list(), - getdomainname(), getgroups(), gethostname(), getpriority(), - getrlimit(), getsid(), gettimeofday(), idle(), init_module(), - io_submit(), ioctl_console(), ioctl_ficlonerange(), - ioctl_fideduperange(), ioctl_ns(), ioctl_tty(), ioperm(), iopl(), - ioprio_set(), kcmp(), kexec_load(), keyctl(), kill(), link(), - lookup_dcookie(), madvise(), mbind(), membarrier(), - migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(), - move_pages(), msgctl(), nice(), open(), open_by_handle_at(), - pciconfig_read(), perf_event_open(), pidfd_getfd(), - pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(), - ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(), - rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(), - sched_setparam(), sched_setscheduler(), semctl(), seteuid(), - setfsgid(), setfsuid(), setgid(), setns(), setpgid(), - setresuid(), setreuid(), setsid(), setuid(), setup(), setxattr(), - shmctl(), shmget(), sigaltstack(), spu_create(), stime(), - swapon(), symlink(), syslog(), truncate(), unlink(), utime(), - utimensat(), write() - - - `ENOTBLK`: Block device required. Raised by umount(). - - - `EBUSY`: Device or resource busy. Raised by bdflush(), dup(), - fcntl(), msync(), prctl(), ptrace(), rename(), rmdir(). - - - `EEXIST`: File exists. Raised by bpf(), create_module(), - inotify_add_watch(), link(), mkdir(), mknod(), mmap(), msgget(), - open(), rename(), rmdir(), semget(), shmget(), symlink() - - - `EXDEV`: Improper link. Raised by copy_file_range(), link(), - rename() - - - `ENODEV`: No such device. Raised by arch_prctl(), eventfd(), - mmap(), open(), prctl(), timerfd_create() - - - `ENOTDIR`: Not a directory. This means that a directory component - in a supplied path *existed* but wasn't a directory. For example, - if you try to `open("foo/bar")` and `foo` is a regular file, then - `ENOTDIR` will be returned. Raised by open(), access(), chdir(), - chroot(), execve(), link(), mkdir(), mknod(), opendir(), - readlink(), rename(), rmdir(), stat(), symlink(), truncate(), - unlink(), utimensat(), bind(), chmod(), chown(), fcntl(), - futimesat(), inotify_add_watch(). - - - `EISDIR`: Is a a directory. Raised by copy_file_range(), - execve(), open(), read(), rename(), truncate(), unlink(). - - - `ENFILE`: Too many open files in system. Raised by accept(), - eventfd(), execve(), inotify_init(), memfd_create(), mmap(), - open(), pipe(), shmget(), socket(), socketpair(), swapon(), - timerfd_create(), uselib(), userfaultfd(). - - - `EMFILE`: Too many open files. Raised by accept(), dup(), - eventfd(), execve(), fanotify_init(), fcntl(), inotify_init(), - memfd_create(), open(), pipe(), socket(), socketpair(), - timerfd_create(). - - - `ENOTTY`: Inappropriate i/o control operation. Raised by ioctl(). + On Windows NT this is NtProcessMemoryCountersEx::PageFaultCount. - - `ETXTBSY`: Won't open executable that's executing in write mode. - Raised by access(), copy_file_range(), execve(), mmap(), open(), - truncate(). + unix.Rusage:nswap() + └─→ count:int - - `EFBIG`: File too large. Raised by copy_file_range(), open(), - truncate(), write(). + Returns number of swap operations. - - `ENOSPC`: No space left on device. Raised by copy_file_range(), - fsync(), inotify_add_watch(), link(), mkdir(), mknod(), msgget(), - open(), rename(), semget(), shmget(), symlink(), - sync_file_range(), write(). + Operating systems like to reserve hard disk space to back their RAM + guarantees, like using a gold standard for fiat currency. When your + system is under heavy memory load, swap operations may happen while + redbean is working. This number keeps track of them. - - `EDQUOT`: Disk quota exceeded. Raised by link(), mkdir(), - mknod(), open(), rename(), symlink(), write() + Not available on Linux, Windows NT. - - `ESPIPE`: Invalid seek. Raised by lseek(), splice(), - sync_file_range(). + unix.Rusage:inblock() + └─→ count:int - - `EROFS`: Read-only filesystem. Raised by access(), bind(), - chmod(), chown(), link(), mkdir(), mknod(), open(), rename(), - rmdir(), symlink(), truncate(), unlink(), utime(), utimensat() + Returns number of times filesystem had to perform input. - - `EMLINK`: Too many links; raised by link(), mkdir(), rename() + On Windows NT this is NtIoCounters::ReadOperationCount. - - `EPIPE`: Broken pipe. Raised by send(), write(). + unix.Rusage:oublock() + └─→ count:int - - `ERANGE`: Result too large. Raised by prctl(), semop(). + Returns number of times filesystem had to perform output. - - `EDEADLK`: Resource deadlock avoided. Raised by fcntl(). + On Windows NT this is NtIoCounters::WriteOperationCount. - - `ENAMETOOLONG`: Filename too long. Raised by access(), bind(), - chdir(), chmod(), chown(), chroot(), execve(), gethostname(), - inotify_add_watch(), link(), mkdir(), mknod(), open(), - readlink(), rename(), rmdir(), stat(), symlink(), truncate(), u - unlink(), utimensat() + unix.Rusage:msgsnd() + └─→ count:int - - `ENOLCK`: No locks available. Raised by fcntl(), flock(). + Returns count of ipc messages sent. - - `ENOTEMPTY`: Directory not empty. Raised by rmdir(). + Not available on Linux, Windows NT. - - `ELOOP`: Too many levels of symbolic links. Raised by access(), - bind(), chdir(), chmod(), chown(), chroot(), execve(), link(), - mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), - symlink(), truncate(), unlink(), utimensat(). + unix.Rusage:msgrcv() + └─→ count:int - - `ENOMSG`: Raised by msgop(). + Returns count of ipc messages received. - - `EIDRM`: Identifier removed. Raised by msgctl(), msgget(), - msgop(), shmget(). + Not available on Linux, Windows NT. - - `ETIME`: Timer expired; timer expired. Raised by connect(). + unix.Rusage:nsignals() + └─→ count:int - - `EPROTO`: Raised by accept(), connect(), socket(), socketpair(). + Returns number of signals received. - - `EOVERFLOW`: Raised by copy_file_range(), fanotify_init(), - lseek(), mmap(), open(), stat(), statfs() + Not available on Linux. - - `ENOTSOCK`: Not a socket. Raised by accept(), bind(), connect(), - getpeername(), getsockname(), getsockopt(), listen(), recv(), - send(), shutdown(). + unix.Rusage:nvcsw() + └─→ count:int - - `EDESTADDRREQ`: Destination address required. Raised by send(), - write(). + Returns number of voluntary context switches. - - `EMSGSIZE`: Message too long. Raised by send(). + This number is a good thing. It means your redbean finished its work + quickly enough within a time slice that it was able to give back the + remaining time to the system. - - `EPROTOTYPE`: Protocol wrong type for socket. Raised by - connect(). + unix.Rusage:nivcsw() + └─→ count:int - - `ENOPROTOOPT`: Protocol not available. Raised by getsockopt(), - accept(). + Returns number of non-consensual context switches. - - `EPROTONOSUPPORT`: Protocol not supported. Raised by socket(), - socketpair(). + This number is a bad thing. It means your redbean was preempted by a + higher priority process after failing to finish its work, within the + allotted time slice. - - `ESOCKTNOSUPPORT`: Socket type not supported. - - `ENOTSUP`: Operation not supported. Raised by chmod(), - clock_getres(), clock_nanosleep(), timer_create() +──────────────────────────────────────────────────────────────────────────────── + + UNIX STAT OBJECT - - `EOPNOTSUPP`: Socket operation not supported. Raised by accept(), - listen(), mmap(), prctl(), readv(), send(), socketpair(), + unix.Stat objects are created by stat() or fstat(). The following + accessor methods are available. - - `EPFNOSUPPORT`: protocol family not supported + unix.Stat:size() + └─→ bytes:int - - `EAFNOSUPPORT`: address family not supported. Raised by - connect(), socket(), socketpair() + Size of file in bytes. - - `EADDRINUSE`: address already in use. Raised by bind(), - connect(), listen() + unix.Stat:mode() + └─→ mode:int - - `EADDRNOTAVAIL`: address not available. Raised by bind(), - connect(). + Contains file type and permissions. - - `ENETDOWN`: network is down; ; WSAENETDOWN. Raised by accept() + For example, `0010644` is what you might see for a file and + `0040755` is what you might see for a directory. - - `ENETUNREACH`: host is unreachable; ; WSAENETUNREACH. Raised by - accept(), connect() + To determine the file type: - - `ENETRESET`: connection reset by network + - `(st:mode() & 0170000) == 0010000` means fifo or pipe + - `(st:mode() & 0170000) == 0020000` means character device + - `(st:mode() & 0170000) == 0040000` means directory + - `(st:mode() & 0170000) == 0060000` means block device + - `(st:mode() & 0170000) == 0100000` means regular file + - `(st:mode() & 0170000) == 0120000` means symbolic link + - `(st:mode() & 0170000) == 0140000` means socket - - `ECONNABORTED`: connection reset before accept. Raised by - accept() + unix.Stat:uid() + └─→ uid:int - - `ECONNRESET`: connection reset by client. Raised by send(), + User ID of file owner. - - `ENOBUFS`: no buffer space available; raised by getpeername(), - getsockname(), send(), + unix.Stat:gid() + └─→ gid:int - - `EISCONN`: socket is connected. Raised by connect(), send(). + Group ID of file owner. - - `ENOTCONN`: socket is not connected. Raised by getpeername(), - recv(), send(), shutdown(), + unix.Stat:birthtim() + └─→ unixts:int, nanos:int - - `ESHUTDOWN`: cannot send after transport endpoint shutdown; note - that shutdown write is an `EPIPE` + File birth time. - - `ETOOMANYREFS`: too many references: cannot splice. Raised by - sendmsg(), + This field should be accurate on Apple, Windows, and BSDs. On Linux + this is the mimimum of atim/mtim/ctim. On Windows NT nanos is only + accurate to hectonanoseconds. - - `ETIMEDOUT`: connection timed out; ; WSAETIMEDOUT; raised by - connect(), + Here's an example of how you might print a file timestamp: - - `ECONNREFUSED`: system-imposed limit on the number of threads was - encountered.; WSAECONNREFUSED. Raised by connect(), listen(), - recv() + st = assert(unix.stat('/etc/passwd')) + unixts, nanos = st:birthtim() + year,mon,mday,hour,min,sec,gmtoffsec = unix.localtime(unixts) + Write('%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.9d%+.2d%.2d % { + year, mon, mday, hour, min, sec, nanos, + gmtoffsec / (60 * 60), math.abs(gmtoffsec) % 60}) - - `EHOSTDOWN`: Host is down. Raised by accept() + unix.Stat:mtim() + └─→ unixts:int, nanos:int - - `EHOSTUNREACH`: Host is unreachable. Raised by accept() + Last modified time. - - `EALREADY`: Connection already in progress. Raised by connect(), - send() + unix.Stat:atim() + └─→ unixts:int, nanos:int - - `ENODATA`: No message is available in xsi stream or named pipe is - being closed; no data available; barely in posix; returned by - ioctl; very close in spirit to EPIPE? + Last access time. + Please note that file systems are sometimes mounted with `noatime` + out of concern for i/o performance. Linux also provides `O_NOATIME` + as an option for open(). -CONSTANTS + On Windows NT this is the same as birth time. - kLogDebug - Integer for debug logging level. See Log. + unix.Stat:ctim() + └─→ unixts:int, nanos:int - kLogVerbose - Integer for verbose logging level, which is less than kLogDebug. + Complicated time. - kLogInfo - Integer for info logging level, which is less than kLogVerbose. + Means time file status was last changed on UNIX. - kLogWarn - Integer for warn logging level, which is less than kLogVerbose. + On Windows NT this is the same as birth time. - kLogError - Integer for error logging level, which is less than kLogWarn. + unix.Stat:blocks() + └─→ count512:int - kLogFatal - Integer for fatal logging level, which is less than kLogError. - Logging anything at this level will result in a backtrace and - process exit. + Number of 512-byte blocks used by storage medium. + This provides some indication of how much physical storage a file + actually consumes. For example, for small file systems, your system + might report this number as being 8, which means 4096 bytes. + + On Windows NT, if `O_COMPRESSED` is used for a file, then this + number will reflect the size *after* compression. you can use: + + st = assert(unix.stat("moby.txt")) + print('file size is %d bytes' % {st:size()}) + print('file takes up %d bytes of space' % {st:blocks() * 512}) + if GetHostOs() == 'WINDOWS' and st:flags() & 0x800 then + print('thanks to file system compression') + end + + To tell whether or not compression is being used on a file, + + unix.Stat:blksize() + └─→ bytes:int + + Block size that underlying device uses. + + This field might be of assistance in computing optimal i/o sizes. + + Please note this field has no relationship to blocks, as the latter + is fixed at a 512 byte size. + + unix.Stat:ino() + └─→ inode:int + + Inode number. + + This can be used to detect some other process used rename() to swap + out a file underneath you, so you can do a refresh. redbean does it + during each main process heartbeat for its own use cases. + + On Windows NT this is set to NtByHandleFileInformation::FileIndex. + + unix.Stat:dev() + └─→ dev:int + + ID of device containing file. + + On Windows NT this is set to + NtByHandleFileInformation::VolumeSerialNumber. + + unix.Stat:rdev() + └─→ rdev:int + + Information about device type. + + This value may be set to 0 or -1 for files that aren't devices, + depending on the operating system. unix.major() and unix.minor() + may be used to extract the device numbers. + + +──────────────────────────────────────────────────────────────────────────────── + + UNIX SIGSET OBJECT + + The unix.Sigset class defines a mutable bitset that may currently + contain 128 entries. See `unix.NSIG` to find out how many signals + your operating system actually supports. + + unix.Sigset(sig:int, ...) + └─→ unix.Sigset + + Constructs new signal bitset object. + + unix.Sigset:add(sig:int) + + Adds signal to bitset. + + unix.Sigset:remove(sig:int) + + Removes signal from bitset. + + unix.Sigset:fill() + + Sets all bits in signal bitset to true. + + unix.Sigset:clear() + + Sets all bits in signal bitset to false. + + unix.Sigset:contains(sig:int) + └─→ bool + + Returns true if `sig` is member of signal bitset. + + unix.Sigset:__repr() + unix.Sigset:__tostring() + + Returns Lua code string that recreates object. + + +──────────────────────────────────────────────────────────────────────────────── + + UNIX SIGNAL MAGNUMS + + unix.SIGINT + Terminal CTRL-C keystroke. + + unix.SIGQUIT + Terminal CTRL-\ keystroke. + + unix.SIGHUP + Terminal hangup or daemon reload; auto-broadcasted to process group. + + unix.SIGILL + Illegal instruction. + + unix.SIGTRAP + INT3 instruction. + + unix.SIGABRT + Process aborted. + + unix.SIGBUS + Valid memory access that went beyond underlying end of file. + + unix.SIGFPE + Illegal math. + + unix.SIGKILL + Terminate with extreme prejudice. + + unix.SIGUSR1 + Do whatever you want. + + unix.SIGUSR2 + Do whatever you want. + + unix.SIGSEGV + Invalid memory access. + + unix.SIGPIPE + Write to closed file descriptor. + + unix.SIGALRM + Sent by setitimer(). + + unix.SIGTERM + Terminate. + + unix.SIGCHLD + Child process exited or terminated and is now a zombie (unless this + is SIG_IGN or SA_NOCLDWAIT) or child process stopped due to terminal + i/o or profiling/debugging (unless you used SA_NOCLDSTOP) + + unix.SIGCONT + Child process resumed from profiling/debugging. + + unix.SIGSTOP + Child process stopped due to profiling/debugging. + + unix.SIGTSTP + Terminal CTRL-Z keystroke. + + unix.SIGTTIN + Terminal input for background process. + + unix.SIGTTOU + Terminal output for background process. + + unix.SIGXCPU + CPU time limit exceeded. + + unix.SIGXFSZ + File size limit exceeded. + + unix.SIGVTALRM + Virtual alarm clock. + + unix.SIGPROF + Profiling timer expired. + + unix.SIGWINCH + Terminal resized. + + unix.SIGPWR + Not implemented in most community editions of system five. + + +──────────────────────────────────────────────────────────────────────────────── + + UNIX ERRNO OBJECT + + This object is returned by system calls that fail. We prefer returning + an object because for many system calls, an error is part their normal + operation. For example, it's often desirable to use the errno() method + when performing a read() to check for EINTR. + + unix.Errno:errno() + └─→ errno:int + + Returns error magic number. + + The error number is always different for different platforms. On + UNIX systems, error numbers occupy the range [1,127] in practice. + The System V ABI reserves numbers as high as 4095. On Windows NT, + error numbers can go up to 65535. + + unix.Errno:winerr() + └─→ errno:int + + Returns Windows error number. + + On UNIX systems this is always 0. On Windows NT this will normally + be the same as errno(). Because Windows defines so many error codes, + there's oftentimes a multimapping between its error codes and System + Five. In those cases, this value reflect the GetLastError() result + at the time the error occurred. + + unix.Errno:name() + └─→ symbol:str + + Returns string of symbolic name of System Five error code. + + For example, this might return `"EINTR"`. + + unix.Errno:call() + └─→ symbol:str + + Returns name of system call that failed. + + For example, this might return `"read"` if read() was what failed. + + unix.Errno:doc() + └─→ symbol:str + + Returns English string describing System Five error code. + + For example, this might return `"Interrupted system call"`. + + unix.Errno:__tostring() + └─→ str + + Returns verbose string describing error. + + Different information components are delimited by slash. + + For example, this might return `"EINTR/4/Interrupted system call"`. + + On Windows NT this will include additional information about the + Windows error (including FormatMessage() output) if the WIN32 error + differs from the System Five error code. + + +──────────────────────────────────────────────────────────────────────────────── + + UNIX ERROR MAGNUMS + + unix.EINVAL + Invalid argument. + + Raised by [pretty much everything]. + + unix.ENOSYS + System call not available on this platform. On Windows this is + + Raised by chroot(), setuid(), setgid(), getsid(), setsid(). + + unix.ENOENT + No such file or directory. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), execve(), opendir(), inotify_add_watch(), link(), + mkdir(), mknod(), open(), readlink(), rename(), rmdir(), stat(), + swapon(), symlink(), truncate(), unlink(), utime(), utimensat(). + + unix.ENOTDIR + Not a directory. This means that a directory component in a supplied + path *existed* but wasn't a directory. For example, if you try to + `open("foo/bar")` and `foo` is a regular file, then `ENOTDIR` will + be returned. + + Raised by open(), access(), chdir(), chroot(), execve(), link(), + mkdir(), mknod(), opendir(), readlink(), rename(), rmdir(), stat(), + symlink(), truncate(), unlink(), utimensat(), bind(), chmod(), + chown(), fcntl(), futimesat(), inotify_add_watch(). + + unix.EINTR + The greatest of all errnos; crucial for building real time reliable + software. + + Raised by accept(), clock_nanosleep(), close(), connect(), dup(), + fcntl(), flock(), getrandom(), nanosleep(), open(), pause(), poll(), + ptrace(), read(), recv(), select(), send(), sigsuspend(), + sigwaitinfo(), truncate(), wait(), write(). + + unix.EIO + Raised by access(), acct(), chdir(), chmod(), chown(), chroot(), + close(), copy_file_range(), execve(), fallocate(), fsync(), + ioperm(), link(), madvise(), mbind(), pciconfig_read(), ptrace(), + read(), readlink(), sendfile(), statfs(), symlink(), + sync_file_range(), truncate(), unlink(), write(). + + unix.ENXIO + No such device or address. + + Raised by lseek(), open(), prctl() + + unix.E2BIG + Argument list too long. + + Raised by execve(), sched_setattr(). + + unix.ENOEXEC + Exec format error. + + Raised by execve(), uselib(). + + unix.ECHILD + No child process. + + Raised by wait(), waitpid(), waitid(), wait3(), wait4(). + + unix.ESRCH + No such process. + + Raised by getpriority(), getrlimit(), getsid(), ioprio_set(), + kill(), setpgid(), tkill(), utimensat(), + + unix.EBADF + Bad file descriptor; cf. EBADFD. + + Raised by accept(), access(), bind(), chdir(), chmod(), chown(), + close(), connect(), copy_file_range(), dup(), fcntl(), flock(), + fsync(), futimesat(), opendir(), getpeername(), getsockname(), + getsockopt(), inotify_add_watch(), inotify_rm_watch(), ioctl(), + link(), listen(), llseek(), lseek(), mkdir(), mknod(), mmap(), + open(), prctl(), read(), readahead(), readlink(), recv(), rename(), + select(), send(), shutdown(), splice(), stat(), symlink(), sync(), + sync_file_range(), timerfd_create(), truncate(), unlink(), + utimensat(), write(), + + unix.EAGAIN + Resource temporarily unavailable (e.g. SO_RCVTIMEO expired, too many + processes, too much memory locked, read or write with O_NONBLOCK + needs polling, etc.). + + Raised by accept(), connect(), fcntl(), fork(), getrandom(), + mincore(), mlock(), mmap(), mremap(), poll(), read(), select(), + send(), setresuid(), setreuid(), setuid(), sigwaitinfo(), splice(), + tee(), timer_create(), timerfd_create(), tkill(), write(), + + unix.EPIPE + Broken pipe. Returned by write(), send(). This happens when you try + to write data to a subprocess via a pipe() but the reader end has + already closed, possibly because the process died. Normally i/o + routines only return this if `SIGPIPE` doesn't kill the process. + Unlike default UNIX programs, redbean currently ignores `SIGPIPE` by + default, so this error code is a distinct possibility when pipes or + sockets are being used. + + unix.ENAMETOOLONG + Filename too long. Cosmopolitan Libc currently defines `PATH_MAX` as + 1024 characters. On UNIX that limit should only apply to system call + wrappers like realpath(). On Windows NT it's observed by all system + calls that accept a pathname. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + execve(), gethostname(), inotify_add_watch(), link(), mkdir(), + mknod(), open(), readlink(), rename(), rmdir(), stat(), symlink(), + truncate(), u unlink(), utimensat() + + unix.EACCES + Permission denied. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clock_getres(), connect(), execve(), fcntl(), getpriority(), + inotify_add_watch(), link(), mkdir(), mknod(), mmap(), mprotect(), + msgctl(), open(), prctl(), ptrace(), readlink(), rename(), rmdir(), + semget(), send(), setpgid(), socket(), stat(), symlink(), + truncate(), unlink(), uselib(), utime(), utimensat(), + + unix.ENOMEM + We require more vespene gas. + + Raised by access(), bind(), chdir(), chmod(), chown(), chroot(), + clone(), copy_file_range(), execve(), fanotify_init(), fork(), + getgroups(), getrlimit(), inotify_add_watch(), inotify_init(), + ioperm(), link(), mbind(), mincore(), mkdir(), mknod(), mlock(), + mmap(), mprotect(), mremap(), msync(), open(), poll(), readlink(), + recv(), rename(), rmdir(), select(), send(), sigaltstack(), + splice(), stat(), subpage_prot(), swapon(), symlink(), + sync_file_range(), tee(), timer_create(), timerfd_create(), + unlink(). + + unix.EPERM + Operation not permitted. + + Raised by accept(), chmod(), chown(), chroot(), copy_file_range(), + execve(), fallocate(), fanotify_init(), fcntl(), futex(), + get_robust_list(), getdomainname(), getgroups(), gethostname(), + getpriority(), getrlimit(), getsid(), gettimeofday(), idle(), + init_module(), io_submit(), ioctl_console(), ioctl_ficlonerange(), + ioctl_fideduperange(), ioperm(), iopl(), ioprio_set(), keyctl(), + kill(), link(), lookup_dcookie(), madvise(), mbind(), membarrier(), + migrate_pages(), mkdir(), mknod(), mlock(), mmap(), mount(), + move_pages(), msgctl(), nice(), open(), open_by_handle_at(), + pciconfig_read(), perf_event_open(), pidfd_getfd(), + pidfd_send_signal(), pivot_root(), prctl(), process_vm_readv(), + ptrace(), quotactl(), reboot(), rename(), request_key(), rmdir(), + rt_sigqueueinfo(), sched_setaffinity(), sched_setattr(), + sched_setparam(), sched_setscheduler(), seteuid(), setfsgid(), + setfsuid(), setgid(), setns(), setpgid(), setresuid(), setreuid(), + setsid(), setuid(), setup(), setxattr(), sigaltstack(), + spu_create(), stime(), swapon(), symlink(), syslog(), truncate(), + unlink(), utime(), utimensat(), write() + + unix.ENOTBLK + Block device required. + + Raised by umount(). + + unix.EBUSY + Device or resource busy. + + Raised by dup(), fcntl(), msync(), prctl(), ptrace(), rename(), + rmdir(). + + unix.EEXIST + File exists. + + Raised by inotify_add_watch(), link(), mkdir(), mknod(), mmap(), + open(), rename(), rmdir(), symlink() + + unix.EXDEV + Improper link. + + Raised by copy_file_range(), link(), rename(). + + unix.ENODEV + No such device. + + Raised by arch_prctl(), mmap(), open(), prctl(), timerfd_create(). + + unix.EISDIR + Is a a directory. + + Raised by copy_file_range(), execve(), open(), read(), rename(), + truncate(), unlink(). + + unix.ENFILE + Too many open files in system. + + Raised by accept(), execve(), inotify_init(), mmap(), open(), + pipe(), socket(), socketpair(), swapon(), timerfd_create(), + uselib(), userfaultfd(). + + unix.EMFILE + Too many open files. + + Raised by accept(), dup(), execve(), fanotify_init(), fcntl(), + inotify_init(), open(), pipe(), socket(), socketpair(), + timerfd_create(). + + unix.ENOTTY + Inappropriate i/o control operation. + + Raised by ioctl(). + + unix.ETXTBSY + Won't open executable that's executing in write mode. + + Raised by access(), copy_file_range(), execve(), mmap(), open(), + truncate(). + + unix.EFBIG + File too large. + + Raised by copy_file_range(), open(), truncate(), write(). + + unix.ENOSPC + No space left on device. + + Raised by copy_file_range(), fsync(), inotify_add_watch(), link(), + mkdir(), mknod(), open(), rename(), symlink(), sync_file_range(), + write(). + + unix.EDQUOT + Disk quota exceeded. + + Raised by link(), mkdir(), mknod(), open(), rename(), symlink(), + write(). + + unix.ESPIPE + Invalid seek. + + Raised by lseek(), splice(), sync_file_range(). + + unix.EROFS + Read-only filesystem. + + Raised by access(), bind(), chmod(), chown(), link(), mkdir(), + mknod(), open(), rename(), rmdir(), symlink(), truncate(), unlink(), + utime(), utimensat(). + + unix.EMLINK + Too many links; + + raised by link(), mkdir(), rename(). + + unix.ERANGE + Result too large. + + Raised by prctl(). + + unix.EDEADLK + Resource deadlock avoided. + + Raised by fcntl(). + + unix.ENOLCK + No locks available. + + Raised by fcntl(), flock(). + + unix.ENOTEMPTY + Directory not empty. Raised by rmdir(). + + unix.ELOOP + Too many levels of symbolic links. Raised by access(), bind(), + chdir(), chmod(), chown(), chroot(), execve(), link(), mkdir(), + mknod(), open(), readlink(), rename(), rmdir(), stat(), symlink(), + truncate(), unlink(), utimensat(). + + unix.ENOMSG + Raised by msgop(). + + unix.EIDRM + Identifier removed. + + Raised by msgctl(). + + unix.ETIME + Timer expired; timer expired. + + Raised by connect(). + + unix.EPROTO + Raised by accept(), connect(), socket(), socketpair(). + + unix.EOVERFLOW + Raised by copy_file_range(), fanotify_init(), lseek(), mmap(), + open(), stat(), statfs() + + unix.ENOTSOCK + Not a socket. + + Raised by accept(), bind(), connect(), getpeername(), getsockname(), + getsockopt(), listen(), recv(), send(), shutdown(). + + unix.EDESTADDRREQ + Destination address required. + + Raised by send(), write(). + + unix.EMSGSIZE + Message too long. + + Raised by send(). + + unix.EPROTOTYPE + Protocol wrong type for socket. + + Raised by connect(). + + unix.ENOPROTOOPT + Protocol not available. + + Raised by getsockopt(), accept(). + + unix.EPROTONOSUPPORT + Protocol not supported. + + Raised by socket(), socketpair(). + + unix.ESOCKTNOSUPPORT + Socket type not supported. + + unix.ENOTSUP + Operation not supported. + + Raised by chmod(), clock_getres(), clock_nanosleep(), + timer_create(). + + unix.EOPNOTSUPP + Socket operation not supported. + + Raised by accept(), listen(), mmap(), prctl(), readv(), send(), + socketpair(). + + unix.EPFNOSUPPORT + Protocol family not supported. + + unix.EAFNOSUPPORT + Address family not supported. + + Raised by connect(), socket(), socketpair() + + unix.EADDRINUSE + Address already in use. + + Raised by bind(), connect(), listen() + + unix.EADDRNOTAVAIL + Address not available. + + Raised by bind(), connect(). + + unix.ENETDOWN + Network is down. + + Raised by accept() + + unix.ENETUNREACH + Host is unreachable. + + Raised by accept(), connect() + + unix.ENETRESET + Connection reset by network. + + unix.ECONNABORTED + Connection reset before accept. + + Raised by accept(). + + unix.ECONNRESET + Connection reset by client. + + Raised by send(). + + unix.ENOBUFS + No buffer space available; + + raised by getpeername(), getsockname(), send(). + + unix.EISCONN + Socket is connected. + + Raised by connect(), send(). + + unix.ENOTCONN + Socket is not connected. + + Raised by getpeername(), recv(), send(), shutdown(). + + unix.ESHUTDOWN + Cannot send after transport endpoint shutdown; note that shutdown + write is an `EPIPE`. + + unix.ETOOMANYREFS + Too many references: cannot splice. + + Raised by sendmsg(). + + unix.ETIMEDOUT + Connection timed out; ; WSAETIMEDOUT; + + raised by connect(). + + unix.ECONNREFUSED + System-imposed limit on the number of threads was encountered. + + Raised by connect(), listen(), recv() + + unix.EHOSTDOWN + Host is down. + + Raised by accept() + + unix.EHOSTUNREACH + Host is unreachable. + + Raised by accept() + + unix.EALREADY + Connection already in progress. + + Raised by connect(), send() + + unix.ENODATA + No message is available in xsi stream or named pipe is being closed; + no data available; barely in posix; returned by ioctl; very close in + spirit to EPIPE? + + +──────────────────────────────────────────────────────────────────────────────── + + UNIX MISCELLANEOUS MAGNUMS + + unix.ARG_MAX + + Returns maximum length of arguments for new processes. + + This is the character limit when calling execve(). It's the sum of + the lengths of `argv` and `envp` including any nul terminators and + pointer arrays. For example to see how much your shell `envp` uses + + $ echo $(($(env | wc -c) + 1 + ($(env | wc -l) + 1) * 8)) + 758 + + POSIX mandates this be 4096 or higher. On Linux this it's 128*1024. + On Windows NT it's 32767*2 because CreateProcess lpCommandLine and + environment block are separately constrained to 32,767 characters. + Most other systems define this limit much higher. + + unix.BUFSIZ + + Returns default buffer size. + + The UNIX module does not perform any buffering between calls. + + Each time a read or write is performed via the UNIX API your redbean + will allocate a buffer of this size by default. This current default + would be 4096 across platforms. + + unix.CLK_TCK + + Returns the scheduler frequency. + + This is granularity at which the kernel does work. For example, the + Linux kernel normally operates at 100hz so its CLK_TCK will be 100. + + This value is useful for making sense out of unix.Rusage data. + + unix.PIPE_BUF + + Returns maximum size at which pipe i/o is guaranteed atomic. + + POSIX requires this be at least 512. Linux is more generous and + allows 4096. On Windows NT this is currently 4096, and it's the + parameter redbean passes to CreateNamedPipe(). + + unix.PATH_MAX + + Returns maximum length of file path. + + This applies to a complete path being passed to system calls. + + POSIX.1 XSI requires this be at least 1024 so that's what most + platforms support. On Windows NT, the limit is technically 260 + characters. Your redbean works around that by prefixing `//?/` + to your paths as needed. On Linux this limit will be 4096, but + that won't be the case for functions such as realpath that are + implemented at the C library level; however such functions are + the exception rather than the norm, and report enametoolong(), + when exceeding the libc limit. + + unix.NAME_MAX + + Returns maximum length of file path component. + + POSIX requires this be at least 14. Most operating systems define it + as 255. It's a good idea to not exceed 253 since that's the limit on + DNS labels. + + unix.NSIG + + Returns maximum number of signals supported by underlying system. + + The limit for unix.Sigset is 128 to support FreeBSD, but most + operating systems define this much lower, like 32. This constant + reflects the value chosen by the underlying operating system. + + +──────────────────────────────────────────────────────────────────────────────── + SEE ALSO - https://justine.lol/redbean/index.html + https://redbean.dev/ https://news.ycombinator.com/item?id=26271117 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 new file mode 100644 index 000000000..503fb899f --- /dev/null +++ b/tool/net/lfuncs.c @@ -0,0 +1,680 @@ +/*-*- 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 "dsp/scale/cdecimate2xuint8x8.h" +#include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/rusage.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/macros.internal.h" +#include "libc/math.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" +#include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/rdtscp.h" +#include "libc/rand/rand.h" +#include "libc/runtime/gc.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/sysconf.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" +#include "net/http/http.h" +#include "net/http/ip.h" +#include "net/http/url.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" +#include "third_party/mbedtls/md.h" +#include "third_party/mbedtls/md5.h" +#include "third_party/mbedtls/platform.h" +#include "third_party/mbedtls/sha1.h" +#include "third_party/mbedtls/sha256.h" +#include "third_party/mbedtls/sha512.h" +#include "tool/net/lfuncs.h" + +static int Rdpid(void) { + return rdpid(); +} + +int LuaHex(lua_State *L) { + char b[19]; + uint64_t x; + x = luaL_checkinteger(L, 1); + FormatHex64(b, x, true); + lua_pushstring(L, b); + return 1; +} + +int LuaOct(lua_State *L) { + char b[24]; + uint64_t x; + x = luaL_checkinteger(L, 1); + FormatOctal64(b, x, true); + lua_pushstring(L, b); + return 1; +} + +int LuaBin(lua_State *L) { + char b[67]; + uint64_t x; + x = luaL_checkinteger(L, 1); + FormatBinary64(b, x, 2); + lua_pushstring(L, b); + return 1; +} + +int LuaGetTime(lua_State *L) { + lua_pushnumber(L, nowl()); + return 1; +} + +int LuaSleep(lua_State *L) { + usleep(1e6 * luaL_checknumber(L, 1)); + return 0; +} + +int LuaRdtsc(lua_State *L) { + lua_pushinteger(L, rdtsc()); + return 1; +} + +int LuaGetCpuNode(lua_State *L) { + lua_pushinteger(L, TSC_AUX_NODE(Rdpid())); + return 1; +} + +int LuaGetCpuCore(lua_State *L) { + lua_pushinteger(L, TSC_AUX_CORE(Rdpid())); + return 1; +} + +int LuaGetCpuCount(lua_State *L) { + lua_pushinteger(L, GetCpuCount()); + return 1; +} + +int LuaGetLogLevel(lua_State *L) { + lua_pushinteger(L, __log_level); + return 1; +} + +int LuaSetLogLevel(lua_State *L) { + __log_level = luaL_checkinteger(L, 1); + return 0; +} + +static int LuaRand(lua_State *L, uint64_t impl(void)) { + lua_pushinteger(L, impl()); + return 1; +} + +int LuaLemur64(lua_State *L) { + return LuaRand(L, lemur64); +} + +int LuaRand64(lua_State *L) { + return LuaRand(L, rand64); +} + +int LuaRdrand(lua_State *L) { + return LuaRand(L, rdrand); +} + +int LuaRdseed(lua_State *L) { + return LuaRand(L, rdseed); +} + +int LuaDecimate(lua_State *L) { + size_t n, m; + const char *s; + unsigned char *p; + s = luaL_checklstring(L, 1, &n); + m = ROUNDUP(n, 16); + p = xmalloc(m); + bzero(p + n, m - n); + cDecimate2xUint8x8(m, p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); + lua_pushlstring(L, (char *)p, (n + 1) >> 1); + free(p); + return 1; +} + +int LuaMeasureEntropy(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushnumber(L, MeasureEntropy(s, n)); + return 1; +} + +int LuaGetHostOs(lua_State *L) { + const char *s = NULL; + if (IsLinux()) { + s = "LINUX"; + } else if (IsMetal()) { + s = "METAL"; + } else if (IsWindows()) { + s = "WINDOWS"; + } else if (IsXnu()) { + s = "XNU"; + } else if (IsOpenbsd()) { + s = "OPENBSD"; + } else if (IsFreebsd()) { + s = "FREEBSD"; + } else if (IsNetbsd()) { + s = "NETBSD"; + } + if (s) { + lua_pushstring(L, s); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaFormatIp(lua_State *L) { + char b[16]; + uint32_t ip; + ip = htonl(luaL_checkinteger(L, 1)); + inet_ntop(AF_INET, &ip, b, sizeof(b)); + lua_pushstring(L, b); + return 1; +} + +int LuaParseIp(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushinteger(L, ParseIp(s, n)); + return 1; +} + +static int LuaIsIp(lua_State *L, bool IsIp(uint32_t)) { + lua_pushboolean(L, IsIp(luaL_checkinteger(L, 1))); + return 1; +} + +int LuaIsPublicIp(lua_State *L) { + return LuaIsIp(L, IsPublicIp); +} + +int LuaIsPrivateIp(lua_State *L) { + return LuaIsIp(L, IsPrivateIp); +} + +int LuaIsLoopbackIp(lua_State *L) { + return LuaIsIp(L, IsLoopbackIp); +} + +int LuaCategorizeIp(lua_State *L) { + lua_pushstring(L, GetIpCategoryName(CategorizeIp(luaL_checkinteger(L, 1)))); + return 1; +} + +int LuaFormatHttpDateTime(lua_State *L) { + char buf[30]; + lua_pushstring(L, FormatUnixHttpDateTime(buf, luaL_checkinteger(L, 1))); + return 1; +} + +int LuaParseHttpDateTime(lua_State *L) { + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + lua_pushinteger(L, ParseHttpDateTime(s, n)); + return 1; +} + +int LuaParseParams(lua_State *L) { + void *m; + size_t size; + const char *data; + struct UrlParams h; + data = luaL_checklstring(L, 1, &size); + bzero(&h, sizeof(h)); + m = ParseParams(data, size, &h); + LuaPushUrlParams(L, &h); + free(h.p); + free(m); + return 1; +} + +int LuaParseHost(lua_State *L) { + void *m; + size_t n; + struct Url h; + const char *p; + bzero(&h, sizeof(h)); + p = luaL_checklstring(L, 1, &n); + m = ParseHost(p, n, &h); + lua_newtable(L); + LuaPushUrlView(L, &h.host); + LuaPushUrlView(L, &h.port); + free(m); + return 1; +} + +int LuaPopcnt(lua_State *L) { + lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1))); + return 1; +} + +int LuaBsr(lua_State *L) { + long x; + if ((x = luaL_checkinteger(L, 1))) { + lua_pushinteger(L, bsrl(x)); + return 1; + } else { + luaL_argerror(L, 1, "zero"); + unreachable; + } +} + +int LuaBsf(lua_State *L) { + long x; + if ((x = luaL_checkinteger(L, 1))) { + lua_pushinteger(L, bsfl(x)); + return 1; + } else { + luaL_argerror(L, 1, "zero"); + unreachable; + } +} + +static int LuaHash(lua_State *L, uint32_t H(uint32_t, const void *, size_t)) { + long i; + size_t n; + const char *p; + i = luaL_checkinteger(L, 1); + p = luaL_checklstring(L, 2, &n); + lua_pushinteger(L, H(i, p, n)); + return 1; +} + +int LuaCrc32(lua_State *L) { + return LuaHash(L, crc32_z); +} + +int LuaCrc32c(lua_State *L) { + return LuaHash(L, crc32c); +} + +int LuaIndentLines(lua_State *L) { + void *p; + size_t n, j; + p = luaL_checklstring(L, 1, &n); + j = luaL_optinteger(L, 2, 1); + if (!(0 <= j && j <= 65535)) { + luaL_argerror(L, 2, "not in range 0..65535"); + unreachable; + } + p = IndentLines(p, n, &n, j); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +int LuaGetMonospaceWidth(lua_State *L) { + int w; + if (lua_isinteger(L, 1)) { + w = wcwidth(lua_tointeger(L, 1)); + } else if (lua_isstring(L, 1)) { + w = strwidth(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0) & 7); + } else { + luaL_argerror(L, 1, "not integer or string"); + unreachable; + } + lua_pushinteger(L, w); + return 1; +} + +int LuaSlurp(lua_State *L) { + char *p, *f; + size_t n; + f = luaL_checkstring(L, 1); + if ((p = xslurp(f, &n))) { + lua_pushlstring(L, p, n); + free(p); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, gc(xasprintf("Can't slurp file %`'s: %m", f))); + return 2; + } +} + +static int LuaCheckControlFlags(lua_State *L, int idx) { + int f = luaL_checkinteger(L, idx); + if (f & ~(kControlWs | kControlC0 | kControlC1)) { + luaL_argerror(L, idx, "invalid control flags"); + unreachable; + } + return f; +} + +int LuaHasControlCodes(lua_State *L) { + int f; + size_t n; + const char *p; + p = luaL_checklstring(L, 1, &n); + f = LuaCheckControlFlags(L, 2); + lua_pushboolean(L, HasControlCodes(p, n, f) != -1); + return 1; +} + +int LuaEncodeLatin1(lua_State *L) { + int f; + char *p; + size_t n; + p = luaL_checklstring(L, 1, &n); + f = LuaCheckControlFlags(L, 2); + p = EncodeLatin1(p, n, &n, f); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +int LuaGetRandomBytes(lua_State *L) { + char *p; + size_t n = luaL_optinteger(L, 1, 16); + if (!(n > 0 && n <= 256)) { + luaL_argerror(L, 1, "not in range 1..256"); + unreachable; + } + p = xmalloc(n); + CHECK_EQ(n, getrandom(p, n, 0)); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +int LuaGetHttpReason(lua_State *L) { + lua_pushstring(L, GetHttpReason(luaL_checkinteger(L, 1))); + return 1; +} + +int LuaGetCryptoHash(lua_State *L) { + size_t hl, pl, kl; + uint8_t d[64]; + mbedtls_md_context_t ctx; + // get hash name, payload, and key + void *h = luaL_checklstring(L, 1, &hl); + void *p = luaL_checklstring(L, 2, &pl); + void *k = luaL_optlstring(L, 3, "", &kl); + const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h); + if (!digest) return luaL_argerror(L, 1, "unknown hash type"); + if (kl == 0) { + // no key provided, run generic hash function + if ((digest->f_md)(p, pl, d)) return luaL_error(L, "bad input data"); + } else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) { + return luaL_error(L, "bad input data"); + } + lua_pushlstring(L, (void *)d, digest->size); + mbedtls_platform_zeroize(d, sizeof(d)); + return 1; +} + +static dontinline int LuaIsValid(lua_State *L, bool V(const char *, size_t)) { + size_t size; + const char *data; + data = luaL_checklstring(L, 1, &size); + lua_pushboolean(L, V(data, size)); + return 1; +} + +int LuaIsValidHttpToken(lua_State *L) { + return LuaIsValid(L, IsValidHttpToken); +} + +int LuaIsAcceptablePath(lua_State *L) { + return LuaIsValid(L, IsAcceptablePath); +} + +int LuaIsReasonablePath(lua_State *L) { + return LuaIsValid(L, IsReasonablePath); +} + +int LuaIsAcceptableHost(lua_State *L) { + return LuaIsValid(L, IsAcceptableHost); +} + +int LuaIsAcceptablePort(lua_State *L) { + return LuaIsValid(L, IsAcceptablePort); +} + +static dontinline int LuaCoderImpl(lua_State *L, + char *C(const char *, size_t, size_t *)) { + void *p; + size_t n; + p = luaL_checklstring(L, 1, &n); + p = C(p, n, &n); + lua_pushlstring(L, p, n); + free(p); + return 1; +} + +static dontinline int LuaCoder(lua_State *L, + char *C(const char *, size_t, size_t *)) { + return LuaCoderImpl(L, C); +} + +int LuaUnderlong(lua_State *L) { + return LuaCoder(L, Underlong); +} + +int LuaEncodeBase64(lua_State *L) { + return LuaCoder(L, EncodeBase64); +} + +int LuaDecodeBase64(lua_State *L) { + return LuaCoder(L, DecodeBase64); +} + +int LuaDecodeLatin1(lua_State *L) { + return LuaCoder(L, DecodeLatin1); +} + +int LuaEscapeHtml(lua_State *L) { + return LuaCoder(L, EscapeHtml); +} + +int LuaEscapeParam(lua_State *L) { + return LuaCoder(L, EscapeParam); +} + +int LuaEscapePath(lua_State *L) { + return LuaCoder(L, EscapePath); +} + +int LuaEscapeHost(lua_State *L) { + return LuaCoder(L, EscapeHost); +} + +int LuaEscapeIp(lua_State *L) { + return LuaCoder(L, EscapeIp); +} + +int LuaEscapeUser(lua_State *L) { + return LuaCoder(L, EscapeUser); +} + +int LuaEscapePass(lua_State *L) { + return LuaCoder(L, EscapePass); +} + +int LuaEscapeSegment(lua_State *L) { + return LuaCoder(L, EscapeSegment); +} + +int LuaEscapeFragment(lua_State *L) { + return LuaCoder(L, EscapeFragment); +} + +int LuaEscapeLiteral(lua_State *L) { + return LuaCoder(L, EscapeJsStringLiteral); +} + +int LuaVisualizeControlCodes(lua_State *L) { + return LuaCoder(L, VisualizeControlCodes); +} + +static dontinline int LuaHasherImpl(lua_State *L, size_t k, + int H(const void *, size_t, uint8_t *)) { + void *p; + size_t n; + uint8_t d[64]; + p = luaL_checklstring(L, 1, &n); + H(p, n, d); + lua_pushlstring(L, (void *)d, k); + mbedtls_platform_zeroize(d, sizeof(d)); + return 1; +} + +static dontinline int LuaHasher(lua_State *L, size_t k, + int H(const void *, size_t, uint8_t *)) { + return LuaHasherImpl(L, k, H); +} + +int LuaMd5(lua_State *L) { + return LuaHasher(L, 16, mbedtls_md5_ret); +} + +int LuaSha1(lua_State *L) { + return LuaHasher(L, 20, mbedtls_sha1_ret); +} + +int LuaSha224(lua_State *L) { + return LuaHasher(L, 28, mbedtls_sha256_ret_224); +} + +int LuaSha256(lua_State *L) { + return LuaHasher(L, 32, mbedtls_sha256_ret_256); +} + +int LuaSha384(lua_State *L) { + return LuaHasher(L, 48, mbedtls_sha512_ret_384); +} + +int LuaSha512(lua_State *L) { + return LuaHasher(L, 64, mbedtls_sha512_ret_512); +} + +int LuaIsHeaderRepeatable(lua_State *L) { + int h; + bool r; + size_t n; + const char *s; + s = luaL_checklstring(L, 1, &n); + if ((h = GetHttpHeader(s, n)) != -1) { + r = kHttpRepeatable[h]; + } else { + r = false; + } + lua_pushboolean(L, r); + return 1; +} + +void LuaPushUrlView(lua_State *L, struct UrlView *v) { + if (v->p) { + lua_pushlstring(L, v->p, v->n); + } else { + lua_pushnil(L); + } +} + +static int64_t GetInterrupts(void) { + struct rusage ru; + if (!getrusage(RUSAGE_SELF, &ru)) { + return ru.ru_nivcsw; + } else { + return 0; + } +} + +static int DoNothing(lua_State *L) { + return 0; +} + +int LuaBenchmark(lua_State *L) { + uint64_t t1, t2; + int64_t interrupts; + double avgticks, overhead; + 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); + lua_gc(L, LUA_GCSTOP); + + for (attempts = 0;;) { + lua_gc(L, LUA_GCCOLLECT); + sched_yield(); + core = TSC_AUX_CORE(Rdpid()); + interrupts = GetInterrupts(); + for (avgticks = iter = 1; iter < count; ++iter) { + lua_pushcfunction(L, DoNothing); + t1 = __startbench_m(); + lua_call(L, 0, 0); + t2 = __endbench_m(); + 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"); + } + } + overhead = avgticks; + + for (attempts = 0;;) { + lua_gc(L, LUA_GCCOLLECT); + sched_yield(); + core = TSC_AUX_CORE(Rdpid()); + interrupts = GetInterrupts(); + for (avgticks = iter = 1; iter < count; ++iter) { + lua_pushvalue(L, 1); + t1 = __startbench_m(); + lua_call(L, 0, 0); + t2 = __endbench_m(); + 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"); + } + } + avgticks = MAX(avgticks - overhead, 0); + + lua_gc(L, LUA_GCRESTART); + lua_pushinteger(L, ConvertTicksToNanos(round(avgticks))); + lua_pushinteger(L, round(avgticks)); + lua_pushinteger(L, round(overhead)); + lua_pushinteger(L, attempts); + return 4; +} diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h new file mode 100644 index 000000000..301c8a49e --- /dev/null +++ b/tool/net/lfuncs.h @@ -0,0 +1,89 @@ +#ifndef COSMOPOLITAN_TOOL_NET_LFUNCS_H_ +#define COSMOPOLITAN_TOOL_NET_LFUNCS_H_ +#include "net/http/url.h" +#include "third_party/lua/lua.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int LuaMaxmind(lua_State *); +int LuaRe(lua_State *); +int LuaUnix(lua_State *); +int luaopen_argon2(lua_State *); +int luaopen_lsqlite3(lua_State *); + +int LuaBenchmark(lua_State *); +int LuaBin(lua_State *); +int LuaBsf(lua_State *); +int LuaBsr(lua_State *); +int LuaCategorizeIp(lua_State *); +int LuaCrc32(lua_State *); +int LuaCrc32c(lua_State *); +int LuaDecimate(lua_State *); +int LuaDecodeBase64(lua_State *); +int LuaDecodeLatin1(lua_State *); +int LuaEncodeBase64(lua_State *); +int LuaEncodeLatin1(lua_State *); +int LuaEscapeFragment(lua_State *); +int LuaEscapeHost(lua_State *); +int LuaEscapeHtml(lua_State *); +int LuaEscapeIp(lua_State *); +int LuaEscapeLiteral(lua_State *); +int LuaEscapeParam(lua_State *); +int LuaEscapePass(lua_State *); +int LuaEscapePath(lua_State *); +int LuaEscapeSegment(lua_State *); +int LuaEscapeUser(lua_State *); +int LuaFormatHttpDateTime(lua_State *); +int LuaFormatIp(lua_State *); +int LuaGetCpuCore(lua_State *); +int LuaGetCpuCount(lua_State *); +int LuaGetCpuNode(lua_State *); +int LuaGetCryptoHash(lua_State *); +int LuaGetHostOs(lua_State *); +int LuaGetHttpReason(lua_State *); +int LuaGetLogLevel(lua_State *); +int LuaGetMonospaceWidth(lua_State *); +int LuaGetRandomBytes(lua_State *); +int LuaGetTime(lua_State *); +int LuaHasControlCodes(lua_State *); +int LuaHex(lua_State *); +int LuaIndentLines(lua_State *); +int LuaIsAcceptableHost(lua_State *); +int LuaIsAcceptablePath(lua_State *); +int LuaIsAcceptablePort(lua_State *); +int LuaIsHeaderRepeatable(lua_State *); +int LuaIsLoopbackIp(lua_State *); +int LuaIsPrivateIp(lua_State *); +int LuaIsPublicIp(lua_State *); +int LuaIsReasonablePath(lua_State *); +int LuaIsValidHttpToken(lua_State *); +int LuaLemur64(lua_State *); +int LuaMd5(lua_State *); +int LuaMeasureEntropy(lua_State *); +int LuaOct(lua_State *); +int LuaParseHost(lua_State *); +int LuaParseHttpDateTime(lua_State *); +int LuaParseIp(lua_State *); +int LuaParseParams(lua_State *); +int LuaPopcnt(lua_State *); +int LuaRand64(lua_State *); +int LuaRdrand(lua_State *); +int LuaRdseed(lua_State *); +int LuaRdtsc(lua_State *); +int LuaSetLogLevel(lua_State *); +int LuaSha1(lua_State *); +int LuaSha224(lua_State *); +int LuaSha256(lua_State *); +int LuaSha384(lua_State *); +int LuaSha512(lua_State *); +int LuaSleep(lua_State *); +int LuaSlurp(lua_State *); +int LuaUnderlong(lua_State *); +int LuaVisualizeControlCodes(lua_State *); + +void LuaPushUrlView(lua_State *, struct UrlView *); +char *FormatUnixHttpDateTime(char *, int64_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_NET_LFUNCS_H_ */ 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/luacheck.h b/tool/net/luacheck.h index c5506387a..b800e48f8 100644 --- a/tool/net/luacheck.h +++ b/tool/net/luacheck.h @@ -7,13 +7,15 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define AssertLuaStackIsEmpty(L) \ - do { \ - if (lua_gettop(L)) { \ - char *s = LuaFormatStack(L); \ - WARNF("lua stack should be empty!\n%s", s); \ - free(s); \ - } \ +#define AssertLuaStackIsAt(L, level) \ + do { \ + if (lua_gettop(L) > level) { \ + char *s = LuaFormatStack(L); \ + WARNF("lua stack should be at %d;" \ + " extra values ignored:\n%s", \ + level, s); \ + free(s); \ + } \ } while (0) COSMOPOLITAN_C_END_ diff --git a/tool/net/lunix.c b/tool/net/lunix.c index e20022b9e..f5c69240a 100644 --- a/tool/net/lunix.c +++ b/tool/net/lunix.c @@ -17,29 +17,39 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" +#include "libc/calls/makedev.h" #include "libc/calls/sigbits.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/struct/bpf.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/struct/itimerval.h" +#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timeval.h" #include "libc/calls/ucontext.h" #include "libc/dns/dns.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" -#include "libc/fmt/kerrornames.internal.h" +#include "libc/fmt/magnumstrs.internal.h" #include "libc/intrin/kprintf.h" -#include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/mem/fmt.h" #include "libc/mem/mem.h" +#include "libc/nt/runtime.h" +#include "libc/runtime/clktck.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sock/syslog.h" +#include "libc/stdio/append.internal.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/at.h" @@ -47,8 +57,10 @@ #include "libc/sysv/consts/dt.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" +#include "libc/sysv/consts/ip.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/itimer.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/log.h" #include "libc/sysv/consts/msg.h" #include "libc/sysv/consts/nr.h" @@ -57,16 +69,23 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/rlimit.h" +#include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/shut.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sio.h" +#include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/sol.h" +#include "libc/sysv/consts/tcp.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" +#include "libc/time/struct/tm.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/lgc.h" #include "third_party/lua/lua.h" #include "third_party/lua/luaconf.h" #include "tool/net/luacheck.h" @@ -76,18 +95,64 @@ * @support Linux, Mac, Windows, FreeBSD, NetBSD, OpenBSD */ -struct UnixStat { - int refs; - struct stat st; -}; - -struct UnixDir { - int refs; - DIR *dir; +struct UnixErrno { + int errno; + int winerr; + const char *call; }; static lua_State *GL; +static void *LuaUnixRealloc(lua_State *L, void *p, size_t n) { + void *p2; + if ((p2 = realloc(p, n))) { + return p2; + } + if (IsLegalSize(n)) { + luaC_fullgc(L, 1); + p2 = realloc(p, n); + } + return p2; +} + +static void *LuaUnixAllocRaw(lua_State *L, size_t n) { + return LuaUnixRealloc(L, 0, n); +} + +static void *LuaUnixAlloc(lua_State *L, size_t n) { + void *p; + if ((p = LuaUnixAllocRaw(L, n))) { + bzero(p, n); + } + return p; +} + +static lua_Integer FixLimit(long x) { + if (0 <= x && x < RLIM_INFINITY) { + return x; + } else { + return -1; + } +} + +static void LuaPushSigset(lua_State *L, struct sigset set) { + struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1); + luaL_setmetatable(L, "unix.Sigset"); + *sp = set; +} + +static void LuaPushStat(lua_State *L, struct stat *st) { + struct stat *stp = lua_newuserdatauv(L, sizeof(*stp), 1); + luaL_setmetatable(L, "unix.Stat"); + *stp = *st; +} + +static void LuaPushRusage(lua_State *L, struct rusage *set) { + struct rusage *sp = lua_newuserdatauv(L, sizeof(*sp), 1); + luaL_setmetatable(L, "unix.Rusage"); + *sp = *set; +} + static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); @@ -98,67 +163,64 @@ static dontinline int ReturnInteger(lua_State *L, lua_Integer x) { return 1; } +static dontinline int ReturnBoolean(lua_State *L, int x) { + lua_pushboolean(L, !!x); + return 1; +} + static dontinline int ReturnString(lua_State *L, const char *x) { lua_pushstring(L, 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); +static int SysretErrno(lua_State *L, const char *call, int olderr) { + struct UnixErrno *ep; + int i, unixerr, winerr; + unixerr = errno; + winerr = GetLastError(); + if (!IsTiny() && !(0 < unixerr && unixerr < (!IsWindows() ? 4096 : 65536))) { + WARNF("errno should not be %d", unixerr); + } + lua_pushnil(L); + ep = lua_newuserdatauv(L, sizeof(*ep), 1); + luaL_setmetatable(L, "unix.Errno"); + ep->errno = unixerr; + ep->winerr = winerr; + ep->call = call; + errno = olderr; return 2; } -static int ReturnErrno(lua_State *L, int nils, int olderr) { - int i; - for (i = 0; i < nils; ++i) { - lua_pushnil(L); +int SysretBool(lua_State *L, const char *call, int olderr, int rc) { + 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 1; + } else { + return SysretErrno(L, call, olderr); } - lua_pushinteger(L, errno); - errno = olderr; - return nils + 1; } -static int ReturnRc(lua_State *L, int64_t rc, int olderr) { +static int SysretInteger(lua_State *L, const char *call, int olderr, + int64_t rc) { if (rc != -1) { + if (!IsTiny() && olderr != errno) { + WARNF("errno unexpectedly changed %d → %d", olderr, errno); + } lua_pushinteger(L, rc); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, call, olderr); } } -static char **ConvertLuaArrayToStringList(lua_State *L, int i) { - int j, n; - char **p; - luaL_checktype(L, i, LUA_TTABLE); - lua_len(L, i); - n = lua_tointeger(L, -1); - lua_pop(L, 1); - p = xcalloc(n + 1, sizeof(*p)); - for (j = 1; j <= n; ++j) { - lua_geti(L, i, j); - p[j - 1] = strdup(lua_tostring(L, -1)); - lua_pop(L, 1); +static void CheckOptvalsize(lua_State *L, uint32_t want, uint32_t got) { + if (!IsTiny()) { + if (want == got) return; + WARNF("getsockopt optvalsize should be %d but was %d", want, got); } - return p; -} - -static char **ConvertLuaTableToEnvList(lua_State *L, int i) { - int j, n; - char **p, *s; - luaL_checktype(L, i, LUA_TTABLE); - p = xcalloc((n = 0) + 1, sizeof(char *)); - lua_pushnil(L); - for (n = 0; lua_next(L, i);) { - if (lua_type(L, -2) == LUA_TSTRING) { - p = xrealloc(p, (++n + 1) * sizeof(*p)); - p[n - 1] = xasprintf("%s=%s", lua_tostring(L, -2), lua_tostring(L, -1)); - } - lua_pop(L, 1); - } - p[n] = 0; - return p; } static void FreeStringList(char **p) { @@ -171,162 +233,231 @@ static void FreeStringList(char **p) { } } +static char **ConvertLuaArrayToStringList(lua_State *L, int i) { + int j, n; + char **p, *s; + luaL_checktype(L, i, LUA_TTABLE); + lua_len(L, i); + n = lua_tointeger(L, -1); + lua_pop(L, 1); + if ((p = LuaUnixAllocRaw(L, (n + 1) * sizeof(*p)))) { + for (j = 1; j <= n; ++j) { + lua_geti(L, i, j); + s = strdup(lua_tostring(L, -1)); + lua_pop(L, 1); + if (s) { + p[j - 1] = s; + } else { + FreeStringList(p); + p = 0; + break; + } + } + p[j - 1] = 0; + } + return p; +} + //////////////////////////////////////////////////////////////////////////////// // System Calls -// unix.getpid() → pid:int -static int LuaUnixGetpid(lua_State *L) { - return ReturnInteger(L, getpid()); -} - -// unix.getppid() → pid:int -static int LuaUnixGetppid(lua_State *L) { - return ReturnInteger(L, getppid()); -} - -// unix.getuid() → uid:int -static int LuaUnixGetuid(lua_State *L) { - return ReturnInteger(L, getuid()); -} - -// unix.getgid() → gid:int -static int LuaUnixGetgid(lua_State *L) { - return ReturnInteger(L, getgid()); -} - -// unix.umask(mask:int) → oldmask:int -static int LuaUnixUmask(lua_State *L) { - return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); -} - -// unix.exit([exitcode:int]) → ⊥ +// unix.exit([exitcode:int]) +// └─→ ⊥ static wontreturn int LuaUnixExit(lua_State *L) { _Exit(luaL_optinteger(L, 1, 0)); } -// unix.access(path:str, how:int) → rc:int[, errno:int] -// how can be: R_OK, W_OK, X_OK, F_OK +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 LuaUnixGetid(L, getpid); +} + +// unix.getppid() +// └─→ pid:int +static int LuaUnixGetppid(lua_State *L) { + return LuaUnixGetid(L, getppid); +} + +// unix.getuid() +// └─→ uid:int +static int LuaUnixGetuid(lua_State *L) { + return LuaUnixGetid(L, getuid); +} + +// unix.getgid() +// └─→ gid:int +static int LuaUnixGetgid(lua_State *L) { + return LuaUnixGetid(L, getgid); +} + +// unix.geteuid() +// └─→ uid:int +static int LuaUnixGeteuid(lua_State *L) { + return LuaUnixGetid(L, geteuid); +} + +// unix.getegid() +// └─→ gid:int +static int LuaUnixGetegid(lua_State *L) { + return LuaUnixGetid(L, getegid); +} + +// unix.umask(newmask:int) +// └─→ oldmask:int +static int LuaUnixUmask(lua_State *L) { + return ReturnInteger(L, umask(luaL_checkinteger(L, 1))); +} + +// unix.access(path:str, how:int[, flags:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixAccess(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_checkinteger(L, 2); - rc = access(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "access", olderr, + faccessat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 4, 0))); } -// unix.mkdir(path:str[, mode:int]) → rc:int[, errno:int] -// mode should be octal +// unix.mkdir(path:str[, mode:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixMkdir(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_optinteger(L, 2, 0755); - rc = mkdir(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "mkdir", olderr, + mkdirat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), + luaL_optinteger(L, 2, 0755))); } -// unix.makedirs(path:str[, mode:int]) → rc:int[, errno:int] -// mode should be octal +// unix.makedirs(path:str[, mode:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixMakedirs(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_optinteger(L, 2, 0755); - rc = makedirs(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "makedirs", olderr, + makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755))); } -// unix.chdir(path:str) → rc:int[, errno:int] +// unix.chdir(path:str) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChdir(lua_State *L) { - int rc, olderr; - const char *file; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - rc = chdir(file); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "chdir", olderr, chdir(luaL_checkstring(L, 1))); } -// unix.unlink(path:str) → rc:int[, errno:int] +// unix.unlink(path:str[, dirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixUnlink(lua_State *L) { - int rc, olderr; - const char *file; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - rc = unlink(file); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "unlink", olderr, + unlinkat(luaL_optinteger(L, 2, AT_FDCWD), luaL_checkstring(L, 1), 0)); } -// unix.rmdir(path:str) → rc:int[, errno:int] +// unix.rmdir(path:str[, dirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixRmdir(lua_State *L) { - const char *file; - int rc, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - rc = rmdir(file); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "rmdir", olderr, + unlinkat(luaL_optinteger(L, 2, AT_FDCWD), + luaL_checkstring(L, 1), AT_REMOVEDIR)); } -// unix.rename(oldpath:str, newpath:str) → rc:int[, errno:int] +// unix.rename(oldpath:str, newpath:str[, olddirfd:int, newdirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixRename(lua_State *L) { - const char *oldpath, *newpath; - int rc, olderr; - olderr = errno; - oldpath = luaL_checklstring(L, 1, 0); - newpath = luaL_checklstring(L, 2, 0); - rc = rename(oldpath, newpath); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "rename", olderr, + renameat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), + luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 2))); } -// unix.link(existingpath:str, newpath:str) → rc:int[, errno:int] +// unix.link(existingpath:str, newpath:str[, flags:int[, olddirfd, newdirfd]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixLink(lua_State *L) { - const char *existingpath, *newpath; - int rc, olderr; - olderr = errno; - existingpath = luaL_checklstring(L, 1, 0); - newpath = luaL_checklstring(L, 2, 0); - rc = link(existingpath, newpath); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "link", olderr, + linkat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 2), + luaL_optinteger(L, 3, 0))); } -// unix.symlink(target:str, linkpath:str) → rc:int[, errno:int] +// unix.symlink(target:str, linkpath:str[, newdirfd:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSymlink(lua_State *L) { - const char *target, *linkpath; - int rc, olderr; - olderr = errno; - target = luaL_checklstring(L, 1, 0); - linkpath = luaL_checklstring(L, 2, 0); - rc = symlink(target, linkpath); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "symlink", olderr, + symlinkat(luaL_checkstring(L, 1), luaL_optinteger(L, 3, AT_FDCWD), + luaL_checkstring(L, 2))); } -// unix.chown(path:str, uid:int, gid:int) → rc:int[, errno:int] +// unix.chown(path:str, uid:int, gid:int[, flags:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChown(lua_State *L) { - const char *file; - int rc, uid, gid, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - uid = luaL_checkinteger(L, 2); - gid = luaL_checkinteger(L, 3); - rc = chown(file, uid, gid); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "chown", olderr, + fchownat(luaL_optinteger(L, 5, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_checkinteger(L, 3), + luaL_optinteger(L, 4, 0))); } -// unix.chmod(path:str, mode:int) → rc:int[, errno:int] +// unix.chmod(path:str, mode:int[, flags:int[, dirfd:int]]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChmod(lua_State *L) { - const char *file; - int rc, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - mode = luaL_checkinteger(L, 2); - rc = chmod(file, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "chmod", olderr, + fchmodat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0))); } -// unix.getcwd(path:str, mode:int) → rc:int[, errno:int] +// unix.readlink(path:str[, dirfd:int]) +// ├─→ content:str +// └─→ nil, unix.Errno +static int LuaUnixReadlink(lua_State *L) { + char *buf; + ssize_t rc; + int olderr = errno; + size_t got, bufsiz = 8192; + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { + if ((rc = readlinkat(luaL_optinteger(L, 2, AT_FDCWD), + luaL_checkstring(L, 1), buf, bufsiz)) != -1) { + got = rc; + if (got < bufsiz) { + lua_pushlstring(L, buf, got); + free(buf); + return 1; + } else { + enametoolong(); + } + } + free(buf); + } + return SysretErrno(L, "readlink", olderr); +} + +// unix.getcwd() +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixGetcwd(lua_State *L) { int olderr; char *path; @@ -336,27 +467,34 @@ static int LuaUnixGetcwd(lua_State *L) { free(path); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "getcwd", olderr); } } -// unix.fork() → childpid|0:int[, errno:int] +// unix.fork() +// ├─┬─→ 0 +// │ └─→ childpid:int +// └─→ nil, unix.Errno static int LuaUnixFork(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = fork(); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretInteger(L, "fork", olderr, fork()); } -// 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.environ() +// └─→ {str,...} +static int LuaUnixEnviron(lua_State *L) { + int i; + char **e; + lua_newtable(L); + for (i = 0, e = environ; *e; ++e) { + lua_pushstring(L, *e); + lua_rawseti(L, -2, ++i); + } + return 1; +} + +// unix.execve(prog:str[, args:List<*>, env:List<*>]) +// └─→ nil, unix.Errno static int LuaUnixExecve(lua_State *L) { int olderr; const char *prog; @@ -364,14 +502,21 @@ static int LuaUnixExecve(lua_State *L) { olderr = errno; prog = luaL_checkstring(L, 1); if (!lua_isnoneornil(L, 2)) { - argv = ConvertLuaArrayToStringList(L, 2); - freeme1 = argv; - if (!lua_isnoneornil(L, 3)) { - envp = ConvertLuaTableToEnvList(L, 3); - freeme2 = envp; + if ((argv = ConvertLuaArrayToStringList(L, 2))) { + freeme1 = argv; + if (!lua_isnoneornil(L, 3)) { + if ((envp = ConvertLuaArrayToStringList(L, 3))) { + freeme2 = envp; + } else { + FreeStringList(argv); + return SysretErrno(L, "execve", olderr); + } + } else { + envp = environ; + freeme2 = 0; + } } else { - envp = environ; - freeme2 = 0; + return SysretErrno(L, "execve", olderr); } } else { ezargs[0] = prog; @@ -384,34 +529,38 @@ static int LuaUnixExecve(lua_State *L) { execve(prog, argv, envp); FreeStringList(freeme1); FreeStringList(freeme2); - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "execve", olderr); } -// unix.commandv(prog:str) → path:str[, errno:int] +// unix.commandv(prog:str) +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixCommandv(lua_State *L) { - int rc, olderr; + int olderr; const char *prog; char *pathbuf, *resolved; olderr = errno; - if ((pathbuf = malloc(PATH_MAX))) { - prog = luaL_checkstring(L, 1); - if ((resolved = commandv(prog, pathbuf))) { + prog = luaL_checkstring(L, 1); + if ((pathbuf = LuaUnixAllocRaw(L, PATH_MAX))) { + if ((resolved = commandv(prog, pathbuf, PATH_MAX))) { lua_pushstring(L, resolved); free(pathbuf); return 1; } else { free(pathbuf); - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "commandv", olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "commandv", olderr); } } -// unix.realpath(path:str) → path:str[, errno:int] +// unix.realpath(path:str) +// ├─→ path:str +// └─→ nil, unix.Errno static int LuaUnixRealpath(lua_State *L) { char *resolved; - int rc, olderr; + int olderr; const char *path; olderr = errno; path = luaL_checkstring(L, 1); @@ -420,7 +569,7 @@ static int LuaUnixRealpath(lua_State *L) { free(resolved); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "realpath", olderr); } } @@ -430,91 +579,102 @@ static int LuaUnixSyslog(lua_State *L) { return 0; } -// unix.chroot(path:str) → rc:int[, errno:int] +// unix.chroot(path:str) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixChroot(lua_State *L) { - int rc, olderr; - const char *path; - olderr = errno; - path = luaL_checkstring(L, 1); - rc = chroot(path); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "chroot", olderr, chroot(luaL_checkstring(L, 1))); } -// unix.setrlimit(resource:int, soft:int[, hard:int]) → rc:int[, errno:int] +// unix.setrlimit(resource:int, soft:int[, hard:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetrlimit(lua_State *L) { - struct rlimit rlim; - int rc, olderr, resource; - olderr = errno; - resource = luaL_checkinteger(L, 1); - rlim.rlim_cur = luaL_checkinteger(L, 2); - rlim.rlim_max = luaL_optinteger(L, 3, rlim.rlim_cur); - return ReturnRc(L, setrlimit(resource, &rlim), olderr); + int olderr = errno; + int64_t soft = luaL_checkinteger(L, 2); + return SysretBool( + L, "setrlimit", olderr, + setrlimit(luaL_checkinteger(L, 1), + &(struct rlimit){soft, luaL_optinteger(L, 3, soft)})); } -// unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] +// unix.getrlimit(resource:int) +// ├─→ soft:int, hard:int +// └─→ nil, unix.Errno static int LuaUnixGetrlimit(lua_State *L) { + int olderr = errno; struct rlimit rlim; - int rc, olderr, resource; - olderr = errno; - resource = luaL_checkinteger(L, 1); - if (!getrlimit(resource, &rlim)) { - lua_pushinteger(L, rlim.rlim_cur < RLIM_INFINITY ? rlim.rlim_cur : -1); - lua_pushinteger(L, rlim.rlim_max < RLIM_INFINITY ? rlim.rlim_max : -1); + if (!getrlimit(luaL_checkinteger(L, 1), &rlim)) { + lua_pushinteger(L, FixLimit(rlim.rlim_cur)); + lua_pushinteger(L, FixLimit(rlim.rlim_max)); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "getrlimit", olderr); } } -// unix.kill(pid, sig) → rc:int[, errno:int] +// unix.getrusage([who:int]) +// ├─→ unix.Rusage +// └─→ nil, unix.Errno +static int LuaUnixGetrusage(lua_State *L) { + struct rusage ru; + int olderr = errno; + if (!getrusage(luaL_optinteger(L, 1, RUSAGE_SELF), &ru)) { + LuaPushRusage(L, &ru); + return 1; + } else { + return SysretErrno(L, "getrusage", olderr); + } +} + +// unix.kill(pid:int, sig:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixKill(lua_State *L) { - int rc, pid, sig, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - sig = luaL_checkinteger(L, 2); - rc = kill(pid, sig); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "kill", olderr, + kill(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -// unix.raise(sig) → rc:int[, errno:int] +// unix.raise(sig:int) +// ├─→ rc:int +// └─→ nil, unix.Errno static int LuaUnixRaise(lua_State *L) { - int rc, sig, olderr; - olderr = errno; - sig = luaL_checkinteger(L, 1); - rc = raise(sig); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretInteger(L, "raise", olderr, raise(luaL_checkinteger(L, 1))); } -// unix.wait([pid, options]) → pid, wstatus, nil, errno +// unix.wait([pid:int, options:int]) +// ├─→ pid:int, wstatus:int, unix.Rusage +// └─→ nil, unix.Errno static int LuaUnixWait(lua_State *L) { - int rc, pid, olderr, options, wstatus; - olderr = errno; - pid = luaL_optinteger(L, 1, -1); - options = luaL_optinteger(L, 2, 0); - rc = wait4(pid, &wstatus, options, 0); - if (rc != -1) { - lua_pushinteger(L, rc); + struct rusage ru; + int pid, wstatus, olderr = errno; + if ((pid = wait4(luaL_optinteger(L, 1, -1), &wstatus, + luaL_optinteger(L, 2, 0), &ru)) != -1) { + lua_pushinteger(L, pid); lua_pushinteger(L, wstatus); - return 2; + LuaPushRusage(L, &ru); + return 3; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrno(L, "wait", olderr); } } -// unix.fcntl(fd, cmd[, arg]) → rc:int[, errno:int] +// unix.fcntl(fd:int, cmd:int[, arg:int]) +// ├─→ true, ... +// └─→ nil, unix.Errno static int LuaUnixFcntl(lua_State *L) { - intptr_t arg; - int rc, fd, cmd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - cmd = luaL_checkinteger(L, 2); - arg = luaL_optinteger(L, 3, 0); - rc = fcntl(fd, cmd, arg); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "fcntl", olderr, + fcntl(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, 0))); } -// unix.dup(oldfd[, newfd[, flags]]) → newfd, errno -// flags can have O_CLOEXEC +// unix.dup(oldfd:int[, newfd:int[, flags:int]]) +// ├─→ newfd:int +// └─→ nil, unix.Errno static int LuaUnixDup(lua_State *L) { int rc, oldfd, newfd, flags, olderr; olderr = errno; @@ -526,120 +686,144 @@ static int LuaUnixDup(lua_State *L) { } else { rc = dup3(oldfd, newfd, flags); } - return ReturnRc(L, rc, olderr); + return SysretInteger(L, "dup", olderr, rc); } -// unix.pipe([flags]) → reader, writer, errno -// flags can have O_CLOEXEC +// unix.pipe([flags:int]) +// ├─→ reader:int, writer:int +// └─→ nil, unix.Errno static int LuaUnixPipe(lua_State *L) { - int flags, olderr, pipefd[2]; - olderr = errno; - flags = luaL_optinteger(L, 1, 0); - if (!pipe2(pipefd, flags)) { + int pipefd[2], olderr = errno; + if (!pipe2(pipefd, luaL_optinteger(L, 1, 0))) { lua_pushinteger(L, pipefd[0]); lua_pushinteger(L, pipefd[1]); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "pipe", olderr); } } -// unix.getsid(pid) → sid:int[, errno:int] +// unix.getsid(pid:int) +// ├─→ sid:int +// └─→ nil, unix.Errno static int LuaUnixGetsid(lua_State *L) { - int rc, pid, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - rc = getsid(pid); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretInteger(L, "getsid", olderr, getsid(luaL_checkinteger(L, 1))); } -// unix.getpgrp() → pgid:int[, errno:int] +static dontinline int LuaUnixRc0(lua_State *L, const char *call, int f(void)) { + int olderr = errno; + return SysretInteger(L, call, olderr, f()); +} + +// unix.getpgrp() +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixGetpgrp(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = getpgrp(); - return ReturnRc(L, rc, olderr); + return LuaUnixRc0(L, "getpgrp", getpgrp); } -// unix.getpgid(pid:int) → pgid:int[, errno:int] -static int LuaUnixGetpgid(lua_State *L) { - int rc, pid, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - rc = getpgid(pid); - return ReturnRc(L, rc, olderr); -} - -// unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] -static int LuaUnixSetpgid(lua_State *L) { - int rc, pid, pgid, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - pgid = luaL_checkinteger(L, 2); - rc = setpgid(pid, pgid); - return ReturnRc(L, rc, olderr); -} - -// unix.setpgrp() → pgid:int[, errno:int] +// unix.setpgrp() +// ├─→ pgid:int +// └─→ nil, unix.Errno static int LuaUnixSetpgrp(lua_State *L) { - int rc, pid, pgrp, olderr; - olderr = errno; - pid = luaL_checkinteger(L, 1); - pgrp = luaL_checkinteger(L, 2); - rc = setpgrp(); - return ReturnRc(L, rc, olderr); + return LuaUnixRc0(L, "setpgrp", setpgrp); } -// unix.setsid() → sid:int[, errno:int] +// unix.setsid() +// ├─→ sid:int +// └─→ nil, unix.Errno static int LuaUnixSetsid(lua_State *L) { - int rc, olderr; - olderr = errno; - rc = setsid(); - return ReturnRc(L, rc, olderr); + return LuaUnixRc0(L, "setsid", setsid); } -// unix.setuid(uid:int) → rc:int[, errno:int] +// unix.getpgid(pid:int) +// ├─→ pgid:int +// └─→ nil, unix.Errno +static int LuaUnixGetpgid(lua_State *L) { + int olderr = errno; + return SysretInteger(L, "getpgid", olderr, getpgid(luaL_checkinteger(L, 1))); +} + +// unix.setpgid(pid:int, pgid:int) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixSetpgid(lua_State *L) { + int olderr = errno; + return SysretBool(L, "setpgid", olderr, + setpgid(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); +} + +static dontinline int LuaUnixSetid(lua_State *L, const char *call, int f(int)) { + int olderr = errno; + return SysretBool(L, call, olderr, f(luaL_checkinteger(L, 1))); +} + +// unix.setuid(uid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetuid(lua_State *L) { - int olderr = errno; - return ReturnRc(L, setuid(luaL_checkinteger(L, 1)), olderr); + return LuaUnixSetid(L, "setuid", setuid); } -// unix.setgid(gid:int) → rc:int[, errno:int] +// unix.setgid(gid:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixSetgid(lua_State *L) { - int olderr = errno; - return ReturnRc(L, setgid(luaL_checkinteger(L, 1)), olderr); + return LuaUnixSetid(L, "setgid", setgid); } -// unix.clock_gettime([clock]) → seconds, nanos, errno +static dontinline int LuaUnixSetresid(lua_State *L, const char *call, + int f(uint32_t, uint32_t, uint32_t)) { + int olderr = errno; + return SysretBool(L, call, olderr, + f(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_checkinteger(L, 3))); +} + +// unix.setresuid(real:int, effective:int, saved:int) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixSetresuid(lua_State *L) { + return LuaUnixSetresid(L, "setresuid", setresuid); +} + +// unix.setresgid(real:int, effective:int, saved:int) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixSetresgid(lua_State *L) { + return LuaUnixSetresid(L, "setresgid", setresgid); +} + +// unix.clock_gettime([clock:int]) +// ├─→ seconds:int, nanos:int +// └─→ nil, unix.Errno static int LuaUnixGettime(lua_State *L) { struct timespec ts; - int rc, clock, olderr; - olderr = errno; - clock = luaL_optinteger(L, 1, CLOCK_REALTIME); - rc = clock_gettime(clock, &ts); - if (rc != -1) { + int rc, olderr = errno; + if (!clock_gettime(luaL_optinteger(L, 1, CLOCK_REALTIME), &ts)) { lua_pushinteger(L, ts.tv_sec); lua_pushinteger(L, ts.tv_nsec); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "clock_gettime", olderr); } } -// unix.nanosleep(seconds, nanos) → remseconds, remnanos, errno +// unix.nanosleep(seconds:int, nanos:int) +// ├─→ remseconds:int, remnanos:int +// └─→ nil, unix.Errno static int LuaUnixNanosleep(lua_State *L) { - int rc, olderr; + int olderr = errno; struct timespec req, rem; - olderr = errno; req.tv_sec = luaL_checkinteger(L, 1); req.tv_nsec = luaL_optinteger(L, 2, 0); - rc = nanosleep(&req, &rem); - if (rc != -1) { + if (!nanosleep(&req, &rem)) { lua_pushinteger(L, rem.tv_sec); lua_pushinteger(L, rem.tv_nsec); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "nanosleep", olderr); } } @@ -649,93 +833,85 @@ static int LuaUnixSync(lua_State *L) { return 0; } -// unix.fsync(fd:int) → rc:int[, errno:int] +// unix.fsync(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFsync(lua_State *L) { - int rc, fd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - rc = fsync(fd); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "fsync", olderr, fsync(luaL_checkinteger(L, 1))); } -// unix.fdatasync(fd:int) → rc:int[, errno:int] +// unix.fdatasync(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixFdatasync(lua_State *L) { - int rc, fd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - rc = fdatasync(fd); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "fdatasync", olderr, fdatasync(luaL_checkinteger(L, 1))); } -// unix.open(path, flags[, mode]) → fd, errno +// unix.open(path:str, flags:int[, mode:int[, dirfd:int]]) +// ├─→ fd:int +// └─→ nil, unix.Errno static int LuaUnixOpen(lua_State *L) { - const char *file; - int rc, flags, mode, olderr; - olderr = errno; - file = luaL_checklstring(L, 1, 0); - flags = luaL_checkinteger(L, 2); - mode = luaL_optinteger(L, 3, 0); - rc = open(file, flags, mode); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretInteger( + L, "open", olderr, + openat(luaL_optinteger(L, 4, AT_FDCWD), luaL_checkstring(L, 1), + luaL_checkinteger(L, 2), luaL_optinteger(L, 3, 0))); } -// unix.close(fd:int) → rc:int[, errno:int] +// unix.close(fd:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixClose(lua_State *L) { - int rc, fd, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - rc = close(fd); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "close", olderr, close(luaL_checkinteger(L, 1))); } -// unix.seek(fd, offset, whence) → newpos, errno -// where whence ∈ {SEEK_SET, SEEK_CUR, SEEK_END} -// whence defaults to SEEK_SET -static int LuaUnixSeek(lua_State *L) { - int64_t newpos, offset; - int fd, olderr, whence; - olderr = errno; - fd = luaL_checkinteger(L, 1); - offset = luaL_checkinteger(L, 2); - whence = luaL_optinteger(L, 3, SEEK_SET); - newpos = lseek(fd, offset, whence); - return ReturnRc(L, newpos, olderr); +// unix.lseek(fd:int, offset:int[, whence:int]) +// ├─→ newposbytes:int +// └─→ nil, unix.Errno +static int LuaUnixLseek(lua_State *L) { + int olderr = errno; + return SysretInteger(L, "lseek", olderr, + lseek(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2), + luaL_optinteger(L, 3, SEEK_SET))); } -// unix.truncate(path, length) → rc:int[, errno:int] -// unix.truncate(fd, length) → rc:int[, errno:int] +// unix.truncate(path:str[, length:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixTruncate(lua_State *L) { - int64_t length; - const char *path; - int rc, fd, olderr, whence; - olderr = errno; - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - length = luaL_checkinteger(L, 2); - rc = ftruncate(fd, length); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - length = luaL_checkinteger(L, 2); - rc = truncate(path, length); - } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "truncate", olderr, + truncate(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0))); } -// unix.read(fd:int[, bufsiz:str, offset:int]) → data:str, errno:int +// unix.ftruncate(fd:int[, length:int]) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixFtruncate(lua_State *L) { + int olderr = errno; + return SysretBool( + L, "ftruncate", olderr, + ftruncate(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 0))); +} + +// unix.read(fd:int[, bufsiz:str[, offset:int]]) +// ├─→ data:str +// └─→ nil, unix.Errno static int LuaUnixRead(lua_State *L) { char *buf; size_t got; + ssize_t rc; int fd, olderr; - int64_t rc, bufsiz, offset; + lua_Integer bufsiz, offset; olderr = errno; fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, BUFSIZ); offset = luaL_optinteger(L, 3, -1); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { if (offset == -1) { rc = read(fd, buf, bufsiz); } else { @@ -748,19 +924,22 @@ static int LuaUnixRead(lua_State *L) { return 1; } else { free(buf); - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "read", olderr); } } else { - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "read", olderr); } } -// unix.write(fd:int, data:str[, offset:int]) → rc:int[, errno:int] +// unix.write(fd:int, data:str[, offset:int]) +// ├─→ wrotebytes:int +// └─→ nil, unix.Errno static int LuaUnixWrite(lua_State *L) { + ssize_t rc; size_t size; int fd, olderr; const char *data; - int64_t rc, offset; + lua_Integer offset; olderr = errno; fd = luaL_checkinteger(L, 1); data = luaL_checklstring(L, 2, &size); @@ -771,161 +950,256 @@ static int LuaUnixWrite(lua_State *L) { } else { rc = pwrite(fd, data, size, offset); } - return ReturnRc(L, rc, olderr); + return SysretInteger(L, "write", olderr, rc); } -// unix.stat(path:str) → UnixStat*[, errno] -// unix.stat(fd:int) → UnixStat*[, errno] +// unix.stat(path:str[, flags:int[, dirfd:int]]) +// ├─→ unix.Stat +// └─→ nil, unix.Errno static int LuaUnixStat(lua_State *L) { - const char *path; - int rc, fd, olderr; - struct UnixStat **ust, *st; - olderr = errno; - if ((st = malloc(sizeof(struct UnixStat)))) { - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - rc = fstat(fd, &st->st); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - rc = stat(path, &st->st); - } else { - free(st); - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - if (rc != -1) { - st->refs = 1; - ust = lua_newuserdatauv(L, sizeof(st), 1); - luaL_setmetatable(L, "UnixStat*"); - *ust = st; + struct stat st; + int olderr = errno; + if (!fstatat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), &st, + luaL_optinteger(L, 2, 0))) { + LuaPushStat(L, &st); + return 1; + } else { + return SysretErrno(L, "stat", olderr); + } +} + +// unix.fstat(fd:int) +// ├─→ unix.Stat +// └─→ nil, unix.Errno +static int LuaUnixFstat(lua_State *L) { + struct stat st; + int olderr = errno; + if (!fstat(luaL_checkinteger(L, 1), &st)) { + LuaPushStat(L, &st); + return 1; + } else { + return SysretErrno(L, "fstat", olderr); + } +} + +static bool IsSockoptBool(int l, int x) { + if (l == SOL_SOCKET) { + 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 || // + x == TCP_QUICKACK || // + x == TCP_FASTOPEN_CONNECT || // + x == TCP_DEFER_ACCEPT; // + } else if (l = SOL_IP) { + return x == IP_HDRINCL; // + } else { + return false; + } +} + +static bool IsSockoptInt(int l, int x) { + if (l == SOL_SOCKET) { + return x == SO_SNDBUF || // + x == SO_RCVBUF || // + x == SO_RCVLOWAT || // + x == SO_SNDLOWAT; // + } else if (l = SOL_TCP) { + return x == TCP_FASTOPEN || // + x == TCP_KEEPCNT || // + x == TCP_MAXSEG || // + x == TCP_SYNCNT || // + x == TCP_NOTSENT_LOWAT || // + x == TCP_WINDOW_CLAMP || // + x == TCP_KEEPIDLE || // + x == TCP_KEEPINTVL; // + } else if (l = SOL_IP) { + return x == IP_TOS || // + x == IP_MTU || // + x == IP_TTL; // + } else { + return false; + } +} + +static bool IsSockoptTimeval(int l, int x) { + if (l == SOL_SOCKET) { + return x == SO_RCVTIMEO || // + x == SO_SNDTIMEO; // + } else { + return false; + } +} + +static int LuaUnixSetsockopt(lua_State *L) { + void *optval; + struct linger l; + uint32_t optsize; + struct timeval tv; + int rc, fd, level, optname, optint, olderr = errno; + fd = luaL_checkinteger(L, 1); + level = luaL_checkinteger(L, 2); + optname = luaL_checkinteger(L, 3); + if (IsSockoptBool(level, optname)) { + // unix.setsockopt(fd:int, level:int, optname:int, value:bool) + // ├─→ true + // └─→ nil, unix.Errno + optint = lua_toboolean(L, 4); + optval = &optint; + optsize = sizeof(optint); + } else if (IsSockoptInt(level, optname)) { + // unix.setsockopt(fd:int, level:int, optname:int, value:int) + // ├─→ true + // └─→ nil, unix.Errno + optint = luaL_checkinteger(L, 4); + optval = &optint; + optsize = sizeof(optint); + } else if (IsSockoptTimeval(level, optname)) { + // unix.setsockopt(fd:int, level:int, optname:int, secs:int[, nanos:int]) + // ├─→ true + // └─→ nil, unix.Errno + tv.tv_sec = luaL_checkinteger(L, 4); + optval = &tv; + optsize = sizeof(tv); + } else if (level == SOL_SOCKET && optname == SO_LINGER) { + // unix.setsockopt(fd:int, level:int, optname:int, secs:int, enabled:bool) + // ├─→ true + // └─→ nil, unix.Errno + l.l_linger = luaL_checkinteger(L, 4); + l.l_onoff = lua_toboolean(L, 5); + optval = &l; + optsize = sizeof(l); + } else { + einval(); + return SysretErrno(L, "setsockopt", olderr); + } + return SysretBool(L, "setsockopt", olderr, + setsockopt(fd, level, optname, optval, optsize)); +} + +static int LuaUnixGetsockopt(lua_State *L) { + uint32_t size; + struct linger l; + struct timeval tv; + 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) || IsSockoptInt(level, optname)) { + // unix.getsockopt(fd:int, level:int, optname:int) + // ├─→ value:int + // └─→ nil, unix.Errno + size = sizeof(optval); + if (getsockopt(fd, level, optname, &optval, &size) != -1) { + CheckOptvalsize(L, sizeof(optval), size); + lua_pushinteger(L, optval); + return 1; + } + } else if (IsSockoptTimeval(level, optname)) { + // unix.getsockopt(fd:int, level:int, optname:int) + // ├─→ secs:int, nsecs:int + // └─→ nil, unix.Errno + size = sizeof(tv); + if (getsockopt(fd, level, optname, &tv, &size) != -1) { + CheckOptvalsize(L, sizeof(tv), size); + lua_pushinteger(L, tv.tv_sec); + lua_pushinteger(L, tv.tv_usec * 1000); + return 2; + } + } else if (level == SOL_SOCKET && optname == SO_LINGER) { + // unix.getsockopt(fd:int, unix.SOL_SOCKET, unix.SO_LINGER) + // ├─→ seconds:int, enabled:bool + // └─→ nil, unix.Errno + size = sizeof(l); + if (getsockopt(fd, level, optname, &l, &size) != -1) { + CheckOptvalsize(L, sizeof(l), size); + lua_pushinteger(L, l.l_linger); + lua_pushboolean(L, !!l.l_onoff); return 1; - } else { - free(st); - return ReturnErrno(L, 1, olderr); } } else { - return ReturnErrno(L, 1, olderr); + einval(); } + return SysretErrno(L, "getsockopt", olderr); } -// unix.opendir(path:str) → UnixDir*[, errno] -// unix.opendir(fd:int) → UnixDir*[, errno] -static int LuaUnixOpendir(lua_State *L) { - DIR *rc; - int fd, olderr; - const char *path; - struct UnixDir **udir, *dir; - olderr = errno; - dir = xcalloc(1, sizeof(struct UnixDir)); - if (lua_isinteger(L, 1)) { - fd = luaL_checkinteger(L, 1); - rc = fdopendir(fd); - } else if (lua_isstring(L, 1)) { - path = luaL_checkstring(L, 1); - rc = opendir(path); - } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - if (!rc) { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - free(dir); - return 2; - } - dir->refs = 1; - dir->dir = rc; - udir = lua_newuserdatauv(L, sizeof(dir), 1); - luaL_setmetatable(L, "UnixDir*"); - *udir = dir; - return 1; -} - -// unix.socket([family[, type[, protocol]]]) → fd, errno -// SOCK_CLOEXEC may be or'd into type -// family defaults to AF_INET -// type defaults to SOCK_STREAM -// protocol defaults to IPPROTO_TCP +// unix.socket([family:int[, type:int[, protocol:int]]]) +// ├─→ fd:int +// └─→ nil, unix.Errno static int LuaUnixSocket(lua_State *L) { - const char *file; - int rc, olderr, family, type, protocol; - olderr = errno; - family = luaL_optinteger(L, 1, AF_INET); - type = luaL_optinteger(L, 2, SOCK_STREAM); - protocol = luaL_optinteger(L, 3, IPPROTO_TCP); - rc = socket(family, type, protocol); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretInteger( + L, "socket", olderr, + socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM), + luaL_optinteger(L, 3, IPPROTO_TCP))); } -// unix.socketpair([family[, type[, protocol]]]) → fd1, fd2[, errno] -// SOCK_CLOEXEC may be or'd into type -// family defaults to AF_INET -// type defaults to SOCK_STREAM -// protocol defaults to IPPROTO_TCP +// unix.socketpair([family:int[, type:int[, protocol:int]]]) +// ├─→ fd1:int, fd2:int +// └─→ nil, unix.Errno static int LuaUnixSocketpair(lua_State *L) { - int olderr, family, type, protocol, sv[2]; - olderr = errno; - family = luaL_optinteger(L, 1, AF_INET); - type = luaL_optinteger(L, 2, SOCK_STREAM); - protocol = luaL_optinteger(L, 3, IPPROTO_TCP); - if (!socketpair(family, type, protocol, sv)) { + 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_pushinteger(L, sv[1]); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "socketpair", olderr); } } -// unix.bind(fd[, ip, port]) → rc:int[, errno: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]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixBind(lua_State *L) { - uint32_t x; - int rc, olderr, fd; - struct sockaddr_in sa; - bzero(&sa, sizeof(sa)); - olderr = errno; - fd = luaL_checkinteger(L, 1); - x = luaL_optinteger(L, 2, 0); - sa.sin_family = AF_INET; - sa.sin_addr.s_addr = htonl(x); - sa.sin_port = htons(luaL_optinteger(L, 3, 0)); - rc = bind(fd, &sa, sizeof(sa)); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "bind", olderr, + 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))); } -// unix.connect(fd, ip, port) → rc:int[, errno: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.connect(fd:int, ip:uint32, port:uint16) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixConnect(lua_State *L) { - int rc, olderr, fd; - struct sockaddr_in sa; - bzero(&sa, sizeof(sa)); - olderr = errno; - fd = luaL_checkinteger(L, 1); - sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)); - sa.sin_port = htons(luaL_checkinteger(L, 3)); - rc = connect(fd, &sa, sizeof(sa)); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool( + L, "connect", olderr, + 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))); } -// unix.listen(fd[, backlog]) → rc:int[, errno:int] +// unix.listen(fd:int[, backlog:int]) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixListen(lua_State *L) { - int rc, fd, olderr, backlog; - olderr = errno; - fd = luaL_checkinteger(L, 1); - backlog = luaL_optinteger(L, 2, 10); - rc = listen(fd, backlog); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "listen", olderr, + listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10))); } -// unix.getsockname(fd) → ip, port, errno +// unix.getsockname(fd:int) +// ├─→ ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixGetsockname(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -938,11 +1212,13 @@ static int LuaUnixGetsockname(lua_State *L) { lua_pushinteger(L, ntohs(sa.sin_port)); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "getsockname", olderr); } } -// unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] +// unix.getpeername(fd:int) +// ├─→ ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixGetpeername(lua_State *L) { int fd, olderr; uint32_t addrsize; @@ -955,11 +1231,13 @@ static int LuaUnixGetpeername(lua_State *L) { lua_pushinteger(L, ntohs(sa.sin_port)); return 2; } else { - return ReturnErrno(L, 2, olderr); + return SysretErrno(L, "getpeername", olderr); } } -// unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, errno:int] +// unix.siocgifconf() +// ├─→ {{name:str,ip:uint32,netmask:uint32}, ...} +// └─→ nil, unix.Errno static int LuaUnixSiocgifconf(lua_State *L) { size_t n; char *data; @@ -967,34 +1245,34 @@ static int LuaUnixSiocgifconf(lua_State *L) { struct ifreq *ifr; struct ifconf conf; olderr = errno; - if (!(data = malloc((n = 4096)))) { - return ReturnErrno(L, 1, olderr); + if (!(data = LuaUnixAllocRaw(L, (n = 4096)))) { + return SysretErrno(L, "siocgifconf", olderr); } if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) { free(data); - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "siocgifconf", 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 SysretErrno(L, "siocgifconf", 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); @@ -1006,7 +1284,17 @@ static int LuaUnixSiocgifconf(lua_State *L) { return 1; } -// unix.gethostname() → host:str[, errno:int] +// sandbox.pledge([promises:str]) +// ├─→ true +// └─→ nil, unix.Errno +static int LuaUnixPledge(lua_State *L) { + int olderr = errno; + return SysretBool(L, "pledge", olderr, pledge(luaL_checkstring(L, 1), 0)); +} + +// unix.gethostname() +// ├─→ host:str +// └─→ nil, unix.Errno static int LuaUnixGethostname(lua_State *L) { int rc, olderr; char buf[DNS_NAME_MAX + 1]; @@ -1017,45 +1305,56 @@ static int LuaUnixGethostname(lua_State *L) { return 1; } else { enomem(); - return ReturnErrno(L, 1, olderr); } - } else { - return ReturnErrno(L, 1, olderr); } + return SysretErrno(L, "gethostname", olderr); } -// unix.accept(serverfd:int) → clientfd:int, ip:uint32, port:uint16[, errno:int] +// unix.accept(serverfd:int[, flags:int]) +// ├─→ clientfd:int, ip:uint32, port:uint16 +// └─→ nil, unix.Errno 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_pushinteger(L, ntohl(sa.sin_addr.s_addr)); lua_pushinteger(L, ntohs(sa.sin_port)); return 3; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrno(L, "accept", olderr); } } -// unix.poll({fd:int=events:int, ...}[, timeoutms:int]) -// → {fd:int=revents:int, ...}[, errno:int] +// unix.poll({[fd:int]=events:int, ...}[, timeoutms:int]) +// ├─→ {[fd:int]=revents:int, ...} +// └─→ nil, unix.Errno static int LuaUnixPoll(lua_State *L) { size_t nfds; - struct pollfd *fds; + struct pollfd *fds, *fds2; int i, fd, olderr, events, timeoutms; + olderr = errno; luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); for (fds = 0, nfds = 0; lua_next(L, 1);) { if (lua_isinteger(L, -2)) { - fds = xrealloc(fds, ++nfds * sizeof(*fds)); - fds[nfds - 1].fd = lua_tointeger(L, -2); - fds[nfds - 1].events = lua_tointeger(L, -1); + if ((fds2 = LuaUnixRealloc(L, fds, (nfds + 1) * sizeof(*fds)))) { + fds2[nfds].fd = lua_tointeger(L, -2); + fds2[nfds].events = lua_tointeger(L, -1); + fds = fds2; + ++nfds; + } else { + free(fds); + return SysretErrno(L, "poll", olderr); + } + } else { + // ignore non-integer key } lua_pop(L, 1); } @@ -1073,26 +1372,28 @@ static int LuaUnixPoll(lua_State *L) { return 1; } else { free(fds); - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "poll", olderr); } } -// unix.recvfrom(fd[, bufsiz[, flags]]) → data, ip, port[, errno] -// flags can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. +// unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) +// ├─→ data:str, ip:uint32, port:uint16 +// └─→ nil, unix.Errno static int LuaUnixRecvfrom(lua_State *L) { char *buf; size_t got; ssize_t rc; uint32_t addrsize; + lua_Integer bufsiz; struct sockaddr_in sa; - int fd, flags, bufsiz, olderr; + int fd, flags, olderr; olderr = errno; addrsize = sizeof(sa); fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize); if (rc != -1) { got = rc; @@ -1103,25 +1404,28 @@ static int LuaUnixRecvfrom(lua_State *L) { return 3; } else { free(buf); - return ReturnErrno(L, 3, olderr); + return SysretErrno(L, "recvfrom", olderr); } } else { - return ReturnErrno(L, 3, olderr); + return SysretErrno(L, "recvfrom", olderr); } } -// unix.recv(fd[, bufsiz[, flags]]) → data[, errno] +// unix.recv(fd:int[, bufsiz:int[, flags:int]]) +// ├─→ data:str +// └─→ nil, unix.Errno static int LuaUnixRecv(lua_State *L) { char *buf; size_t got; ssize_t rc; - int fd, flags, bufsiz, olderr, pushed; + lua_Integer bufsiz; + int fd, flags, olderr, pushed; olderr = errno; fd = luaL_checkinteger(L, 1); bufsiz = luaL_optinteger(L, 2, 1500); flags = luaL_optinteger(L, 3, 0); bufsiz = MIN(bufsiz, 0x7ffff000); - if ((buf = malloc(bufsiz))) { + if ((buf = LuaUnixAllocRaw(L, bufsiz))) { rc = recv(fd, buf, bufsiz, flags); if (rc != -1) { got = rc; @@ -1130,36 +1434,39 @@ static int LuaUnixRecv(lua_State *L) { return 1; } else { free(buf); - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "recv", olderr); } } else { - return ReturnErrno(L, 3, olderr); + return SysretErrno(L, "recv", olderr); } } -// unix.send(fd, data[, flags]) → sent, errno +// unix.send(fd:int, data:str[, flags:int]) +// ├─→ sent:int +// └─→ nil, unix.Errno static int LuaUnixSend(lua_State *L) { char *data; ssize_t rc; size_t sent, size; - int fd, flags, bufsiz, olderr; + lua_Integer bufsiz; + int fd, flags, olderr; olderr = errno; fd = luaL_checkinteger(L, 1); 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", olderr, send(fd, data, size, flags)); } -// unix.sendto(fd, data, ip, port[, flags]) → sent, errno -// flags MSG_OOB, MSG_DONTROUTE, MSG_NOSIGNAL, etc. +// unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int]) +// ├─→ sent:int +// └─→ nil, unix.Errno static int LuaUnixSendto(lua_State *L) { char *data; - ssize_t rc; size_t sent, size; + lua_Integer bufsiz; + int fd, flags, olderr; struct sockaddr_in sa; - int fd, flags, bufsiz, olderr; olderr = errno; bzero(&sa, sizeof(sa)); fd = luaL_checkinteger(L, 1); @@ -1168,77 +1475,64 @@ 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", olderr, + sendto(fd, data, size, flags, &sa, sizeof(sa))); } -// unix.shutdown(fd, how) → rc:int[, errno:int] -// how can be SHUT_RD, SHUT_WR, or SHUT_RDWR +// unix.shutdown(fd:int, how:int) +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixShutdown(lua_State *L) { - int rc, fd, how, olderr; - olderr = errno; - fd = luaL_checkinteger(L, 1); - how = luaL_checkinteger(L, 2); - rc = shutdown(fd, how); - return ReturnRc(L, rc, olderr); + int olderr = errno; + return SysretBool(L, "shutdown", olderr, + shutdown(luaL_checkinteger(L, 1), luaL_checkinteger(L, 2))); } -// unix.sigprocmask(how[, mask]) → oldmask[, errno] -// how can be SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK +// unix.sigprocmask(how:int, newmask:unix.Sigset) +// ├─→ oldmask:unix.Sigset +// └─→ nil, unix.Errno static int LuaUnixSigprocmask(lua_State *L) { uint64_t imask; - int how, olderr; - sigset_t mask, oldmask, *maskptr; + int i, n, how, olderr; + struct sigset *newmask, oldmask; olderr = errno; how = luaL_checkinteger(L, 1); - if (lua_isnoneornil(L, 2)) { - // if mask isn't passed then we're querying - maskptr = 0; - } else { - maskptr = &mask; - imask = luaL_checkinteger(L, 2); - bzero(&mask, sizeof(mask)); - if (how == SIG_SETMASK) { - sigprocmask(how, 0, &mask); - } - mask.__bits[0] = imask; - } - if (!sigprocmask(how, maskptr, &oldmask)) { - lua_pushinteger(L, oldmask.__bits[0]); + newmask = luaL_checkudata(L, 2, "unix.Sigset"); + if (!sigprocmask(how, newmask, &oldmask)) { + LuaPushSigset(L, oldmask); return 1; } else { - lua_pushnil(L); - lua_pushinteger(L, errno); - errno = olderr; - return 2; + return SysretErrno(L, "sigprocmask", olderr); } } static void LuaUnixOnSignal(int sig, siginfo_t *si, ucontext_t *ctx) { + int type; + lua_State *L = GL; + struct sigset ss, os; STRACE("LuaUnixOnSignal(%G)", sig); - lua_getglobal(GL, "__signal_handlers"); - CHECK_EQ(LUA_TFUNCTION, lua_geti(GL, -1, sig)); - lua_remove(GL, -2); - lua_pushinteger(GL, sig); - if (lua_pcall(GL, 1, 0, 0) != LUA_OK) { - ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(GL, -1)); - lua_pop(GL, 1); // pop error + lua_getglobal(L, "__signal_handlers"); + type = lua_rawgeti(L, -1, sig); + lua_remove(L, -2); // pop __signal_handlers + if (type == LUA_TFUNCTION) { + lua_pushinteger(L, sig); + if (lua_pcall(L, 1, 0, 0) != LUA_OK) { + sigfillset(&ss); + sigprocmask(SIG_BLOCK, &ss, &os); + ERRORF("(lua) %s failed: %s", strsignal(sig), lua_tostring(L, -1)); + sigprocmask(SIG_SETMASK, &os, 0); + lua_pop(L, 1); // pop error + } + } else { + lua_pop(L, 1); // pop handler } } -// unix.sigaction(sig[,handler[,flags[,mask]]]) → handler,flags,mask[,errno] -// -// unix = require "unix" -// unix.sigaction(unix.SIGUSR1, function(sig) -// print(string.format("got %s", unix.strsignal(sig))) -// end) -// unix.sigprocmask(unix.SIG_SETMASK, -1) -// unix.raise(unix.SIGUSR1) -// unix.sigsuspend() -// -// handler can be SIG_IGN, SIG_DFL, intptr_t, or a Lua function -// sig can be SIGINT, SIGQUIT, SIGTERM, SIGUSR1, etc. +// unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:unix.Sigset]]]) +// ├─→ oldhandler:func|int, flags:int, mask:unix.Sigset +// └─→ nil, unix.Errno static int LuaUnixSigaction(lua_State *L) { + struct sigset *mask; int i, n, sig, olderr; struct sigaction sa, oldsa, *saptr; saptr = &sa; @@ -1277,15 +1571,19 @@ static int LuaUnixSigaction(lua_State *L) { luaL_argerror(L, 2, "sigaction handler not integer or function"); unreachable; } - sa.sa_flags = luaL_optinteger(L, 3, SA_RESTART); - sa.sa_mask.__bits[0] |= luaL_optinteger(L, 4, 0); + sa.sa_flags = luaL_optinteger(L, 3, 0); + if (!lua_isnoneornil(L, 4)) { + mask = luaL_checkudata(L, 4, "unix.Sigset"); + sa.sa_mask.__bits[0] |= mask->__bits[0]; + sa.sa_mask.__bits[1] |= mask->__bits[1]; + } if (!sigaction(sig, saptr, &oldsa)) { lua_getglobal(L, "__signal_handlers"); // push the old handler result to stack. if the global lua handler // table has a real function, then we prefer to return that. if it's // absent or a raw integer value, then we're better off returning // what the kernel gave us in &oldsa. - if (lua_geti(L, -1, sig) != LUA_TFUNCTION) { + if (lua_rawgeti(L, -1, sig) != LUA_TFUNCTION) { lua_pop(L, 1); lua_pushinteger(L, (intptr_t)oldsa.sa_handler); } @@ -1296,46 +1594,36 @@ static int LuaUnixSigaction(lua_State *L) { } else { lua_pushnil(L); } - lua_seti(L, -3, sig); + lua_rawseti(L, -3, sig); } // remove the signal handler table from stack lua_remove(L, -2); // finish pushing the last 2/3 results lua_pushinteger(L, oldsa.sa_flags); - lua_pushinteger(L, oldsa.sa_mask.__bits[0]); + LuaPushSigset(L, oldsa.sa_mask); return 3; } else { - return ReturnErrno(L, 3, olderr); + return SysretErrno(L, "sigaction", olderr); } } -// unix.sigsuspend([mask]) → errno +// unix.sigsuspend([mask:Sigmask]) +// └─→ nil, 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; + struct sigset *set; + if (!lua_isnoneornil(L, 1)) { + set = luaL_checkudata(L, 1, "unix.Sigset"); + } else { + set = 0; + } + sigsuspend(set); + return SysretErrno(L, "sigsuspend", 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 +// unix.setitimer(which[, intervalsec, intns, valuesec, valuens]) +// ├─→ intervalsec:int, intervalns:int, valuesec:int, valuens:int +// └─→ nil, unix.Errno static int LuaUnixSetitimer(lua_State *L) { int which, olderr; struct itimerval it, oldit, *itptr; @@ -1344,164 +1632,249 @@ static int LuaUnixSetitimer(lua_State *L) { if (!lua_isnoneornil(L, 2)) { itptr = ⁢ it.it_interval.tv_sec = luaL_optinteger(L, 2, 0); - it.it_interval.tv_usec = luaL_optinteger(L, 3, 0); + it.it_interval.tv_usec = luaL_optinteger(L, 3, 0) / 1000; it.it_value.tv_sec = luaL_optinteger(L, 4, 0); - it.it_value.tv_usec = luaL_optinteger(L, 5, 0); + it.it_value.tv_usec = luaL_optinteger(L, 5, 0) / 1000; } else { itptr = 0; } if (!setitimer(which, itptr, &oldit)) { lua_pushinteger(L, oldit.it_interval.tv_sec); - lua_pushinteger(L, oldit.it_interval.tv_usec); + lua_pushinteger(L, oldit.it_interval.tv_usec * 1000); lua_pushinteger(L, oldit.it_value.tv_sec); - lua_pushinteger(L, oldit.it_value.tv_usec); + lua_pushinteger(L, oldit.it_value.tv_usec * 1000); return 4; } else { - return ReturnErrno(L, 4, olderr); + return SysretErrno(L, "setitimer", olderr); } } -// unix.strerror(errno) → str -static int LuaUnixStrerror(lua_State *L) { - return ReturnString(L, strerror(luaL_checkinteger(L, 1))); +static int LuaUnixStr(lua_State *L, char *f(int)) { + return ReturnString(L, f(luaL_checkinteger(L, 1))); } -// unix.strerrno(errno) → str -static int LuaUnixStrerrno(lua_State *L) { - return ReturnString(L, strerror_short(luaL_checkinteger(L, 1))); -} - -// unix.strsignal(sig) → str +// unix.strsignal(sig:int) +// └─→ symbol:str static int LuaUnixStrsignal(lua_State *L) { - return ReturnString(L, strsignal(luaL_checkinteger(L, 1))); + return LuaUnixStr(L, strsignal); } -// unix.WIFEXITED(wstatus) → int +// unix.WIFEXITED(wstatus) +// └─→ bool static int LuaUnixWifexited(lua_State *L) { - return ReturnInteger(L, WIFEXITED(luaL_checkinteger(L, 1))); + return ReturnBoolean(L, WIFEXITED(luaL_checkinteger(L, 1))); } -// unix.WEXITSTATUS(wstatus) → int +// unix.WEXITSTATUS(wstatus) +// └─→ exitcode:uint8 static int LuaUnixWexitstatus(lua_State *L) { return ReturnInteger(L, WEXITSTATUS(luaL_checkinteger(L, 1))); } -// unix.WIFSIGNALED(wstatus) → int +// unix.WIFSIGNALED(wstatus) +// └─→ bool static int LuaUnixWifsignaled(lua_State *L) { - return ReturnInteger(L, WIFSIGNALED(luaL_checkinteger(L, 1))); + return ReturnBoolean(L, WIFSIGNALED(luaL_checkinteger(L, 1))); } -// unix.WTERMSIG(wstatus) → int +// unix.WTERMSIG(wstatus) +// └─→ sig:uint8 static int LuaUnixWtermsig(lua_State *L) { return ReturnInteger(L, WTERMSIG(luaL_checkinteger(L, 1))); } -//////////////////////////////////////////////////////////////////////////////// -// UnixStat* object - -static dontinline struct stat *GetUnixStat(lua_State *L) { - struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "UnixStat*"); - return &(*ust)->st; +static dontinline int LuaUnixTime(lua_State *L, const char *call, + struct tm *f(const time_t *, struct tm *)) { + int64_t ts; + struct tm tm; + int olderr = errno; + ts = luaL_checkinteger(L, 1); + if (f(&ts, &tm)) { + lua_pushinteger(L, tm.tm_year + 1900); + lua_pushinteger(L, tm.tm_mon + 1); // 1 ≤ mon ≤ 12 + lua_pushinteger(L, tm.tm_mday); // 1 ≤ mday ≤ 31 + lua_pushinteger(L, tm.tm_hour); // 0 ≤ hour ≤ 23 + lua_pushinteger(L, tm.tm_min); // 0 ≤ min ≤ 59 + lua_pushinteger(L, tm.tm_sec); // 0 ≤ sec ≤ 60 + lua_pushinteger(L, tm.tm_gmtoff); // ±93600 seconds + lua_pushinteger(L, tm.tm_wday); // 0 ≤ wday ≤ 6 + lua_pushinteger(L, tm.tm_yday); // 0 ≤ yday ≤ 365 + lua_pushinteger(L, tm.tm_isdst); // daylight savings + lua_pushstring(L, tm.tm_zone); + return 11; + } else { + return SysretErrno(L, call, olderr); + } } +// unix.gmtime(unixsecs:int) +// ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str +// └─→ nil,unix.Errno +static int LuaUnixGmtime(lua_State *L) { + return LuaUnixTime(L, "gmtime", gmtime_r); +} + +// unix.localtime(unixts:int) +// ├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str +// └─→ nil,unix.Errno +static int LuaUnixLocaltime(lua_State *L) { + return LuaUnixTime(L, "localtime", localtime_r); +} + +// unix.major(rdev:int) +// └─→ major:int +static int LuaUnixMajor(lua_State *L) { + return ReturnInteger(L, major(luaL_checkinteger(L, 1))); +} + +// unix.minor(rdev:int) +// └─→ minor:int +static int LuaUnixMinor(lua_State *L) { + return ReturnInteger(L, minor(luaL_checkinteger(L, 1))); +} + +//////////////////////////////////////////////////////////////////////////////// +// unix.Stat object + +static struct stat *GetUnixStat(lua_State *L) { + return luaL_checkudata(L, 1, "unix.Stat"); +} + +// unix.Stat:size() +// └─→ bytes:int static int LuaUnixStatSize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_size); } +// unix.Stat:mode() +// └─→ mode:int static int LuaUnixStatMode(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_mode); } +// unix.Stat:dev() +// └─→ dev:int static int LuaUnixStatDev(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_dev); } +// unix.Stat:ino() +// └─→ inodeint static int LuaUnixStatIno(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_ino); } +// unix.Stat:nlink() +// └─→ count:int static int LuaUnixStatNlink(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_nlink); } +// unix.Stat:rdev() +// └─→ rdev:int static int LuaUnixStatRdev(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_rdev); } +// unix.Stat:uid() +// └─→ uid:int static int LuaUnixStatUid(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_uid); } +// unix.Stat:gid() +// └─→ gid:int static int LuaUnixStatGid(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_gid); } +// unix.Stat:blocks() +// └─→ count:int static int LuaUnixStatBlocks(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blocks); } +// unix.Stat:blksize() +// └─→ bytes:int static int LuaUnixStatBlksize(lua_State *L) { return ReturnInteger(L, GetUnixStat(L)->st_blksize); } +static dontinline int ReturnTimespec(lua_State *L, struct timespec *ts) { + lua_pushinteger(L, ts->tv_sec); + lua_pushinteger(L, ts->tv_nsec); + return 2; +} + +// unix.Stat:atim() +// └─→ unixts:int, nanos:int static int LuaUnixStatAtim(lua_State *L) { return ReturnTimespec(L, &GetUnixStat(L)->st_atim); } +// unix.Stat:mtim() +// └─→ unixts:int, nanos:int static int LuaUnixStatMtim(lua_State *L) { return ReturnTimespec(L, &GetUnixStat(L)->st_mtim); } +// unix.Stat:ctim() +// └─→ unixts:int, nanos:int static int LuaUnixStatCtim(lua_State *L) { return ReturnTimespec(L, &GetUnixStat(L)->st_ctim); } +// unix.Stat:birthtim() +// └─→ unixts:int, nanos:int static int LuaUnixStatBirthtim(lua_State *L) { return ReturnTimespec(L, &GetUnixStat(L)->st_birthtim); } -static void FreeUnixStat(struct UnixStat *stat) { - if (!--stat->refs) { - free(stat); - } +// unix.Stat:gen() +// └─→ gen:int [xnu/bsd] +static int LuaUnixStatGen(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_gen); } -static int LuaUnixStatGc(lua_State *L) { - struct UnixStat **ust; - ust = luaL_checkudata(L, 1, "UnixStat*"); - if (*ust) { - FreeUnixStat(*ust); - *ust = 0; - } - return 0; +// unix.Stat:flags() +// └─→ flags:int [xnu/bsd] +static int LuaUnixStatFlags(lua_State *L) { + return ReturnInteger(L, GetUnixStat(L)->st_flags); +} + +static int LuaUnixStatToString(lua_State *L) { + struct stat *st = GetUnixStat(L); + lua_pushstring(L, "unix.Stat()"); + return 1; } 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}, // + {"flags", LuaUnixStatFlags}, // + {"gen", LuaUnixStatGen}, // {0}, // }; static const luaL_Reg kLuaUnixStatMeta[] = { - {"__gc", LuaUnixStatGc}, // - {0}, // + {"__tostring", LuaUnixStatToString}, // + {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); @@ -1510,64 +1883,421 @@ static void LuaUnixStatObj(lua_State *L) { } //////////////////////////////////////////////////////////////////////////////// -// UnixDir* object +// unix.Rusage object -static struct UnixDir **GetUnixDirSelf(lua_State *L) { - return luaL_checkudata(L, 1, "UnixDir*"); +static struct rusage *GetUnixRusage(lua_State *L) { + return luaL_checkudata(L, 1, "unix.Rusage"); +} + +static dontinline int ReturnTimeval(lua_State *L, struct timeval *tv) { + lua_pushinteger(L, tv->tv_sec); + lua_pushinteger(L, tv->tv_usec * 1000); + return 2; +} + +// unix.Rusage:utime() +// └─→ unixts:int, nanos:int +static int LuaUnixRusageUtime(lua_State *L) { + return ReturnTimeval(L, &GetUnixRusage(L)->ru_utime); +} + +// unix.Rusage:stime() +// └─→ unixts:int, nanos:int +static int LuaUnixRusageStime(lua_State *L) { + return ReturnTimeval(L, &GetUnixRusage(L)->ru_stime); +} + +// unix.Rusage:maxrss() +// └─→ kilobytes:int +static int LuaUnixRusageMaxrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_maxrss); +} + +// unix.Rusage:ixrss() +// └─→ integralkilobytes:int +static int LuaUnixRusageIxrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_ixrss); +} + +// unid.Rusage:idrss() +// └─→ integralkilobytes:int +static int LuaUnixRusageIdrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_idrss); +} + +// unis.Rusage:isrss() +// └─→ integralkilobytes:int +static int LuaUnixRusageIsrss(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_isrss); +} + +// unix.Rusage:minflt() +// └─→ count:int +static int LuaUnixRusageMinflt(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_minflt); +} + +// unix.Rusage:majflt() +// └─→ count:int +static int LuaUnixRusageMajflt(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_majflt); +} + +// unix.Rusage:nswap() +// └─→ count:int +static int LuaUnixRusageNswap(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nswap); +} + +// unix.Rusage:inblock() +// └─→ count:int +static int LuaUnixRusageInblock(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_inblock); +} + +// unix.Rusage:oublock() +// └─→ count:int +static int LuaUnixRusageOublock(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_oublock); +} + +// unix.Rusage:msgsnd() +// └─→ count:int +static int LuaUnixRusageMsgsnd(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_msgsnd); +} + +// unix.Rusage:msgrcv() +// └─→ count:int +static int LuaUnixRusageMsgrcv(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_msgrcv); +} + +// unix.Rusage:nsignals() +// └─→ count:int +static int LuaUnixRusageNsignals(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nsignals); +} + +// unix.Rusage:nvcsw() +// └─→ count:int +static int LuaUnixRusageNvcsw(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nvcsw); +} + +// unix.Rusage:nivcsw() +// └─→ count:int +static int LuaUnixRusageNivcsw(lua_State *L) { + return ReturnInteger(L, GetUnixRusage(L)->ru_nivcsw); +} + +static int LuaUnixRusageToString(lua_State *L) { + char *b = 0; + struct rusage *ru = GetUnixRusage(L); + appends(&b, "{"); + appendf(&b, "%s={%ld, %ld}", "utime", ru->ru_utime.tv_sec, + ru->ru_utime.tv_usec * 1000); + if (ru->ru_stime.tv_sec || ru->ru_stime.tv_usec) { + appendw(&b, READ16LE(", ")); + appendf(&b, "%s={%ld, %ld}", "stime", ru->ru_stime.tv_sec, + ru->ru_stime.tv_usec * 1000); + } + if (ru->ru_maxrss) appendf(&b, ", %s=%ld", "maxrss", ru->ru_maxrss); + if (ru->ru_ixrss) appendf(&b, ", %s=%ld", "ixrss", ru->ru_ixrss); + if (ru->ru_idrss) appendf(&b, ", %s=%ld", "idrss", ru->ru_idrss); + if (ru->ru_isrss) appendf(&b, ", %s=%ld", "isrss", ru->ru_isrss); + if (ru->ru_minflt) appendf(&b, ", %s=%ld", "minflt", ru->ru_minflt); + if (ru->ru_majflt) appendf(&b, ", %s=%ld", "majflt", ru->ru_majflt); + if (ru->ru_nswap) appendf(&b, ", %s=%ld", "nswap", ru->ru_nswap); + if (ru->ru_inblock) appendf(&b, ", %s=%ld", "inblock", ru->ru_inblock); + if (ru->ru_oublock) appendf(&b, ", %s=%ld", "oublock", ru->ru_oublock); + if (ru->ru_msgsnd) appendf(&b, ", %s=%ld", "msgsnd", ru->ru_msgsnd); + if (ru->ru_msgrcv) appendf(&b, ", %s=%ld", "msgrcv", ru->ru_msgrcv); + if (ru->ru_nsignals) appendf(&b, ", %s=%ld", "nsignals", ru->ru_nsignals); + if (ru->ru_nvcsw) appendf(&b, ", %s=%ld", "nvcsw", ru->ru_nvcsw); + if (ru->ru_nivcsw) appendf(&b, ", %s=%ld", "nivcsw", ru->ru_nivcsw); + appendw(&b, '}'); + lua_pushlstring(L, b, appendz(b).i); + return 1; +} + +static const luaL_Reg kLuaUnixRusageMeth[] = { + {"utime", LuaUnixRusageUtime}, // + {"stime", LuaUnixRusageStime}, // + {"maxrss", LuaUnixRusageMaxrss}, // + {"ixrss", LuaUnixRusageIxrss}, // + {"idrss", LuaUnixRusageIdrss}, // + {"isrss", LuaUnixRusageIsrss}, // + {"minflt", LuaUnixRusageMinflt}, // + {"majflt", LuaUnixRusageMajflt}, // + {"nswap", LuaUnixRusageNswap}, // + {"inblock", LuaUnixRusageInblock}, // + {"oublock", LuaUnixRusageOublock}, // + {"msgsnd", LuaUnixRusageMsgsnd}, // + {"msgrcv", LuaUnixRusageMsgrcv}, // + {"nsignals", LuaUnixRusageNsignals}, // + {"nvcsw", LuaUnixRusageNvcsw}, // + {"nivcsw", LuaUnixRusageNivcsw}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixRusageMeta[] = { + {"__repr", LuaUnixRusageToString}, // + {"__tostring", LuaUnixRusageToString}, // + {0}, // +}; + +static void LuaUnixRusageObj(lua_State *L) { + luaL_newmetatable(L, "unix.Rusage"); + luaL_setfuncs(L, kLuaUnixRusageMeta, 0); + luaL_newlibtable(L, kLuaUnixRusageMeth); + luaL_setfuncs(L, kLuaUnixRusageMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//////////////////////////////////////////////////////////////////////////////// +// unix.Errno object + +static struct UnixErrno *GetUnixErrno(lua_State *L) { + return luaL_checkudata(L, 1, "unix.Errno"); +} + +// unix.Errno:errno() +// └─→ errno:int +static int LuaUnixErrnoErrno(lua_State *L) { + return ReturnInteger(L, GetUnixErrno(L)->errno); +} + +static int LuaUnixErrnoWinerr(lua_State *L) { + return ReturnInteger(L, GetUnixErrno(L)->winerr); +} + +static int LuaUnixErrnoName(lua_State *L) { + return ReturnString(L, strerrno(GetUnixErrno(L)->errno)); +} + +static int LuaUnixErrnoDoc(lua_State *L) { + return ReturnString(L, strerdoc(GetUnixErrno(L)->errno)); +} + +static int LuaUnixErrnoCall(lua_State *L) { + return ReturnString(L, GetUnixErrno(L)->call); +} + +static int LuaUnixErrnoToString(lua_State *L) { + char msg[256]; + struct UnixErrno *e; + e = GetUnixErrno(L); + if (e->call) { + strerror_wr(e->errno, e->winerr, msg, sizeof(msg)); + lua_pushfstring(L, "%s() failed: %s", e->call, msg); + } else { + lua_pushstring(L, strerrno(e->errno)); + } + return 1; +} + +static const luaL_Reg kLuaUnixErrnoMeth[] = { + {"errno", LuaUnixErrnoErrno}, // + {"winerr", LuaUnixErrnoWinerr}, // + {"name", LuaUnixErrnoName}, // + {"call", LuaUnixErrnoCall}, // + {"doc", LuaUnixErrnoDoc}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixErrnoMeta[] = { + {"__tostring", LuaUnixErrnoToString}, // + {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.Sigset object + +// unix.Sigset(sig:int, ...) +// └─→ unix.Sigset +static int LuaUnixSigset(lua_State *L) { + int i, n; + lua_Integer sig; + struct sigset set; + sigemptyset(&set); + n = lua_gettop(L); + for (i = 1; i <= n; ++i) { + sig = luaL_checkinteger(L, i); + if (1 <= sig && sig <= NSIG) { + set.__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); + } + } + LuaPushSigset(L, set); + return 1; +} + +// unix.Sigset:add(sig:int) +static int LuaUnixSigsetAdd(lua_State *L) { + lua_Integer sig; + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + sig = luaL_checkinteger(L, 2); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); + } + return 0; +} + +// unix.Sigset:remove(sig:int) +static int LuaUnixSigsetRemove(lua_State *L) { + lua_Integer sig; + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + sig = luaL_checkinteger(L, 2); + if (1 <= sig && sig <= NSIG) { + set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63)); + } + return 0; +} + +// unix.Sigset:fill() +static int LuaUnixSigsetFill(lua_State *L) { + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + memset(set, -1, sizeof(*set)); + return 0; +} + +// unix.Sigset:clear() +static int LuaUnixSigsetClear(lua_State *L) { + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + bzero(set, sizeof(*set)); + return 0; +} + +// unix.Sigset:contains(sig:int) +// └─→ bool +static int LuaUnixSigsetContains(lua_State *L) { + lua_Integer sig; + struct sigset *set; + set = luaL_checkudata(L, 1, "unix.Sigset"); + sig = luaL_checkinteger(L, 2); + return ReturnBoolean( + L, (1 <= sig && sig <= NSIG) + ? !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))) + : false); +} + +static int LuaUnixSigsetTostring(lua_State *L) { + char *b = 0; + int sig, first; + struct sigset *ss; + ss = luaL_checkudata(L, 1, "unix.Sigset"); + appends(&b, "unix.Sigset"); + appendw(&b, '('); + for (sig = first = 1; sig <= NSIG; ++sig) { + if (sigismember(ss, sig) == 1) { + if (!first) { + appendw(&b, READ16LE(", ")); + } else { + first = 0; + } + appendw(&b, READ64LE("unix.\0\0")); + appends(&b, strsignal(sig)); + } + } + appendw(&b, ')'); + lua_pushlstring(L, b, appendz(b).i); + free(b); + return 1; +} + +static const luaL_Reg kLuaUnixSigsetMeth[] = { + {"add", LuaUnixSigsetAdd}, // + {"fill", LuaUnixSigsetFill}, // + {"clear", LuaUnixSigsetClear}, // + {"remove", LuaUnixSigsetRemove}, // + {"contains", LuaUnixSigsetContains}, // + {0}, // +}; + +static const luaL_Reg kLuaUnixSigsetMeta[] = { + {"__tostring", LuaUnixSigsetTostring}, // + {"__repr", LuaUnixSigsetTostring}, // + {0}, // +}; + +static void LuaUnixSigsetObj(lua_State *L) { + luaL_newmetatable(L, "unix.Sigset"); + luaL_setfuncs(L, kLuaUnixSigsetMeta, 0); + luaL_newlibtable(L, kLuaUnixSigsetMeth); + luaL_setfuncs(L, kLuaUnixSigsetMeth, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//////////////////////////////////////////////////////////////////////////////// +// unix.Dir object + +static DIR **GetUnixDirSelf(lua_State *L) { + return luaL_checkudata(L, 1, "unix.Dir"); } static DIR *GetDirOrDie(lua_State *L) { - struct UnixDir **udir; - udir = GetUnixDirSelf(L); - assert((*udir)->dir); - if (*udir) return (*udir)->dir; - luaL_argerror(L, 1, "UnixDir* is closed"); + DIR **dirp; + dirp = GetUnixDirSelf(L); + if (*dirp) return *dirp; + luaL_argerror(L, 1, "unix.UnixDir is closed"); unreachable; } -static int FreeUnixDir(struct UnixDir *dir) { - if (--dir->refs) return 0; - return closedir(dir->dir); -} - -// UnixDir:close() → rc:int[, errno:int] -// may be called multiple times -// called by the garbage collector too +// unix.Dir:close() +// ├─→ true +// └─→ nil, unix.Errno static int LuaUnixDirClose(lua_State *L) { + DIR **dirp; int rc, olderr; - struct UnixDir **udir; - udir = GetUnixDirSelf(L); - if (!*udir) return 0; - olderr = 0; - rc = FreeUnixDir(*udir); - *udir = 0; - return ReturnRc(L, rc, olderr); + dirp = GetUnixDirSelf(L); + if (*dirp) { + olderr = 0; + rc = closedir(*dirp); + *dirp = 0; + return SysretBool(L, "closedir", olderr, rc); + } else { + lua_pushboolean(L, true); + return 1; + } } -// UnixDir:read() → name, kind, ino, off[, errno] -// returns nil if no more entries -// kind can be DT_UNKNOWN/REG/DIR/BLK/LNK/CHR/FIFO/SOCK +// unix.Dir:read() +// ├─→ name:str, kind:int, ino:int, off:int +// └─→ nil static int LuaUnixDirRead(lua_State *L) { - int olderr; struct dirent *ent; - olderr = errno; - errno = 0; if ((ent = readdir(GetDirOrDie(L)))) { lua_pushlstring(L, ent->d_name, strnlen(ent->d_name, sizeof(ent->d_name))); lua_pushinteger(L, ent->d_type); lua_pushinteger(L, ent->d_ino); lua_pushinteger(L, ent->d_off); return 4; - } else if (!ent && !errno) { - return 0; // end of listing } else { - return ReturnErrno(L, 4, olderr); + // end of directory stream condition + // we make the assumption getdents() won't fail + lua_pushnil(L); + return 1; } } -// UnixDir:fd() → fd, errno -// EOPNOTSUPP if using /zip/ -// EOPNOTSUPP if IsWindows() +// unix.Dir:fd() +// ├─→ fd:int +// └─→ nil, unix.Errno static int LuaUnixDirFd(lua_State *L) { int fd, olderr; olderr = errno; @@ -1576,24 +2306,58 @@ static int LuaUnixDirFd(lua_State *L) { lua_pushinteger(L, fd); return 1; } else { - return ReturnErrno(L, 1, olderr); + return SysretErrno(L, "dirfd", olderr); } } -// UnixDir:tell() → off +// unix.Dir:tell() +// ├─→ off:int +// └─→ nil, unix.Errno static int LuaUnixDirTell(lua_State *L) { - long off; - off = telldir(GetDirOrDie(L)); - lua_pushinteger(L, off); - return 1; + int olderr = errno; + return SysretInteger(L, "telldir", olderr, telldir(GetDirOrDie(L))); } -// UnixDir:rewind() +// unix.Dir:rewind() static int LuaUnixDirRewind(lua_State *L) { rewinddir(GetDirOrDie(L)); return 0; } +static int ReturnDir(lua_State *L, DIR *dir) { + DIR **dirp; + dirp = lua_newuserdatauv(L, sizeof(*dirp), 1); + luaL_setmetatable(L, "unix.Dir"); + *dirp = dir; + return 1; +} + +// unix.opendir(path:str) +// ├─→ state:unix.Dir +// └─→ nil, unix.Errno +static int LuaUnixOpendir(lua_State *L) { + DIR *dir; + int olderr = errno; + if ((dir = opendir(luaL_checkstring(L, 1)))) { + return ReturnDir(L, dir); + } else { + return SysretErrno(L, "opendir", olderr); + } +} + +// unix.fdopendir(fd:int) +// ├─→ next:function, state:unix.Dir +// └─→ nil, unix.Errno +static int LuaUnixFdopendir(lua_State *L) { + DIR *dir; + int olderr = errno; + if ((dir = fdopendir(luaL_checkinteger(L, 1)))) { + return ReturnDir(L, dir); + } else { + return SysretErrno(L, "fdopendir", olderr); + } +} + static const luaL_Reg kLuaUnixDirMeth[] = { {"close", LuaUnixDirClose}, // {"read", LuaUnixDirRead}, // @@ -1604,12 +2368,13 @@ static const luaL_Reg kLuaUnixDirMeth[] = { }; static const luaL_Reg kLuaUnixDirMeta[] = { - {"__gc", LuaUnixDirClose}, // - {0}, // + {"__call", LuaUnixDirRead}, // + {"__gc", LuaUnixDirClose}, // + {0}, // }; 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); @@ -1621,11 +2386,13 @@ static void LuaUnixDirObj(lua_State *L) { // UNIX module static const luaL_Reg kLuaUnix[] = { + {"Sigset", LuaUnixSigset}, // creates signal bitmask {"exit", LuaUnixExit}, // exit w/o atexit - {"stat", LuaUnixStat}, // get file info + {"stat", LuaUnixStat}, // get file info from path + {"fstat", LuaUnixFstat}, // get file info from fd {"open", LuaUnixOpen}, // open file fd at lowest slot {"close", LuaUnixClose}, // close file or socket - {"seek", LuaUnixSeek}, // seek in file + {"lseek", LuaUnixLseek}, // seek in file {"read", LuaUnixRead}, // read from file or socket {"write", LuaUnixWrite}, // write to file or socket {"access", LuaUnixAccess}, // check my file authorization @@ -1633,9 +2400,11 @@ static const luaL_Reg kLuaUnix[] = { {"chdir", LuaUnixChdir}, // change directory {"chown", LuaUnixChown}, // change owner of file {"chmod", LuaUnixChmod}, // change mode of file + {"readlink", LuaUnixReadlink}, // reads symbolic link {"getcwd", LuaUnixGetcwd}, // get current directory {"fork", LuaUnixFork}, // make child process via mitosis {"execve", LuaUnixExecve}, // replace process with program + {"environ", LuaUnixEnviron}, // get environment variables {"commandv", LuaUnixCommandv}, // resolve program on $PATH {"realpath", LuaUnixRealpath}, // abspath without dots/symlinks {"syslog", LuaUnixSyslog}, // logs to system log @@ -1648,6 +2417,7 @@ static const luaL_Reg kLuaUnix[] = { {"makedirs", LuaUnixMakedirs}, // make directory and parents too {"rmdir", LuaUnixRmdir}, // remove empty directory {"opendir", LuaUnixOpendir}, // read directory entry list + {"fdopendir", LuaUnixFdopendir}, // read directory entry list {"rename", LuaUnixRename}, // rename file or directory {"link", LuaUnixLink}, // create hard link {"unlink", LuaUnixUnlink}, // remove file @@ -1656,11 +2426,12 @@ static const luaL_Reg kLuaUnix[] = { {"fsync", LuaUnixFsync}, // flush open file {"fdatasync", LuaUnixFdatasync}, // flush open file w/o metadata {"truncate", LuaUnixTruncate}, // shrink or extend file medium - {"ftruncate", LuaUnixTruncate}, // shrink or extend file medium + {"ftruncate", LuaUnixFtruncate}, // shrink or extend file medium {"umask", LuaUnixUmask}, // set default file mask {"chroot", LuaUnixChroot}, // change root directory {"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs {"getrlimit", LuaUnixGetrlimit}, // query resource limits + {"getrusage", LuaUnixGetrusage}, // query resource usages {"getppid", LuaUnixGetppid}, // get parent process id {"getpgrp", LuaUnixGetpgrp}, // get process group id {"getpgid", LuaUnixGetpgid}, // get process group id of pid @@ -1670,14 +2441,20 @@ static const luaL_Reg kLuaUnix[] = { {"setsid", LuaUnixSetsid}, // create a new session id {"getpid", LuaUnixGetpid}, // get id of this process {"getuid", LuaUnixGetuid}, // get real user id of process + {"geteuid", LuaUnixGeteuid}, // get effective user id of process {"setuid", LuaUnixSetuid}, // set real user id of process + {"setresuid", LuaUnixSetresuid}, // sets real/effective/saved uids {"getgid", LuaUnixGetgid}, // get real group id of process + {"getegid", LuaUnixGetegid}, // get effective group id of process {"setgid", LuaUnixSetgid}, // set real group id of process + {"setresgid", LuaUnixSetresgid}, // sets real/effective/saved gids {"gethostname", LuaUnixGethostname}, // get hostname of this machine {"clock_gettime", LuaUnixGettime}, // get timestamp w/ nano precision {"nanosleep", LuaUnixNanosleep}, // sleep w/ nano precision {"socket", LuaUnixSocket}, // create network communication fd {"socketpair", LuaUnixSocketpair}, // create bidirectional pipe + {"setsockopt", LuaUnixSetsockopt}, // tune socket options + {"getsockopt", LuaUnixGetsockopt}, // get socket tunings {"poll", LuaUnixPoll}, // waits for file descriptor events {"bind", LuaUnixBind}, // reserve network interface address {"listen", LuaUnixListen}, // begin listening for clients @@ -1695,8 +2472,11 @@ static const luaL_Reg kLuaUnix[] = { {"sigprocmask", LuaUnixSigprocmask}, // change signal mask {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"setitimer", LuaUnixSetitimer}, // set alarm clock - {"strerror", LuaUnixStrerror}, // turn errno into string - {"strerrno", LuaUnixStrerrno}, // turn errno into string + {"gmtime", LuaUnixGmtime}, // destructure unix timestamp + {"pledge", LuaUnixPledge}, // enables syscall sandbox + {"localtime", LuaUnixLocaltime}, // localize unix timestamp + {"major", LuaUnixMajor}, // extract device info + {"minor", LuaUnixMinor}, // extract device info {"strsignal", LuaUnixStrsignal}, // turn signal into string {"WIFEXITED", LuaUnixWifexited}, // gets exit code from wait status {"WEXITSTATUS", LuaUnixWexitstatus}, // gets exit status from wait status @@ -1705,60 +2485,34 @@ static const luaL_Reg kLuaUnix[] = { {0}, // }; -int LuaUnix(lua_State *L) { +static void LoadMagnums(lua_State *L, struct MagnumStr *ms, const char *pfx) { int i; - char sigbuf[12]; + char b[64], *p; + p = stpcpy(b, pfx); + for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) { + stpcpy(p, MAGNUM_STRING(ms, i)); + LuaSetIntField(L, b, MAGNUM_NUMBER(ms, i)); + } +} +int LuaUnix(lua_State *L) { GL = L; luaL_newlib(L, kLuaUnix); + LuaUnixSigsetObj(L); + LuaUnixRusageObj(L); + LuaUnixErrnoObj(L); LuaUnixStatObj(L); LuaUnixDirObj(L); lua_newtable(L); lua_setglobal(L, "__signal_handlers"); - // errnos - for (i = 0; kErrorNames[i].x; ++i) { - LuaSetIntField(L, (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s), - *(const int *)((uintptr_t)kErrorNames + kErrorNames[i].x)); - } - - // signals - strcpy(sigbuf, "SIG"); - for (i = 0; kStrSignal[i].x; ++i) { - strcpy(sigbuf + 3, (const char *)((uintptr_t)kStrSignal + kStrSignal[i].s)); - LuaSetIntField(L, sigbuf, - *(const int *)((uintptr_t)kStrSignal + kStrSignal[i].x)); - } - - // 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, kErrnoNames, ""); + LoadMagnums(L, kOpenFlags, "O_"); + LoadMagnums(L, kSignalNames, "SIG"); + LoadMagnums(L, kIpOptnames, "IP_"); + LoadMagnums(L, kTcpOptnames, "TCP_"); + LoadMagnums(L, kSockOptnames, "SO_"); + LoadMagnums(L, kClockNames, "CLOCK_"); // seek() whence LuaSetIntField(L, "SEEK_SET", SEEK_SET); @@ -1795,19 +2549,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); @@ -1816,11 +2557,17 @@ int LuaUnix(lua_State *L) { // socket() type LuaSetIntField(L, "SOCK_STREAM", SOCK_STREAM); LuaSetIntField(L, "SOCK_DGRAM", SOCK_DGRAM); + LuaSetIntField(L, "SOCK_RAW", SOCK_RAW); + LuaSetIntField(L, "SOCK_RDM", SOCK_RDM); + LuaSetIntField(L, "SOCK_SEQPACKET", SOCK_SEQPACKET); LuaSetIntField(L, "SOCK_CLOEXEC", SOCK_CLOEXEC); // socket() protocol + LuaSetIntField(L, "IPPROTO_IP", IPPROTO_IP); + LuaSetIntField(L, "IPPROTO_ICMP", IPPROTO_ICMP); LuaSetIntField(L, "IPPROTO_TCP", IPPROTO_TCP); LuaSetIntField(L, "IPPROTO_UDP", IPPROTO_UDP); + LuaSetIntField(L, "IPPROTO_RAW", IPPROTO_RAW); // shutdown() how LuaSetIntField(L, "SHUT_RD", SHUT_RD); @@ -1844,7 +2591,7 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "DT_FIFO", DT_FIFO); LuaSetIntField(L, "DT_SOCK", DT_SOCK); - // readdir() type + // poll() flags LuaSetIntField(L, "POLLIN", POLLIN); LuaSetIntField(L, "POLLPRI", POLLPRI); LuaSetIntField(L, "POLLOUT", POLLOUT); @@ -1859,6 +2606,7 @@ int LuaUnix(lua_State *L) { // i/o options LuaSetIntField(L, "AT_FDCWD", AT_FDCWD); + LuaSetIntField(L, "AT_EACCESS", AT_EACCESS); LuaSetIntField(L, "AT_SYMLINK_NOFOLLOW", AT_SYMLINK_NOFOLLOW); // sigprocmask() handlers @@ -1885,5 +2633,32 @@ int LuaUnix(lua_State *L) { LuaSetIntField(L, "LOG_INFO", LOG_INFO); LuaSetIntField(L, "LOG_DEBUG", LOG_DEBUG); + // setsockopt() level + LuaSetIntField(L, "SOL_IP", SOL_IP); + LuaSetIntField(L, "SOL_SOCKET", SOL_SOCKET); + LuaSetIntField(L, "SOL_TCP", SOL_TCP); + LuaSetIntField(L, "SOL_UDP", SOL_UDP); + + // sigaction() flags + LuaSetIntField(L, "SA_RESTART", SA_RESTART); + LuaSetIntField(L, "SA_RESETHAND", SA_RESETHAND); + LuaSetIntField(L, "SA_NODEFER", SA_NODEFER); + LuaSetIntField(L, "SA_NOCLDWAIT", SA_NOCLDWAIT); + LuaSetIntField(L, "SA_NOCLDSTOP", SA_NOCLDSTOP); + + // getrusage() who + LuaSetIntField(L, "RUSAGE_SELF", RUSAGE_SELF); + LuaSetIntField(L, "RUSAGE_THREAD", RUSAGE_THREAD); + LuaSetIntField(L, "RUSAGE_CHILDREN", RUSAGE_CHILDREN); + LuaSetIntField(L, "RUSAGE_BOTH", RUSAGE_BOTH); + + LuaSetIntField(L, "ARG_MAX", __arg_max()); + LuaSetIntField(L, "BUFSIZ", BUFSIZ); + LuaSetIntField(L, "CLK_TCK", CLK_TCK); + LuaSetIntField(L, "NAME_MAX", _NAME_MAX); + LuaSetIntField(L, "NSIG", _NSIG); + LuaSetIntField(L, "PATH_MAX", _PATH_MAX); + LuaSetIntField(L, "PIPE_BUF", PIPE_BUF); + return 1; } diff --git a/tool/net/net.mk b/tool/net/net.mk index edede4163..3de3d5ec2 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -90,7 +90,8 @@ o/$(MODE)/tool/net/%.com.dbg: \ o/$(MODE)/tool/net/redbean.com.dbg: \ $(TOOL_NET_DEPS) \ - o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/lfuncs.o \ o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -116,9 +117,10 @@ o/$(MODE)/tool/net/redbean.com: \ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean/.ape \ - o/$(MODE)/tool/net/.redbean/.symtab \ tool/net/help.txt \ tool/net/.init.lua \ tool/net/favicon.ico \ @@ -174,6 +176,7 @@ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ +o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ @@ -208,7 +211,8 @@ o/$(MODE)/tool/net/demo/virtualbean.html.zip.o: \ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ $(TOOL_NET_DEPS) \ - o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/lfuncs.o \ o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -219,6 +223,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \ + o/$(MODE)/tool/net/demo/unix-dir.lua.zip.o \ o/$(MODE)/tool/net/demo/unix-info.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ o/$(MODE)/tool/net/demo/hello.lua.zip.o \ @@ -258,9 +263,10 @@ o/$(MODE)/tool/net/redbean-demo.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-demo/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-demo/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-demo/.ape \ - o/$(MODE)/tool/net/.redbean-demo/.symtab \ tool/net/help.txt # REDBEAN-STATIC.COM @@ -280,9 +286,10 @@ o/$(MODE)/tool/net/redbean-static.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-static/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-static/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-static/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-static/.ape \ - o/$(MODE)/tool/net/.redbean-static/.symtab \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -316,9 +323,10 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-unsecure/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-unsecure/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-unsecure/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-unsecure/.ape \ - o/$(MODE)/tool/net/.redbean-unsecure/.symtab \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -326,6 +334,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean-unsecure.o \ + o/$(MODE)/tool/net/lfuncs.o \ o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -358,9 +367,10 @@ o/$(MODE)/tool/net/redbean-original.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/net/.redbean-original/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-original/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-original/.ape \ - o/$(MODE)/tool/net/.redbean-original/.symtab \ tool/net/help.txt \ tool/net/favicon.ico \ tool/net/redbean.png @@ -408,6 +418,9 @@ o/$(MODE)/tool/net/redbean-original.com.dbg: \ o/$(MODE)/tool/net/redbean-original.o: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -DSTATIC -DUNSECURE -DREDBEAN=\"redbean-original\" $(OUTPUT_OPTION) $< +o/$(MODE)/tool/net/redbean-original.s: tool/net/redbean.c o/$(MODE)/tool/net/redbean.o + @$(COMPILE) -AOBJECTIFY.c $(COMPILE.c) -DSTATIC -DUNSECURE -DREDBEAN=\"redbean-original\" $(OUTPUT_OPTION) $< + # REDBEAN-ASSIMILATE.COM # # Same as REDBEAN.COM except without no-modify-self behavior. @@ -428,6 +441,8 @@ o/$(MODE)/tool/net/redbean-assimilate.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-assimilate @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean-assimilate/.symtab $< + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ + o/$(MODE)/tool/net/.redbean-assimilate/.symtab @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ o/$(MODE)/tool/net/.redbean-assimilate/.symtab \ tool/net/help.txt \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index ca65139e0..777b36463 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -57,11 +57,13 @@ #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" +#include "libc/nt/version.h" #include "libc/rand/rand.h" #include "libc/runtime/clktck.h" #include "libc/runtime/directmap.internal.h" #include "libc/runtime/gc.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" @@ -146,6 +148,7 @@ #include "third_party/zlib/zlib.h" #include "tool/build/lib/case.h" #include "tool/build/lib/psk.h" +#include "tool/net/lfuncs.h" #include "tool/net/luacheck.h" #include "tool/net/sandbox.h" @@ -176,7 +179,7 @@ STATIC_YOINK("zip_uri_support"); #define REDBEAN "redbean" #endif -#define VERSION 0x010500 +#define VERSION 0x020000 #define HEARTBEAT 5000 /*ms*/ #define HASH_LOAD_FACTOR /* 1. / */ 4 #define READ(F, P, N) readv(F, &(struct iovec){P, N}, 1) @@ -189,10 +192,28 @@ STATIC_YOINK("zip_uri_support"); #define HeaderEqualCase(H, S) \ SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) -// letters not used: EIJNOQWXYinoqwxy +#define TRACE_BEGIN \ + do { \ + if (!IsTiny()) { \ + if (funtrace) ++g_ftrace; \ + if (systrace) ++__strace; \ + } \ + } while (0) + +#define TRACE_END \ + do { \ + if (!IsTiny()) { \ + if (funtrace) --g_ftrace; \ + if (systrace) --__strace; \ + } \ + } while (0) + +// 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:" + +extern unsigned long long __kbirth; static const uint8_t kGzipHeader[] = { 0x1F, // MAGNUM @@ -377,6 +398,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; @@ -1016,8 +1038,17 @@ static bool IsServerFd(int fd) { } static void ChangeUser(void) { - if (changegid) LOGIFNEG1(setgid(changegid)); - if (changeuid) LOGIFNEG1(setuid(changeuid)); + if (changegid) { + if (setgid(changegid)) { + FATALF("setgid() failed: %m"); + } + } + // order matters + if (changeuid) { + if (setuid(changeuid)) { + FATALF("setuid() failed: %m"); + } + } } static void Daemonize(void) { @@ -1056,7 +1087,7 @@ static bool LuaEvalCode(const char *code) { lua_pop(L, 1); // pop error return false; } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); return true; } @@ -1084,7 +1115,7 @@ static bool LuaOnClientConnection(void) { dropit = false; } lua_pop(L, 1); // pop result or error - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); return dropit; #else return false; @@ -1108,7 +1139,7 @@ static void LuaOnProcessCreate(int pid) { LogLuaError("OnProcessCreate", lua_tostring(L, -1)); lua_pop(L, 1); // pop error } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); #endif } @@ -1121,7 +1152,7 @@ static void LuaOnProcessDestroy(int pid) { LogLuaError("OnProcessDestroy", lua_tostring(L, -1)); lua_pop(L, 1); // pop error } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, 0); #endif } @@ -1139,12 +1170,13 @@ static inline bool IsHookDefined(const char *s) { static void CallSimpleHook(const char *s) { #ifndef STATIC lua_State *L = GL; + int n = lua_gettop(L); lua_getglobal(L, s); if (LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) { LogLuaError(s, lua_tostring(L, -1)); lua_pop(L, 1); // pop error } - AssertLuaStackIsEmpty(L); + AssertLuaStackIsAt(L, n); #endif } @@ -1174,10 +1206,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); @@ -1855,6 +1887,13 @@ static bool ClientAcceptsGzip(void) { HeaderHas(&msg, inbuf.p, kHttpAcceptEncoding, "gzip", 4); } +char *FormatUnixHttpDateTime(char *s, int64_t t) { + struct tm tm; + gmtime_r(&t, &tm); + FormatHttpDateTime(s, &tm); + return s; +} + static void UpdateCurrentDate(long double now) { int64_t t; struct tm tm; @@ -1879,13 +1918,6 @@ forceinline int GetMode(struct Asset *a) { return a->file ? a->file->st.st_mode : GetZipCfileMode(zbase + a->cf); } -static char *FormatUnixHttpDateTime(char *s, int64_t t) { - struct tm tm; - gmtime_r(&t, &tm); - FormatHttpDateTime(s, &tm); - return s; -} - forceinline bool IsCompressionMethodSupported(int method) { return method == kZipCompressionNone || method == kZipCompressionDeflate; } @@ -2225,37 +2257,14 @@ static void *LoadAsset(struct Asset *a, size_t *out_size) { static wontreturn void PrintUsage(int fd, int rc) { size_t n; - int pip[2]; const char *p; struct Asset *a; - char buf[PATH_MAX]; - char *args[2] = {0}; if (!(a = GetAssetZip("/help.txt", 9)) || !(p = LoadAsset(a, &n))) { fprintf(stderr, "error: /help.txt is not a zip asset\n"); exit(1); } - if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) && - ((args[0] = commandv("less", buf)) || - (args[0] = commandv("more", buf)))) { - sigaction(SIGPIPE, &(struct sigaction){.sa_handler = SIG_IGN}, 0); - close(0); - pipe(pip); - if (!fork()) { - close(pip[1]); - execv(args[0], args); - _Exit(127); - } - close(0); - WritevAll(pip[1], &(struct iovec){p, n}, 1); - close(pip[1]); - wait(0); - free(p); - exit(0); - } else { - WritevAll(fd, &(struct iovec){p, n}, 1); - free(p); - exit(rc); - } + __paginate(fd, p); + exit(rc); } static void AppendLogo(void) { @@ -2714,7 +2723,8 @@ static void LaunchBrowser(const char *path) { // assign a loopback address if no server or unknown server address if (!servers.n || !addr.s_addr) addr.s_addr = htonl(INADDR_LOOPBACK); if (*path != '/') path = gc(xasprintf("/%s", path)); - if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX))))) { + if ((prog = commandv(GetSystemUrlLauncherCommand(), gc(malloc(PATH_MAX)), + PATH_MAX))) { u = gc(xasprintf("http://%s:%d%s", inet_ntoa(addr), port, gc(EscapePath(path, -1, 0)))); DEBUGF("(srvr) opening browser with command %`'s %s", prog, u); @@ -3013,23 +3023,15 @@ static char *GetLuaResponse(void) { return luaheaderp ? luaheaderp : SetStatus(200, "OK"); } -static bool IsLoopbackClient(void) { - uint32_t ip; - uint16_t port; - GetRemoteAddr(&ip, &port); - return IsLoopbackIp(ip); -} - -static bool IsPrivateClient(void) { - uint32_t ip; - uint16_t port; - GetRemoteAddr(&ip, &port); - return IsLoopbackIp(ip) || IsPrivateIp(ip); -} - static bool ShouldServeCrashReportDetails(void) { - if (leakcrashreports) return true; - return IsPrivateClient(); + uint32_t ip; + uint16_t port; + if (leakcrashreports) { + return true; + } else { + GetRemoteAddr(&ip, &port); + return IsLoopbackIp(ip) || IsPrivateIp(ip); + } } static char *LuaOnHttpRequest(void) { @@ -3161,29 +3163,32 @@ static const char *LuaCheckHost(lua_State *L, int idx, size_t *hostlen) { static void OnlyCallFromInitLua(lua_State *L, const char *api) { if (isinitialized) { - luaL_error(L, "%s() should be called from the global scope of .init.lua", - api); + luaL_error(L, "%s() should be called %s", api, + "from the global scope of .init.lua"); unreachable; } } -static void DontCallFromInitLua(lua_State *L, const char *api) { - if (!isinitialized) { - luaL_error(L, "%s() can't be called from .init.lua", api); +static void OnlyCallFromMainProcess(lua_State *L, const char *api) { + if (__isworker) { + luaL_error(L, "%s() should be called %s", api, + "from .init.lua or the repl"); unreachable; } } static void OnlyCallDuringConnection(lua_State *L, const char *api) { if (!ishandlingconnection) { - luaL_error(L, "%s() can only be called while handling a connection", api); + luaL_error(L, "%s() can only be called ", api, + "while handling a connection"); unreachable; } } static void OnlyCallDuringRequest(lua_State *L, const char *api) { if (!ishandlingrequest) { - luaL_error(L, "%s() can only be called while handling a request", api); + luaL_error(L, "%s() can only be called %s", api, + "while handling a request"); unreachable; } } @@ -3674,7 +3679,7 @@ static void LogBody(const char *d, const char *s, size_t n) { } static int LuaFetch(lua_State *L) { -#define ssl nope /* TODO(jart): make this file less huge */ +#define ssl nope // TODO(jart): make this file less huge char *p; ssize_t rc; bool usessl; @@ -4112,55 +4117,58 @@ static int LuaGetRemoteAddr(lua_State *L) { return LuaGetAddr(L, GetRemoteAddr); } -static int LuaFormatIp(lua_State *L) { - char b[16]; - uint32_t ip; - ip = htonl(luaL_checkinteger(L, 1)); - inet_ntop(AF_INET, &ip, b, sizeof(b)); - lua_pushstring(L, b); +static int LuaLog(lua_State *L) { + int level, line; + lua_Debug ar; + const char *msg, *module; + level = luaL_checkinteger(L, 1); + if (LOGGABLE(level)) { + msg = luaL_checkstring(L, 2); + if (lua_getstack(L, 1, &ar) && lua_getinfo(L, "Sl", &ar)) { + module = ar.short_src; + line = ar.currentline; + } else { + module = gc(strndup(effectivepath.p, effectivepath.n)); + line = -1; + } + flogf(level, module, line, NULL, "%s", msg); + } + return 0; +} + +static int LuaEncodeSmth(lua_State *L, + int Encoder(lua_State *, char **, char *, int)) { + int useoutput = false; + int maxdepth = 64; + char *numformat = "%.14g"; + char *p = 0; + if (lua_istable(L, 2)) { + lua_settop(L, 2); // discard any extra arguments + lua_getfield(L, 2, "useoutput"); + // ignore useoutput outside of request handling + if (ishandlingrequest && lua_isboolean(L, -1)) { + useoutput = lua_toboolean(L, -1); + } + lua_getfield(L, 2, "numformat"); + numformat = luaL_optstring(L, -1, numformat); + } + lua_settop(L, 1); // keep the passed argument on top + Encoder(L, useoutput ? &outbuf : &p, numformat, -1); + if (useoutput) { + lua_pushnil(L); + } else { + lua_pushstring(L, p); + free(p); + } return 1; } -static int LuaParseIp(lua_State *L) { - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - lua_pushinteger(L, ParseIp(s, n)); - return 1; +static int LuaEncodeJson(lua_State *L) { + return LuaEncodeSmth(L, LuaEncodeJsonData); } -static int LuaIsIp(lua_State *L, bool IsIp(uint32_t)) { - lua_pushboolean(L, IsIp(luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaIsPublicIp(lua_State *L) { - return LuaIsIp(L, IsPublicIp); -} - -static int LuaIsPrivateIp(lua_State *L) { - return LuaIsIp(L, IsPrivateIp); -} - -static int LuaIsLoopbackIp(lua_State *L) { - return LuaIsIp(L, IsLoopbackIp); -} - -static int LuaIsLoopbackClient(lua_State *L) { - OnlyCallDuringRequest(L, "IsLoopbackClient"); - lua_pushboolean(L, IsLoopbackClient()); - return 1; -} - -static int LuaIsPrivateClient(lua_State *L) { - OnlyCallDuringRequest(L, "IsPrivateClient"); - lua_pushboolean(L, IsPrivateClient()); - return 1; -} - -static int LuaCategorizeIp(lua_State *L) { - lua_pushstring(L, GetIpCategoryName(CategorizeIp(luaL_checkinteger(L, 1)))); - return 1; +static int LuaEncodeLua(lua_State *L) { + return LuaEncodeSmth(L, LuaEncodeLuaData); } static int LuaGetUrl(lua_State *L) { @@ -4173,14 +4181,6 @@ static int LuaGetUrl(lua_State *L) { return 1; } -static void LuaPushUrlView(lua_State *L, struct UrlView *v) { - if (v->p) { - lua_pushlstring(L, v->p, v->n); - } else { - lua_pushnil(L); - } -} - static int LuaGetScheme(lua_State *L) { OnlyCallDuringRequest(L, "GetScheme"); LuaPushUrlView(L, &url.scheme); @@ -4261,21 +4261,8 @@ static int LuaGetPort(lua_State *L) { return 1; } -static int LuaFormatHttpDateTime(lua_State *L) { - char buf[30]; - lua_pushstring(L, FormatUnixHttpDateTime(buf, luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaParseHttpDateTime(lua_State *L) { - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - lua_pushinteger(L, ParseHttpDateTime(s, n)); - return 1; -} - static int LuaGetBody(lua_State *L) { + OnlyCallDuringRequest(L, "GetBody"); lua_pushlstring(L, inbuf.p + hdrsize, payloadlength); return 1; } @@ -4526,35 +4513,6 @@ static int LuaGetParams(lua_State *L) { return 1; } -static int LuaParseParams(lua_State *L) { - void *m; - size_t size; - const char *data; - struct UrlParams h; - data = luaL_checklstring(L, 1, &size); - bzero(&h, sizeof(h)); - m = ParseParams(data, size, &h); - LuaPushUrlParams(L, &h); - free(h.p); - free(m); - return 1; -} - -static int LuaParseHost(lua_State *L) { - void *m; - size_t n; - struct Url h; - const char *p; - bzero(&h, sizeof(h)); - p = luaL_checklstring(L, 1, &n); - m = ParseHost(p, n, &h); - lua_newtable(L); - LuaPushUrlView(L, &h.host); - LuaPushUrlView(L, &h.port); - free(m); - return 1; -} - static int LuaWrite(lua_State *L) { size_t size; const char *data; @@ -4566,350 +4524,6 @@ static int LuaWrite(lua_State *L) { return 0; } -static int LuaCheckControlFlags(lua_State *L, int idx) { - int f = luaL_checkinteger(L, idx); - if (f & ~(kControlWs | kControlC0 | kControlC1)) { - luaL_argerror(L, idx, "invalid control flags"); - unreachable; - } - return f; -} - -static int LuaHasControlCodes(lua_State *L) { - int f; - size_t n; - const char *p; - p = luaL_checklstring(L, 1, &n); - f = LuaCheckControlFlags(L, 2); - lua_pushboolean(L, HasControlCodes(p, n, f) != -1); - return 1; -} - -static int LuaIsValid(lua_State *L, bool V(const char *, size_t)) { - size_t size; - const char *data; - data = luaL_checklstring(L, 1, &size); - lua_pushboolean(L, V(data, size)); - return 1; -} - -static int LuaIsValidHttpToken(lua_State *L) { - return LuaIsValid(L, IsValidHttpToken); -} - -static int LuaIsAcceptablePath(lua_State *L) { - return LuaIsValid(L, IsAcceptablePath); -} - -static int LuaIsReasonablePath(lua_State *L) { - return LuaIsValid(L, IsReasonablePath); -} - -static int LuaIsAcceptableHost(lua_State *L) { - return LuaIsValid(L, IsAcceptableHost); -} - -static int LuaIsAcceptablePort(lua_State *L) { - return LuaIsValid(L, IsAcceptablePort); -} - -static dontinline int LuaCoderImpl(lua_State *L, - char *C(const char *, size_t, size_t *)) { - void *p; - size_t n; - p = luaL_checklstring(L, 1, &n); - p = C(p, n, &n); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static dontinline int LuaCoder(lua_State *L, - char *C(const char *, size_t, size_t *)) { - return LuaCoderImpl(L, C); -} - -static int LuaUnderlong(lua_State *L) { - return LuaCoder(L, Underlong); -} - -static int LuaEncodeBase64(lua_State *L) { - return LuaCoder(L, EncodeBase64); -} - -static int LuaDecodeBase64(lua_State *L) { - return LuaCoder(L, DecodeBase64); -} - -static int LuaDecodeLatin1(lua_State *L) { - return LuaCoder(L, DecodeLatin1); -} - -static int LuaEscapeHtml(lua_State *L) { - return LuaCoder(L, EscapeHtml); -} - -static int LuaEscapeParam(lua_State *L) { - return LuaCoder(L, EscapeParam); -} - -static int LuaEscapePath(lua_State *L) { - return LuaCoder(L, EscapePath); -} - -static int LuaEscapeHost(lua_State *L) { - return LuaCoder(L, EscapeHost); -} - -static int LuaEscapeIp(lua_State *L) { - return LuaCoder(L, EscapeIp); -} - -static int LuaEscapeUser(lua_State *L) { - return LuaCoder(L, EscapeUser); -} - -static int LuaEscapePass(lua_State *L) { - return LuaCoder(L, EscapePass); -} - -static int LuaEscapeSegment(lua_State *L) { - return LuaCoder(L, EscapeSegment); -} - -static int LuaEscapeFragment(lua_State *L) { - return LuaCoder(L, EscapeFragment); -} - -static int LuaEscapeLiteral(lua_State *L) { - return LuaCoder(L, EscapeJsStringLiteral); -} - -static int LuaVisualizeControlCodes(lua_State *L) { - return LuaCoder(L, VisualizeControlCodes); -} - -static dontinline int LuaHasherImpl(lua_State *L, size_t k, - int H(const void *, size_t, uint8_t *)) { - void *p; - size_t n; - uint8_t d[64]; - p = luaL_checklstring(L, 1, &n); - H(p, n, d); - lua_pushlstring(L, (void *)d, k); - mbedtls_platform_zeroize(d, sizeof(d)); - return 1; -} - -static dontinline int LuaHasher(lua_State *L, size_t k, - int H(const void *, size_t, uint8_t *)) { - return LuaHasherImpl(L, k, H); -} - -static int LuaMd5(lua_State *L) { - return LuaHasher(L, 16, mbedtls_md5_ret); -} - -static int LuaSha1(lua_State *L) { - return LuaHasher(L, 20, mbedtls_sha1_ret); -} - -static int LuaSha224(lua_State *L) { - return LuaHasher(L, 28, mbedtls_sha256_ret_224); -} - -static int LuaSha256(lua_State *L) { - return LuaHasher(L, 32, mbedtls_sha256_ret_256); -} - -static int LuaSha384(lua_State *L) { - return LuaHasher(L, 48, mbedtls_sha512_ret_384); -} - -static int LuaSha512(lua_State *L) { - return LuaHasher(L, 64, mbedtls_sha512_ret_512); -} - -static dontinline int LuaGetCryptoHash(lua_State *L) { - size_t hl, pl, kl; - uint8_t d[64]; - mbedtls_md_context_t ctx; - // get hash name, payload, and key - void *h = luaL_checklstring(L, 1, &hl); - void *p = luaL_checklstring(L, 2, &pl); - void *k = luaL_optlstring(L, 3, "", &kl); - - const mbedtls_md_info_t *digest = mbedtls_md_info_from_string(h); - if (!digest) return luaL_argerror(L, 1, "unknown hash type"); - - if (kl == 0) { - // no key provided, run generic hash function - if ((digest->f_md)(p, pl, d)) return luaL_error(L, "bad input data"); - } else if (mbedtls_md_hmac(digest, k, kl, p, pl, d)) - return luaL_error(L, "bad input data"); - - lua_pushlstring(L, (void *)d, digest->size); - mbedtls_platform_zeroize(d, sizeof(d)); - return 1; -} - -static int LuaGetRandomBytes(lua_State *L) { - char *p; - size_t n = luaL_optinteger(L, 1, 16); - if (!(n > 0 && n <= 256)) { - luaL_argerror(L, 1, "not in range 1..256"); - unreachable; - } - - p = malloc(n); - CHECK_EQ(n, getrandom(p, n, 0)); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static int LuaGetHttpReason(lua_State *L) { - lua_pushstring(L, GetHttpReason(luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaEncodeSmth(lua_State *L, - int Encoder(lua_State *, char **, int, char *)) { - int useoutput = false; - int maxdepth = 64; - char *numformat = "%.14g"; - char *p = 0; - if (lua_istable(L, 2)) { - lua_settop(L, 2); // discard any extra arguments - lua_getfield(L, 2, "useoutput"); - // ignore useoutput outside of request handling - if (ishandlingrequest && lua_isboolean(L, -1)) - useoutput = lua_toboolean(L, -1); - lua_getfield(L, 2, "maxdepth"); - maxdepth = luaL_optinteger(L, -1, maxdepth); - lua_getfield(L, 2, "numformat"); - numformat = luaL_optstring(L, -1, numformat); - } - lua_settop(L, 1); // keep the passed argument on top - Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat); - if (useoutput) { - lua_pushnil(L); - } else { - lua_pushstring(L, p); - free(p); - } - return 1; -} - -static int LuaEncodeJson(lua_State *L) { - return LuaEncodeSmth(L, LuaEncodeJsonData); -} - -static int LuaEncodeLua(lua_State *L) { - return LuaEncodeSmth(L, LuaEncodeLuaData); -} - -static int LuaEncodeLatin1(lua_State *L) { - int f; - char *p; - size_t n; - p = luaL_checklstring(L, 1, &n); - f = LuaCheckControlFlags(L, 2); - p = EncodeLatin1(p, n, &n, f); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static int LuaSlurp(lua_State *L) { - char *p, *f; - size_t n; - f = luaL_checkstring(L, 1); - if ((p = xslurp(f, &n))) { - lua_pushlstring(L, p, n); - free(p); - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, gc(xasprintf("Can't slurp file %`'s: %m", f))); - return 2; - } -} - -static int LuaIndentLines(lua_State *L) { - void *p; - size_t n, j; - p = luaL_checklstring(L, 1, &n); - j = luaL_optinteger(L, 2, 1); - if (!(0 <= j && j <= 65535)) { - luaL_argerror(L, 2, "not in range 0..65535"); - unreachable; - } - p = IndentLines(p, n, &n, j); - lua_pushlstring(L, p, n); - free(p); - return 1; -} - -static int LuaGetMonospaceWidth(lua_State *L) { - int w; - if (lua_isinteger(L, 1)) { - w = wcwidth(lua_tointeger(L, 1)); - } else if (lua_isstring(L, 1)) { - w = strwidth(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0) & 7); - } else { - luaL_argerror(L, 1, "not integer or string"); - unreachable; - } - lua_pushinteger(L, w); - return 1; -} - -static int LuaPopcnt(lua_State *L) { - lua_pushinteger(L, popcnt(luaL_checkinteger(L, 1))); - return 1; -} - -static int LuaBsr(lua_State *L) { - long x; - if ((x = luaL_checkinteger(L, 1))) { - lua_pushinteger(L, bsr(x)); - return 1; - } else { - luaL_argerror(L, 1, "zero"); - unreachable; - } -} - -static int LuaBsf(lua_State *L) { - long x; - if ((x = luaL_checkinteger(L, 1))) { - lua_pushinteger(L, bsf(x)); - return 1; - } else { - luaL_argerror(L, 1, "zero"); - unreachable; - } -} - -static int LuaHash(lua_State *L, uint32_t H(uint32_t, const void *, size_t)) { - long i; - size_t n; - const char *p; - i = luaL_checkinteger(L, 1); - p = luaL_checklstring(L, 2, &n); - lua_pushinteger(L, H(i, p, n)); - return 1; -} - -static int LuaCrc32(lua_State *L) { - return LuaHash(L, crc32_z); -} - -static int LuaCrc32c(lua_State *L) { - return LuaHash(L, crc32c); -} - static dontinline int LuaProgramInt(lua_State *L, void P(long)) { P(luaL_checkinteger(L, 1)); return 0; @@ -4921,7 +4535,7 @@ static int LuaProgramPort(lua_State *L) { } static int LuaProgramCache(lua_State *L) { - OnlyCallFromInitLua(L, "ProgramCache"); + OnlyCallFromMainProcess(L, "ProgramCache"); return LuaProgramInt(L, ProgramCache); } @@ -4940,6 +4554,18 @@ static int LuaProgramGid(lua_State *L) { return LuaProgramInt(L, ProgramGid); } +static int LuaGetClientFd(lua_State *L) { + OnlyCallDuringConnection(L, "GetClientFd"); + lua_pushinteger(L, client); + return 1; +} + +static int LuaIsClientUsingSsl(lua_State *L) { + OnlyCallDuringConnection(L, "IsClientUsingSsl"); + lua_pushboolean(L, usessl); + return 1; +} + static int LuaProgramSslTicketLifetime(lua_State *L) { OnlyCallFromInitLua(L, "ProgramSslTicketLifetime"); return LuaProgramInt(L, ProgramSslTicketLifetime); @@ -4947,9 +4573,9 @@ static int LuaProgramSslTicketLifetime(lua_State *L) { static int LuaProgramUniprocess(lua_State *L) { OnlyCallFromInitLua(L, "ProgramUniprocess"); - if (!lua_isboolean(L, 1) && !lua_isnoneornil(L, 1)) + if (!lua_isboolean(L, 1) && !lua_isnoneornil(L, 1)) { return luaL_argerror(L, 1, "invalid uniprocess mode; boolean expected"); - + } lua_pushboolean(L, uniprocess); if (lua_isboolean(L, 1)) uniprocess = lua_toboolean(L, 1); return 1; @@ -4966,7 +4592,7 @@ static int LuaProgramAddr(lua_State *L) { } static int LuaProgramBrand(lua_State *L) { - OnlyCallFromInitLua(L, "ProgramBrand"); + OnlyCallFromMainProcess(L, "ProgramBrand"); return LuaProgramString(L, ProgramBrand); } @@ -4988,7 +4614,7 @@ static int LuaProgramSslPresharedKey(lua_State *L) { struct Psk psk; size_t n1, n2, i; const char *p1, *p2; - OnlyCallFromInitLua(L, "ProgramSslPresharedKey"); + OnlyCallFromMainProcess(L, "ProgramSslPresharedKey"); p1 = luaL_checklstring(L, 1, &n1); p2 = luaL_checklstring(L, 2, &n2); if (!n1 || n1 > MBEDTLS_PSK_MAX_LEN || !n2) { @@ -5016,6 +4642,7 @@ static int LuaProgramSslPresharedKey(lua_State *L) { static int LuaProgramSslCiphersuite(lua_State *L) { mbedtls_ssl_ciphersuite_t *suite; + OnlyCallFromInitLua(L, "ProgramSslCiphersuite"); if (!(suite = GetCipherSuite(luaL_checkstring(L, 1)))) { luaL_argerror(L, 1, "unsupported or unknown ciphersuite"); unreachable; @@ -5071,11 +4698,12 @@ static int LuaProgramSslClientVerify(lua_State *L) { } static int LuaProgramSslFetchVerify(lua_State *L) { - OnlyCallFromInitLua(L, "ProgramSslFetchVerify"); + OnlyCallFromMainProcess(L, "ProgramSslFetchVerify"); return LuaProgramBool(L, &sslfetchverify); } static int LuaProgramSslInit(lua_State *L) { + OnlyCallFromInitLua(L, "SslInit"); TlsInit(); return 0; } @@ -5089,7 +4717,6 @@ static int LuaProgramLogBodies(lua_State *L) { } static int LuaEvadeDragnetSurveillance(lua_State *L) { - OnlyCallFromInitLua(L, "EvadeDragnetSurveillance"); return LuaProgramBool(L, &evadedragnetsurveillance); } @@ -5101,16 +4728,6 @@ static int LuaProgramSslCompression(lua_State *L) { return 0; } -static int LuaGetLogLevel(lua_State *L) { - lua_pushinteger(L, __log_level); - return 1; -} - -static int LuaSetLogLevel(lua_State *L) { - __log_level = luaL_checkinteger(L, 1); - return 0; -} - static int LuaHidePath(lua_State *L) { size_t pathlen; const char *path; @@ -5119,35 +4736,6 @@ static int LuaHidePath(lua_State *L) { return 0; } -static int LuaLog(lua_State *L) { - int level, line; - lua_Debug ar; - const char *msg, *module; - level = luaL_checkinteger(L, 1); - if (LOGGABLE(level)) { - msg = luaL_checkstring(L, 2); - if (lua_getstack(L, 1, &ar) && lua_getinfo(L, "Sl", &ar)) { - module = ar.short_src; - line = ar.currentline; - } else { - module = gc(strndup(effectivepath.p, effectivepath.n)); - line = -1; - } - flogf(level, module, line, NULL, "%s", msg); - } - return 0; -} - -static int LuaSleep(lua_State *L) { - usleep(1e6 * luaL_checknumber(L, 1)); - return 0; -} - -static int LuaGetTime(lua_State *L) { - lua_pushnumber(L, nowl()); - return 1; -} - static int LuaIsHiddenPath(lua_State *L) { size_t n; const char *s; @@ -5156,21 +4744,6 @@ static int LuaIsHiddenPath(lua_State *L) { return 1; } -static int LuaIsHeaderRepeatable(lua_State *L) { - int h; - bool r; - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - if ((h = GetHttpHeader(s, n)) != -1) { - r = kHttpRepeatable[h]; - } else { - r = false; - } - lua_pushboolean(L, r); - return 1; -} - static int LuaGetZipPaths(lua_State *L) { char *path; uint8_t *zcf; @@ -5276,31 +4849,6 @@ static int LuaGetAssetComment(lua_State *L) { return 1; } -static int LuaGetHostOs(lua_State *L) { - const char *s = NULL; - if (IsLinux()) { - s = "LINUX"; - } else if (IsMetal()) { - s = "METAL"; - } else if (IsWindows()) { - s = "WINDOWS"; - } else if (IsXnu()) { - s = "XNU"; - } else if (IsOpenbsd()) { - s = "OPENBSD"; - } else if (IsFreebsd()) { - s = "FREEBSD"; - } else if (IsNetbsd()) { - s = "NETBSD"; - } - if (s) { - lua_pushstring(L, s); - } else { - lua_pushnil(L); - } - return 1; -} - static int LuaLaunchBrowser(lua_State *L) { OnlyCallFromInitLua(L, "LaunchBrowser"); launchbrowser = strdup(luaL_optstring(L, 1, "/")); @@ -5331,77 +4879,68 @@ static bool LuaRunAsset(const char *path, bool mandatory) { return !!a; } -static int LuaRdtsc(lua_State *L) { - lua_pushinteger(L, rdtsc()); - return 1; -} - -static int LuaGetCpuNode(lua_State *L) { - lua_pushinteger(L, TSC_AUX_NODE(rdpid())); - return 1; -} - -static int LuaGetCpuCore(lua_State *L) { - lua_pushinteger(L, TSC_AUX_CORE(rdpid())); - return 1; -} - -static int LuaRand(lua_State *L, uint64_t impl(void)) { - lua_pushinteger(L, impl()); - return 1; -} - -static int LuaLemur64(lua_State *L) { - return LuaRand(L, lemur64); -} - -static int LuaRand64(lua_State *L) { - return LuaRand(L, rand64); -} - -static int LuaRdrand(lua_State *L) { - return LuaRand(L, rdrand); -} - -static int LuaRdseed(lua_State *L) { - return LuaRand(L, rdseed); -} - -static int LuaDecimate(lua_State *L) { - size_t n, m; - const char *s; - unsigned char *p; - s = luaL_checklstring(L, 1, &n); - m = ROUNDUP(n, 16); - p = xmalloc(m); - bzero(p + n, m - n); - cDecimate2xUint8x8(m, p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); - lua_pushlstring(L, (char *)p, (n + 1) >> 1); - free(p); - return 1; -} - -static int LuaMeasureEntropy(lua_State *L) { - size_t n; - const char *s; - s = luaL_checklstring(L, 1, &n); - lua_pushnumber(L, MeasureEntropy(s, n)); - return 1; -} - -static int LuaGetClientFd(lua_State *L) { - OnlyCallDuringConnection(L, "GetClientFd"); - lua_pushinteger(L, client); - return 1; -} - -static int LuaIsClientUsingSsl(lua_State *L) { - OnlyCallDuringConnection(L, "IsClientUsingSsl"); - lua_pushboolean(L, usessl); - return 1; -} +// +// list of functions that can't be run from the repl +static const char *const kDontAutoComplete[] = { + "GetBody", // + "GetClientAddr", // + "GetClientFd", // + "GetCookie", // + "GetEffectivePath", // + "GetFragment", // + "GetHeader", // + "GetHeaders", // + "GetHost", // + "GetHttpVersion", // + "GetMethod", // + "GetParam", // + "GetParams", // + "GetPass", // + "GetPath", // + "GetPort", // + "GetRemoteAddr", // + "GetScheme", // + "GetServerAddr", // + "GetSslIdentity", // + "GetStatus", // + "GetUrl", // + "GetUser", // + "HasParam", // + "IsClientUsingSsl", // + "LaunchBrowser", // + "ProgramAddr", // TODO + "ProgramBrand", // + "ProgramCertificate", // TODO + "ProgramGid", // + "ProgramLogPath", // TODO + "ProgramPidPath", // TODO + "ProgramPort", // TODO + "ProgramPrivateKey", // TODO + "ProgramSslCiphersuite", // TODO + "ProgramSslClientVerify", // TODO + "ProgramSslCompression", // + "ProgramSslTicketLifetime", // + "ProgramTimeout", // TODO + "ProgramUid", // + "ProgramUniprocess", // + "Respond", // + "Route", // + "RouteHost", // + "RoutePath", // + "ServeAsset", // + "ServeIndex", // + "ServeListing", // + "ServeRedirect", // + "ServeStatusz", // + "SetCookie", // + "SetHeader", // + "SslInit", // TODO + "Write", // +}; +// static const luaL_Reg kLuaFuncs[] = { + {"Benchmark", LuaBenchmark}, // {"Bsf", LuaBsf}, // {"Bsr", LuaBsr}, // {"CategorizeIp", LuaCategorizeIp}, // @@ -5436,6 +4975,7 @@ static const luaL_Reg kLuaFuncs[] = { {"GetComment", LuaGetAssetComment}, // {"GetCookie", LuaGetCookie}, // {"GetCpuCore", LuaGetCpuCore}, // + {"GetCpuCount", LuaGetCpuCount}, // {"GetCpuNode", LuaGetCpuNode}, // {"GetCryptoHash", LuaGetCryptoHash}, // {"GetDate", LuaGetDate}, // @@ -5480,9 +5020,7 @@ static const luaL_Reg kLuaFuncs[] = { {"IsDaemon", LuaIsDaemon}, // {"IsHeaderRepeatable", LuaIsHeaderRepeatable}, // {"IsHiddenPath", LuaIsHiddenPath}, // - {"IsLoopbackClient", LuaIsLoopbackClient}, // {"IsLoopbackIp", LuaIsLoopbackIp}, // - {"IsPrivateClient", LuaIsPrivateClient}, // {"IsPrivateIp", LuaIsPrivateIp}, // {"IsPublicIp", LuaIsPublicIp}, // {"IsReasonablePath", LuaIsReasonablePath}, // @@ -5544,6 +5082,9 @@ static const luaL_Reg kLuaFuncs[] = { {"Underlong", LuaUnderlong}, // {"VisualizeControlCodes", LuaVisualizeControlCodes}, // {"Write", LuaWrite}, // + {"bin", LuaBin}, // + {"hex", LuaHex}, // + {"oct", LuaOct}, // #ifndef UNSECURE {"Fetch", LuaFetch}, // {"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, // @@ -5558,12 +5099,6 @@ static const luaL_Reg kLuaFuncs[] = { #endif }; -int LuaMaxmind(lua_State *); -int LuaRe(lua_State *); -int LuaUnix(lua_State *); -int luaopen_argon2(lua_State *); -int luaopen_lsqlite3(lua_State *); - static const luaL_Reg kLuaLibs[] = { {"re", LuaRe}, // {"unix", LuaUnix}, // @@ -5575,13 +5110,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) { @@ -5623,10 +5166,116 @@ 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); + if (n > 0) { + for (i = 1; i <= n; i++) { + if (i > 1) appendw(&b, '\t'); + LuaEncodeLuaData(L, &b, "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 + TRACE_BEGIN; + status = lua_runchunk(L, n, LUA_MULTRET); + TRACE_END; + } + 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); + 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) { + TRACE_BEGIN; + status = lua_runchunk(GL, 0, LUA_MULTRET); + TRACE_END; + } + 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"); @@ -6704,8 +6353,7 @@ static int EnableSandbox(void) { sandbox = &kSandboxOfflineProg; break; } - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != -1 && - prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, sandbox) != -1) { + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, sandbox) != -1) { return 0; } else { return -1; @@ -6733,20 +6381,10 @@ static int HandleConnection(size_t i) { meltdown = false; __isworker = true; connectionclose = false; - if (!IsTiny()) { - if (systrace) { - extern unsigned long long __kbirth; - __strace = 1; - __kbirth = rdtsc(); - } - if (funtrace) { - if (ftrace_install() != -1) { - g_ftrace = 1; - } else { - WARNF("ftrace failed to install %m"); - } - } + if (!IsTiny() && systrace) { + __kbirth = rdtsc(); } + TRACE_BEGIN; if (sandboxed) { CHECK_NE(-1, EnableSandbox()); } @@ -6845,35 +6483,35 @@ static void RestoreApe(void) { struct Asset *a; extern char ape_rom_vaddr[] __attribute__((__weak__)); if (!(SUPPORT_VECTOR & (METAL | WINDOWS | XNU))) return; - if (IsWindows()) return; /* TODO */ - if (IsOpenbsd()) return; /* TODO */ - if (IsNetbsd()) return; /* TODO */ + if (IsWindows()) return; // TODO + if (IsOpenbsd()) return; // TODO + if (IsNetbsd()) return; // TODO if (endswith(zpath, ".com.dbg")) return; if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) { close(zfd); - if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) + if ((zfd = OpenExecutable()) == -1 || WRITE(zfd, p, n) == -1) { WARNF("(srvr) can't restore .ape"); + } free(p); } else { - INFOF("(srvr) /.ape not found"); + DEBUGF("(srvr) /.ape not found"); } } static int HandleReadline(void) { int status; + lua_State *L = GL; for (;;) { - status = lua_loadline(GL); + status = lua_loadline(L); if (status < 0) { if (status == -1) { OnTerm(SIGHUP); // eof INFOF("got repl eof"); - write(1, "\r\n", 2); return -1; } else if (errno == EINTR) { errno = 0; - OnInt(SIGINT); INFOF("got repl interrupt"); - return 0; + return -1; } else if (errno == EAGAIN) { errno = 0; return 0; @@ -6882,18 +6520,17 @@ static int HandleReadline(void) { return -1; } } - write(1, "\r\n", 2); linenoiseDisableRawMode(); - _spinlock(&lualock); + LUA_REPL_LOCK; if (status == LUA_OK) { - status = lua_runchunk(GL, 0, LUA_MULTRET); + status = lua_runchunk(L, 0, LUA_MULTRET); } if (status == LUA_OK) { - lua_l_print(GL); + LuaPrint(L); } else { - lua_report(GL, status); + lua_report(L, status); } - _spunlock(&lualock); + LUA_REPL_UNLOCK; if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } @@ -6911,21 +6548,24 @@ static int HandlePoll(int ms) { if (polls[pollid].fd < 0) continue; if (polls[pollid].fd) { // handle listen socket + LUA_REPL_LOCK; serverid = pollid - 1; assert(0 <= serverid && serverid < servers.n); serveraddr = &servers.p[serverid].addr; ishandlingconnection = true; - _spinlock(&lualock); rc = HandleConnection(serverid); - _spunlock(&lualock); ishandlingconnection = false; + LUA_REPL_UNLOCK; if (rc == -1) return -1; +#ifndef STATIC } else { // handle standard input rc = HandleReadline(); if (rc == -1) return rc; +#endif } } +#ifndef STATIC } else if (__replmode) { // handle refresh repl line if (!IsWindows()) { @@ -6934,6 +6574,7 @@ static int HandlePoll(int ms) { } else { linenoiseRefreshLine(lua_repl_linenoise); } +#endif } } else { if (errno == EINTR || errno == EAGAIN) { @@ -7029,16 +6670,22 @@ static void HandleShutdown(void) { // this function coroutines with linenoise static int EventLoop(int ms) { long double t; - VERBOSEF("EventLoop()"); + DEBUGF("EventLoop()"); while (!terminated) { errno = 0; if (zombied) { + LUA_REPL_LOCK; ReapZombies(); + LUA_REPL_UNLOCK; } else if (invalidated) { + LUA_REPL_LOCK; HandleReload(); + LUA_REPL_UNLOCK; invalidated = false; } else if (meltdown) { + LUA_REPL_LOCK; EnterMeltdownMode(); + LUA_REPL_UNLOCK; meltdown = false; } else if ((t = nowl()) - lastheartbeat > HEARTBEAT / 1000.) { lastheartbeat = t; @@ -7051,23 +6698,28 @@ static int EventLoop(int ms) { } static void ReplEventLoop(void) { + lua_State *L = GL; DEBUGF("ReplEventLoop()"); polls[0].fd = 0; - lua_initrepl(GL, "redbean"); + lua_repl_completions_callback = HandleCompletions; + lua_initrepl(L, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } EventLoop(100); linenoiseDisableRawMode(); lua_freerepl(); - lua_settop(GL, 0); // clear stack + lua_settop(L, 0); // clear stack polls[0].fd = -1; } static uint32_t WindowsReplThread(void *arg) { + int sig; + lua_State *L = GL; DEBUGF("WindowsReplThread()"); lua_repl_blocking = true; - lua_initrepl(GL, "redbean"); + lua_repl_completions_callback = HandleCompletions; + lua_initrepl(L, "redbean"); if (lua_repl_isterminal) { linenoiseEnableRawMode(0); } @@ -7078,9 +6730,13 @@ static uint32_t WindowsReplThread(void *arg) { } linenoiseDisableRawMode(); lua_freerepl(); - _spinlock(&lualock); - lua_settop(GL, 0); // clear stack - _spunlock(&lualock); + LUA_REPL_LOCK; + lua_settop(L, 0); // clear stack + LUA_REPL_UNLOCK; + if ((sig = linenoiseGetInterrupt())) { + raise(sig); + } + DEBUGF("WindowsReplThread() exiting"); return 0; } @@ -7201,7 +6857,6 @@ static void GetOpts(int argc, char *argv[]) { CASE('S', ++sandboxed); CASE('v', ++__log_level); CASE('s', --__log_level); - CASE('f', funtrace = true); CASE('Z', systrace = true); CASE('b', logbodies = true); CASE('z', printport = true); @@ -7227,6 +6882,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 @@ -7239,6 +6895,12 @@ static void GetOpts(int argc, char *argv[]) { CASE('C', ProgramFile(optarg, ProgramCertificate)); CASE('K', ProgramFile(optarg, ProgramPrivateKey)); #endif + case 'f': + funtrace = true; + if (ftrace_install() == -1) { + WARNF("ftrace failed to install %m"); + } + break; default: PrintUsage(2, EX_USAGE); } @@ -7248,6 +6910,11 @@ static void GetOpts(int argc, char *argv[]) { } void RedBean(int argc, char *argv[]) { + if (IsLinux()) { + // disable sneak privilege since we don't use them + // seccomp will fail later if this fails + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + } reader = read; writer = WritevAll; gmtoff = GetGmtOffset((lastrefresh = startserver = nowl())); @@ -7256,7 +6923,7 @@ void RedBean(int argc, char *argv[]) { (shared = mmap(NULL, ROUNDUP(sizeof(struct Shared), FRAMESIZE), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0))); - zpath = program_executable_name; + zpath = GetProgramExecutableName(); CHECK_NE(-1, (zfd = open(zpath, O_RDONLY))); CHECK_NE(-1, fstat(zfd, &zst)); OpenZip(true); @@ -7296,7 +6963,7 @@ void RedBean(int argc, char *argv[]) { #else GetHostsTxt(); // for effect GetResolvConf(); // for effect - if (daemonize || !linenoiseIsTerminal()) { + if (daemonize || uniprocess || !linenoiseIsTerminal()) { EventLoop(HEARTBEAT); } else if (IsWindows()) { uint32_t tid; diff --git a/tool/net/sandbox.h b/tool/net/sandbox.h index 37a796a80..82af014eb 100644 --- a/tool/net/sandbox.h +++ b/tool/net/sandbox.h @@ -29,4 +29,7 @@ #define _SECCOMP_LOG_AND_RETURN_ERRNO(MAGNUM) \ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ((MAGNUM) & SECCOMP_RET_DATA)) +#define _SECCOMP_LOG_AND_KILL_PROCESS() \ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | SECCOMP_RET_KILL_PROCESS) + #endif /* COSMOPOLITAN_TOOL_NET_SANDBOX_H_ */ diff --git a/tool/plinko/lib/gc.c b/tool/plinko/lib/gc.c index 0b270d740..6e12485d8 100644 --- a/tool/plinko/lib/gc.c +++ b/tool/plinko/lib/gc.c @@ -50,7 +50,7 @@ struct Gc *NewGc(int A) { if (B < cHeap) cHeap = B; n = ROUNDUP(A - B, DWBITS) / DWBITS; G = Addr(BANE); - memset(G->M, 0, n * sizeof(G->M[0])); + bzero(G->M, n * sizeof(G->M[0])); G->n = n; G->A = A; G->B = B; diff --git a/tool/plinko/plinko.mk b/tool/plinko/plinko.mk index dd96951fb..f841c1c40 100644 --- a/tool/plinko/plinko.mk +++ b/tool/plinko/plinko.mk @@ -55,7 +55,7 @@ o/$(MODE)/tool/plinko/plinko.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/plinko/.redbean @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/plinko/.plinko/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/plinko/.plinko/.symtab $(TOOL_PLINKO_OBJS): \ diff --git a/tool/viz/bin2asm.c b/tool/viz/bin2asm.c index 7af7011db..4e2e19457 100644 --- a/tool/viz/bin2asm.c +++ b/tool/viz/bin2asm.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { while ((c = getchar()) != -1) { if (col == 0) { printf("\t.byte\t"); - memset(glyphs, 0, sizeof(glyphs)); + bzero(glyphs, sizeof(glyphs)); } ch = c & 0xff; glyphs[col] = kCp437[ch]; diff --git a/tool/viz/derasterize.c b/tool/viz/derasterize.c index 45f5115ae..0b35ca2d4 100644 --- a/tool/viz/derasterize.c +++ b/tool/viz/derasterize.c @@ -248,7 +248,7 @@ static unsigned combinecolors(unsigned char bf[1u << MC][2], const unsigned char bl[CN][YS * XS]) { uint64_t hv, ht[(1u << MC) * 2]; unsigned i, j, n, b, f, h, hi, bu, fu; - memset(ht, 0, sizeof(ht)); + bzero(ht, sizeof(ht)); for (n = b = 0; b < BN && n < (1u << MC); ++b) { bu = bl[2][b] << 020 | bl[1][b] << 010 | bl[0][b]; hi = 0; @@ -295,7 +295,7 @@ static unsigned combinecolors(unsigned char bf[1u << MC][2], const float lb[CN][YS * XS]) { \ unsigned i, k, gu; \ float p[BN], q[BN], fu, bu, r; \ - memset(q, 0, sizeof(q)); \ + bzero(q, sizeof(q)); \ for (k = 0; k < CN; ++k) { \ gu = kGlyphs[g]; \ bu = lb[k][b]; \ @@ -336,7 +336,7 @@ static float adjudicate(unsigned b, unsigned f, unsigned g, const float lb[CN][YS * XS]) { unsigned i, k, gu; float p[BN], q[BN], fu, bu, r; - memset(q, 0, sizeof(q)); + bzero(q, sizeof(q)); for (k = 0; k < CN; ++k) { gu = kGlyphs[g]; bu = lb[k][b]; @@ -488,7 +488,7 @@ static void LoadFileViaImageMagick(const char *path, unsigned yn, unsigned xn, const char *convert; int pid, ws, pipefds[2]; char pathbuf[PATH_MAX], dim[32]; - if (!(convert = commandv("convert", pathbuf))) { + if (!(convert = commandv("convert", pathbuf, sizeof(pathbuf)))) { fputs("error: `convert` command not found\n" "try: apt-get install imagemagick\n", stderr); diff --git a/tool/viz/lib/bilinearscale.c b/tool/viz/lib/bilinearscale.c index 7836787cc..edfa5b077 100644 --- a/tool/viz/lib/bilinearscale.c +++ b/tool/viz/lib/bilinearscale.c @@ -93,7 +93,7 @@ void *BilinearScale(long dcw, long dyw, long dxw, gc(xmemalign(64, ROUNDUP(dxn, 64))), gc(xmemalign(64, ROUNDUP(sxn, 64) * 2))); } else { - memset(dst[c0], 0, &dst[cn][0][0] - &dst[c0][0][0]); + bzero(dst[c0], &dst[cn][0][0] - &dst[c0][0][0]); } } return dst; diff --git a/tool/viz/lib/convolve.h b/tool/viz/lib/convolve.h index 66ca31813..892af17f3 100644 --- a/tool/viz/lib/convolve.h +++ b/tool/viz/lib/convolve.h @@ -19,12 +19,12 @@ forceinline void convolve(unsigned yn, unsigned xn, __m128 img[yn][xn], int KW, kflip[KW - i - 1][KW - j - 1] = (__v4sf){f, f, f, f}; } } - memset(&g, 0, sizeof(g)); + bzero(&g, sizeof(g)); resizegraphic(&g, yn, xn); tmp = g.b.p; for (y = 0; y < yn - KW; ++y) { for (x = 0; x < xn - KW; ++x) { - memset(&p, 0, sizeof(p)); + bzero(&p, sizeof(p)); for (i = 0; i < KW; ++i) { for (j = 0; j < KW; ++j) { p += img[y + i][x + j] * kflip[i][j] + C2; diff --git a/tool/viz/lib/unsharp.c b/tool/viz/lib/unsharp.c index 56cfbe3ae..db1932c91 100644 --- a/tool/viz/lib/unsharp.c +++ b/tool/viz/lib/unsharp.c @@ -45,7 +45,7 @@ long unsharp(long cn, long yw, long xw, unsigned char img[cn][yw][xw], long yn, for (x = 0; x < xn; ++x) { img[c][y - 3][x] = MIN(255, MAX(0, (*t)[y % 3][x])); } - memset((*t)[y % 3], 0, sizeof(short) * xn); + bzero((*t)[y % 3], sizeof(short) * xn); } if (y < yn) { for (x = 0; x < xn; ++x) { diff --git a/tool/viz/lib/ycbcr2rgb3.c b/tool/viz/lib/ycbcr2rgb3.c index 806d8824b..9d1df4c04 100644 --- a/tool/viz/lib/ycbcr2rgb3.c +++ b/tool/viz/lib/ycbcr2rgb3.c @@ -155,8 +155,8 @@ void YCbCrInit(struct YCbCr **ycbcr, bool yonly, int swing, double gamma, const double gamut[3], const double illuminant[3]) { if (!*ycbcr) *ycbcr = xcalloc(1, sizeof(struct YCbCr)); (*ycbcr)->yonly = yonly; - memset((*ycbcr)->magnums, 0, sizeof((*ycbcr)->magnums)); - memset((*ycbcr)->lighting, 0, sizeof((*ycbcr)->lighting)); + bzero((*ycbcr)->magnums, sizeof((*ycbcr)->magnums)); + bzero((*ycbcr)->lighting, sizeof((*ycbcr)->lighting)); YCbCrComputeCoefficients(swing, gamma, gamut, illuminant, (*ycbcr)->magnums, (*ycbcr)->lighting, (*ycbcr)->transfer[0]); imapxlatab((*ycbcr)->transfer[1]); diff --git a/tool/viz/life.c b/tool/viz/life.c index a648022b3..0b984ab13 100644 --- a/tool/viz/life.c +++ b/tool/viz/life.c @@ -612,7 +612,7 @@ static int LoadFile(const char *path) { } if (yn > byn || xn > bxn) goto ReadError; xchg(&board, &board2); - memset(board, 0, (byn * bxn) >> 3); + bzero(board, (byn * bxn) >> 3); yo = byn / 2 - yn / 2; xo = bxn / 2 - xn / 2; y = 0; @@ -857,7 +857,7 @@ static void Rando2(void) { static void ReadKeyboard(void) { char buf[32], *p = buf; - memset(buf, 0, sizeof(buf)); + bzero(buf, sizeof(buf)); if (readansi(0, buf, sizeof(buf)) == -1) { if (errno == EINTR) return; exit(errno); @@ -895,7 +895,7 @@ static void ReadKeyboard(void) { } break; case 'R': - memset(board, 0, (byn * bxn) >> 3); + bzero(board, (byn * bxn) >> 3); break; case CTRL('T'): OnTurbo(); @@ -1166,7 +1166,7 @@ static void OnMenuOpen(int64_t hwnd) { char buf8[PATH_MAX]; char16_t buf16[PATH_MAX]; struct NtOpenFilename ofn; - memset(&ofn, 0, sizeof(ofn)); + bzero(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hwnd; ofn.lpstrFile = buf16; @@ -1370,7 +1370,7 @@ static void Gui(void) { int64_t hwnd, mh; struct NtMsg msg; struct NtWndClass wc; - memset(&wc, 0, sizeof(wc)); + bzero(&wc, sizeof(wc)); wc.lpfnWndProc = NT2SYSV(WindowProc); wc.hInstance = GetModuleHandle(NULL); wc.hCursor = LoadCursor(0, kNtIdcCross); diff --git a/tool/viz/magikarp.c b/tool/viz/magikarp.c index 3cba134aa..920490d10 100644 --- a/tool/viz/magikarp.c +++ b/tool/viz/magikarp.c @@ -192,7 +192,7 @@ static void *MagikarpY(CHAR w, unsigned char p[1u << w][1u << w], char yw, long y, x, yn, xn, ym; unsigned char(*t)[(1u << w) + 2][1u << w]; t = memalign(64, ((1u << w) + 2) * (1u << w)); - memset(t, 0, ((1u << w) + 2) * (1u << w)); + bzero(t, ((1u << w) + 2) * (1u << w)); yn = 1u << yw; xn = 1u << xw; ym = yn >> 1; diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index 4f16e59d9..8793f8776 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -496,7 +496,7 @@ static void OnMouse(char *p) { static void ReadKeyboard(void) { char buf[32], *p = buf; - memset(buf, 0, sizeof(buf)); + bzero(buf, sizeof(buf)); if (readansi(0, buf, sizeof(buf)) == -1) { if (errno == EINTR) return; exit(errno); @@ -797,7 +797,7 @@ static void Zoom(long have) { n >>= 1; } if (n < tyn * txn) { - memset(canvas + n, 0, canvassize - n); + bzero(canvas + n, canvassize - n); } if (have != -1) { n = have >> zoom; @@ -817,7 +817,7 @@ static void FileZoom(void) { have = MIN(displaysize, size - offset); have = pread(fd, canvas, have, offset); have = MAX(0, have); - memset(canvas + have, 0, canvassize - have); + bzero(canvas + have, canvassize - have); Zoom(have); Render(); } diff --git a/tool/viz/printdos2errno.c b/tool/viz/printdos2errno.c index 498c89862..87f88fccf 100644 --- a/tool/viz/printdos2errno.c +++ b/tool/viz/printdos2errno.c @@ -26,11 +26,11 @@ int main(int argc, char *argv[]) { int i; for (i = 0; kDos2Errno[i].doscode; ++i) { - kprintf("dos error %10hu maps to rva %10d errno %10d which is %s%n", - kDos2Errno[i].doscode, kDos2Errno[i].systemv, - *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv), - strerror_short( - *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv))); + kprintf( + "dos error %10hu maps to rva %10d errno %10d which is %s%n", + kDos2Errno[i].doscode, kDos2Errno[i].systemv, + *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv), + strerrno(*(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv))); } return 0; } diff --git a/tool/viz/viz.mk b/tool/viz/viz.mk index 7dfba9880..744f644f8 100644 --- a/tool/viz/viz.mk +++ b/tool/viz/viz.mk @@ -88,7 +88,7 @@ o/$(MODE)/tool/viz/printimage.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/viz/.printimage/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/viz/.printimage/.symtab o/$(MODE)/tool/viz/printvideo.com: \ @@ -98,7 +98,7 @@ o/$(MODE)/tool/viz/printvideo.com: \ @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ @$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \ -o o/$(MODE)/tool/viz/.printvideo/.symtab $< - @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ + @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \ o/$(MODE)/tool/viz/.printvideo/.symtab o/$(MODE)/tool/viz/derasterize.o: \ diff --git a/usr/share/zoneinfo/Anchorage b/usr/share/zoneinfo/Anchorage new file mode 100644 index 000000000..cdf0572be Binary files /dev/null and b/usr/share/zoneinfo/Anchorage differ diff --git a/usr/share/zoneinfo/GMT b/usr/share/zoneinfo/GMT new file mode 100644 index 000000000..c63474664 Binary files /dev/null and b/usr/share/zoneinfo/GMT differ diff --git a/usr/share/zoneinfo/India b/usr/share/zoneinfo/India new file mode 100644 index 000000000..0014046d2 Binary files /dev/null and b/usr/share/zoneinfo/India differ