diff --git a/examples/getcpucount.c b/examples/getcpucount.c index 4ed429ad7..ec38eb478 100644 --- a/examples/getcpucount.c +++ b/examples/getcpucount.c @@ -14,11 +14,11 @@ int main(int argc, char *argv[]) { int c, n; - char a[22]; + char a[22], *p; if ((c = GetCpuCount())) { - n = int64toarray_radix10(c, a); - a[n++] = '\n'; - return write(1, a, n) == n ? 0 : 1; + p = FormatInt64(a, c); + *p++ = '\n'; + return write(1, a, p - a) == p - a ? 0 : 1; } else { return 1; } diff --git a/examples/printprimes.c b/examples/printprimes.c index 5624a98bf..73c7ff947 100644 --- a/examples/printprimes.c +++ b/examples/printprimes.c @@ -27,7 +27,7 @@ int main(int argc, char *argv[]) { } } if (isprime) { - int64toarray_radix10(i, buf); + FormatInt64(buf, i); fputs(buf, stdout); fputc('\n', stdout); if (k++ % 100 == 0) { diff --git a/examples/time.c b/examples/time.c index a224bd4ff..2213ba405 100644 --- a/examples/time.c +++ b/examples/time.c @@ -47,8 +47,8 @@ void PrintMetric(const char *name, long double d) { mils = fmodl(d * 1000, 1000); mics = fmodl(d * 1000000, 1000); p = stpcpy(p, name), *p++ = '\t'; - p += int64toarray_radix10(mins, p), *p++ = 'm'; - p += int64toarray_radix10(secs, p), *p++ = '.'; + p = FormatInt64(p, mins), *p++ = 'm'; + p = FormatInt64(p, secs), *p++ = '.'; *p++ = '0' + mils / 100; *p++ = '0' + mils / 10 % 10; *p++ = '0' + mils % 10; diff --git a/libc/calls/commandv.c b/libc/calls/commandv.c index 2cfb9eeb5..fad7460f3 100644 --- a/libc/calls/commandv.c +++ b/libc/calls/commandv.c @@ -28,31 +28,30 @@ #include "libc/sysv/consts/ok.h" #include "libc/sysv/errfuns.h" -static noasan bool IsExePath(const char *s, size_t n) { +static bool IsExePath(const char *s, size_t n) { return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".exe") || READ32LE(s + n - 4) == READ32LE(".EXE")); } -static noasan bool IsComPath(const char *s, size_t n) { +static bool IsComPath(const char *s, size_t n) { return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".com") || READ32LE(s + n - 4) == READ32LE(".COM")); } -static noasan bool IsComDbgPath(const char *s, size_t n) { +static bool IsComDbgPath(const char *s, size_t n) { return n >= 8 && (READ64LE(s + n - 8) == READ64LE(".com.dbg") || READ64LE(s + n - 8) == READ64LE(".COM.DBG")); } -static noasan bool AccessCommand(const char *name, - char path[hasatleast PATH_MAX], size_t namelen, - int *err, const char *suffix, size_t pathlen) { +static bool AccessCommand(const char *name, char path[hasatleast PATH_MAX], + 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 && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) { - path[pathlen] = !IsWindows() ? '/' - : memchr(path, '\\', pathlen) ? '\\' - : '/'; + path[pathlen] = + !IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/'; pathlen++; } memcpy(path + pathlen, name, namelen); @@ -62,8 +61,8 @@ static noasan bool AccessCommand(const char *name, return false; } -static noasan bool SearchPath(const char *name, char path[hasatleast PATH_MAX], - size_t namelen, int *err, const char *suffix) { +static bool SearchPath(const char *name, char path[hasatleast PATH_MAX], + size_t namelen, int *err, const char *suffix) { char sep; size_t i; const char *p; @@ -87,10 +86,9 @@ static noasan bool SearchPath(const char *name, char path[hasatleast PATH_MAX], return false; } -static noasan bool FindCommand(const char *name, - char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, - const char *suffix, int *err) { +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; @@ -107,15 +105,13 @@ static noasan bool FindCommand(const char *name, SearchPath(name, pathbuf, namelen, err, suffix); } -static noasan bool FindVerbatim(const char *name, - char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, int *err) { +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 noasan bool FindSuffixed(const char *name, - char pathbuf[hasatleast PATH_MAX], - size_t namelen, bool priorityonly, int *err) { +static bool FindSuffixed(const char *name, char pathbuf[hasatleast PATH_MAX], + size_t namelen, bool priorityonly, int *err) { return !IsExePath(name, namelen) && !IsComPath(name, namelen) && !IsComDbgPath(name, namelen) && (FindCommand(name, pathbuf, namelen, priorityonly, ".com", err) || @@ -131,7 +127,7 @@ static noasan bool FindSuffixed(const char *name, * @asyncsignalsafe * @vforksafe */ -noasan char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { +char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { int e, f; char *res; size_t namelen; diff --git a/libc/calls/ioctl_tcgets.c b/libc/calls/ioctl_tcgets.c index 5e9a7aa83..14860ee24 100644 --- a/libc/calls/ioctl_tcgets.c +++ b/libc/calls/ioctl_tcgets.c @@ -73,7 +73,7 @@ int ioctl_tcgets(int fd, ...) { tio = va_arg(va, struct termios *); va_end(va); if (fd >= 0) { - if (!tio) { + if (!tio || (IsAsan() && !__asan_is_valid(tio, sizeof(*tio)))) { rc = efault(); } else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { rc = enotty(); diff --git a/libc/calls/ioctl_tcsets.c b/libc/calls/ioctl_tcsets.c index 9ec4f832e..b7f8a4155 100644 --- a/libc/calls/ioctl_tcsets.c +++ b/libc/calls/ioctl_tcsets.c @@ -68,7 +68,7 @@ int ioctl_tcsets(int fd, uint64_t request, ...) { va_start(va, request); tio = va_arg(va, const struct termios *); va_end(va); - if (!tio) { + if (!tio || (IsAsan() && !__asan_is_valid(tio, sizeof(*tio)))) { rc = efault(); } else if (fd >= 0) { if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { diff --git a/libc/calls/ptsname_r.c b/libc/calls/ptsname_r.c index 3a85a84f1..49fefdb0a 100644 --- a/libc/calls/ptsname_r.c +++ b/libc/calls/ptsname_r.c @@ -29,7 +29,7 @@ errno_t ptsname_r(int fd, char *buf, size_t size) { if (size) { if (!buf) return einval(); if (ioctl(fd, TIOCGPTN, &pty) == -1) return errno; - int64toarray_radix10(pty, stpcpy(tb, "/dev/pts/")); + FormatInt32(stpcpy(tb, "/dev/pts/"), pty); if (strlen(tb) + 1 >= size) return (errno = ERANGE); stpcpy(buf, tb); /* TODO(jart): OpenBSD OMG */ diff --git a/libc/calls/strace.internal.h b/libc/calls/strace.internal.h index 14479d578..aea83829a 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 0 /* not configurable w/ flag yet */ -#define _POLLTRACE 0 /* not configurable w/ flag yet */ +#define _KERNTRACE 1 /* not configurable w/ flag yet */ +#define _POLLTRACE 1 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ -#define _NTTRACE 0 /* not configurable w/ flag yet */ +#define _NTTRACE 1 /* not configurable w/ flag yet */ #define STRACE_PROLOGUE "%rSYS %5P %'18T " diff --git a/libc/calls/ttyname_r.c b/libc/calls/ttyname_r.c index 8c2d3a41f..fcef19b89 100644 --- a/libc/calls/ttyname_r.c +++ b/libc/calls/ttyname_r.c @@ -64,7 +64,7 @@ static int ttyname_linux(int fd, char *buf, size_t size) { struct stat st1, st2; if (!isatty(fd)) return errno; char name[PATH_MAX]; - int64toarray_radix10(fd, stpcpy(name, "/proc/self/fd/")); + FormatInt32(stpcpy(name, "/proc/self/fd/"), fd); ssize_t got; got = readlink(name, buf, size); if (got == -1) return errno; diff --git a/libc/dns/getnameinfo.c b/libc/dns/getnameinfo.c index 0966a676b..c0be7d773 100644 --- a/libc/dns/getnameinfo.c +++ b/libc/dns/getnameinfo.c @@ -73,10 +73,10 @@ int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *name, ip = (uint8_t *)&(((struct sockaddr_in *)addr)->sin_addr); p = rdomain; - p += int64toarray_radix10(ip[3], p), *p++ = '.'; - p += int64toarray_radix10(ip[2], p), *p++ = '.'; - p += int64toarray_radix10(ip[1], p), *p++ = '.'; - p += int64toarray_radix10(ip[0], p), stpcpy(p, ".in-addr.arpa"); + p = FormatUint32(p, ip[3]), *p++ = '.'; + p = FormatUint32(p, ip[2]), *p++ = '.'; + p = FormatUint32(p, ip[1]), *p++ = '.'; + p = FormatUint32(p, ip[0]), stpcpy(p, ".in-addr.arpa"); info[0] = '\0'; if (name != NULL && namelen != 0) { if ((flags & NI_NUMERICHOST) && (flags & NI_NAMEREQD)) return EAI_NONAME; diff --git a/libc/fmt/fmt.c b/libc/fmt/fmt.c index 2964eda62..488a10cfb 100644 --- a/libc/fmt/fmt.c +++ b/libc/fmt/fmt.c @@ -147,12 +147,12 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { continue; } else if (format[1] == 'd') { /* FAST PATH: PLAIN INTEGER */ d = va_arg(va, int); - if (out(ibuf, arg, int64toarray_radix10(d, ibuf)) == -1) return -1; + if (out(ibuf, arg, FormatInt64(ibuf, d) - ibuf) == -1) return -1; format += 2; continue; } else if (format[1] == 'u') { /* FAST PATH: PLAIN UNSIGNED */ u = va_arg(va, unsigned); - if (out(ibuf, arg, uint64toarray_radix10(u, ibuf)) == -1) return -1; + if (out(ibuf, arg, FormatUint64(ibuf, u) - ibuf) == -1) return -1; format += 2; continue; } else if (format[1] == 'x') { /* FAST PATH: PLAIN HEX */ @@ -167,12 +167,12 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { continue; } else if (format[1] == 'l' && format[2] == 'd') { ld = va_arg(va, long); /* FAST PATH: PLAIN LONG */ - if (out(ibuf, arg, int64toarray_radix10(ld, ibuf)) == -1) return -1; + if (out(ibuf, arg, FormatInt64(ibuf, ld) - ibuf) == -1) return -1; format += 3; continue; } else if (format[1] == 'l' && format[2] == 'u') { lu = va_arg(va, unsigned long); /* FAST PATH: PLAIN UNSIGNED LONG */ - if (out(ibuf, arg, uint64toarray_radix10(lu, ibuf)) == -1) return -1; + if (out(ibuf, arg, FormatUint64(ibuf, lu) - ibuf) == -1) return -1; format += 3; continue; } else if (format[1] == '.' && format[2] == '*' && format[3] == 's') { diff --git a/libc/fmt/formatint32.c b/libc/fmt/formatint32.c index 4637eb710..9afc72f50 100644 --- a/libc/fmt/formatint32.c +++ b/libc/fmt/formatint32.c @@ -24,7 +24,7 @@ * @param p needs at least 12 bytes * @return pointer to nul byte */ -dontinline char *FormatUint32(char p[static 12], uint32_t x) { +dontinline char *FormatUint32(char p[hasatleast 12], uint32_t x) { char t; size_t i, a, b; i = 0; @@ -49,7 +49,7 @@ dontinline char *FormatUint32(char p[static 12], uint32_t x) { * @param p needs at least 12 bytes * @return pointer to nul byte */ -char *FormatInt32(char p[static 12], int32_t x) { +char *FormatInt32(char p[hasatleast 12], int32_t x) { if (x < 0) *p++ = '-', x = -(uint32_t)x; return FormatUint32(p, x); } diff --git a/libc/fmt/itoa64radix10.greg.c b/libc/fmt/formatoctal32.c similarity index 70% rename from libc/fmt/itoa64radix10.greg.c rename to libc/fmt/formatoctal32.c index 25d403a8c..18f295e20 100644 --- a/libc/fmt/itoa64radix10.greg.c +++ b/libc/fmt/formatoctal32.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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,34 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/reverse.internal.h" -#include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" -#include "libc/limits.h" /** - * Converts unsigned 64-bit integer to string. - * @param a needs at least 21 bytes - * @return bytes written w/o nul + * Converts unsigned 32-bit integer to octal string. + * + * @param p needs at least 12 bytes + * @param z ensures it starts with zero + * @return pointer to nul byte */ -dontinline size_t uint64toarray_radix10(uint64_t i, char a[hasatleast 21]) { - size_t j = 0; +char *FormatOctal32(char p[hasatleast 13], uint32_t x, bool z) { + char t; + size_t i, a, b; + i = 0; + z = x && z; do { - a[j++] = i % 10 + '0'; - i = i / 10; - } while (i > 0); - a[j] = '\0'; - reverse(a, j); - return j; -} - -/** - * Converts signed 64-bit integer to string. - * @param a needs at least 21 bytes - * @return bytes written w/o nul - */ -size_t int64toarray_radix10(int64_t i, char a[hasatleast 21]) { - if (i >= 0) return uint64toarray_radix10(i, a); - *a++ = '-'; - return 1 + uint64toarray_radix10(-(uint64_t)i, a); + p[i++] = x % 8 + '0'; + x = x / 8; + } while (x > 0); + if (z) p[i++] = '0'; + p[i] = '\0'; + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; } diff --git a/libc/fmt/formatoctal64.c b/libc/fmt/formatoctal64.c new file mode 100644 index 000000000..c500467ab --- /dev/null +++ b/libc/fmt/formatoctal64.c @@ -0,0 +1,47 @@ +/*-*- 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" + +/** + * Converts unsigned 64-bit integer to octal string. + * + * @param p needs at least 12 bytes + * @param z ensures it starts with zero + * @return pointer to nul byte + */ +char *FormatOctal64(char p[hasatleast 24], uint64_t x, bool z) { + char t; + size_t i, a, b; + i = 0; + z = x && z; + do { + p[i++] = x % 8 + '0'; + x = x / 8; + } while (x > 0); + if (z) p[i++] = '0'; + p[i] = '\0'; + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; +} diff --git a/libc/fmt/itoa.h b/libc/fmt/itoa.h index 5cfbc1466..f123dc271 100644 --- a/libc/fmt/itoa.h +++ b/libc/fmt/itoa.h @@ -2,19 +2,6 @@ #define COSMOPOLITAN_LIBC_FMT_ITOA_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § integer conversion ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│─╝ - FASTEST + TINY - - - uint64toarray_radix10(0x31337, a) l: 68 (20ns) m: 112 (33ns) - - int64toarray_radix10(0x31337, a) l: 69 (20ns) m: 134 (39ns) - - FAST + AWESOME - - - snprintf(a, sizeof(a), "%d", 0x31337) l: 199 (58ns) m: 421 (123ns) - - uint128toarray_radix10(0x31337, a) l: 93 (27ns) m: 141 (41ns) - - int128toarray_radix10(0x31337, a) l: 96 (28ns) m: 173 (51ns) */ unsigned LengthInt64(int64_t) pureconst; unsigned LengthUint64(uint64_t) pureconst; @@ -26,8 +13,8 @@ char *FormatInt64(char[hasatleast 21], int64_t); char *FormatUint64(char[hasatleast 21], uint64_t); char *FormatInt64Thousands(char[hasatleast 27], int64_t); char *FormatUint64Thousands(char[hasatleast 27], uint64_t); -size_t int64toarray_radix10(int64_t, char[hasatleast 21]); -size_t uint64toarray_radix10(uint64_t, char[hasatleast 21]); +char *FormatOctal32(char[hasatleast 13], uint32_t, bool); +char *FormatOctal64(char[hasatleast 24], uint64_t, bool); 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/intrin/asan.c b/libc/intrin/asan.c index 78dd24dd2..aa5a86468 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -722,7 +722,7 @@ static void __asan_report_memory_origin(const unsigned char *addr, int size, if (_base <= addr && addr < _end) { __asan_report_memory_origin_image((intptr_t)addr, size); } else if (IsAutoFrame((intptr_t)addr >> 16)) { - __asan_report_memory_origin_heap(addr, size); + /* __asan_report_memory_origin_heap(addr, size); */ } } @@ -1202,8 +1202,9 @@ void __asan_unregister_globals(struct AsanGlobal g[], int n) { 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 %p bt %p %p %p %p%n", - s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3]); + kprintf( + "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]); } void __asan_report_load(uint8_t *addr, int size) { diff --git a/libc/intrin/bzero.c b/libc/intrin/bzero.c index cb5e0c48d..c321dc59b 100644 --- a/libc/intrin/bzero.c +++ b/libc/intrin/bzero.c @@ -149,7 +149,7 @@ void(bzero)(void *p, size_t n) { } while (n); } } else if (IsTiny()) { - asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "0"(p), "a"(0)); + asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "a"(0)); return; } else if (X86_HAVE(AVX)) { bzero_avx(b, n); diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index b8d9ddd76..77acd0597 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -116,7 +116,7 @@ o//libc/intrin/memcmp.o \ o//libc/intrin/memset.o \ o//libc/intrin/memmove.o: \ OVERRIDE_CFLAGS += \ - -O2 + -O2 -finline o/$(MODE)/libc/intrin/bzero.o \ o/$(MODE)/libc/intrin/memcmp.o \ diff --git a/libc/log/attachdebugger.c b/libc/log/attachdebugger.c index 6617fb71a..7d5208182 100644 --- a/libc/log/attachdebugger.c +++ b/libc/log/attachdebugger.c @@ -56,11 +56,11 @@ relegated int(AttachDebugger)(intptr_t continuetoaddr) { struct StackFrame *bp; char pidstr[11], breakcmd[40]; const char *se, *elf, *gdb, *rewind, *layout; + __restore_tty(); if (IsGenuineCosmo() || !(gdb = GetGdbPath()) || !isatty(0) || !isatty(1) || (ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC)) == -1) { return -1; } - __restore_tty(ttyfd); ksnprintf(pidstr, sizeof(pidstr), "%u", getpid()); layout = "layout asm"; if ((elf = FindDebugBinary())) { diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.greg.c similarity index 95% rename from libc/log/backtrace2.c rename to libc/log/backtrace2.greg.c index 5cdcd7f91..642625fe1 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.greg.c @@ -59,11 +59,12 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { size_t i, j, gi; int ws, pid, pipefds[2]; struct Garbages *garbage; - sigset_t chldmask, savemask; const struct StackFrame *frame; char *debugbin, *p1, *p2, *p3, *addr2line; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; + return -1; + if (!(debugbin = FindDebugBinary())) { return -1; } @@ -105,19 +106,15 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { argv[i++] = buf + j; buf[j++] = '0'; buf[j++] = 'x'; - j += uint64toarray_radix16(addr, buf + j) + 1; + j += uint64toarray_radix16(addr - 1, buf + j) + 1; } argv[i++] = NULL; - sigemptyset(&chldmask); - sigaddset(&chldmask, SIGCHLD); - sigprocmask(SIG_BLOCK, &chldmask, &savemask); pipe(pipefds); if (!(pid = vfork())) { - sigprocmask(SIG_SETMASK, &savemask, NULL); dup2(pipefds[1], 1); if (pipefds[0] != 1) close(pipefds[0]); if (pipefds[1] != 1) close(pipefds[1]); - execvp(addr2line, argv); + execve(addr2line, argv, environ); _exit(127); } close(pipefds[1]); @@ -150,7 +147,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { if (errno == EINTR) continue; return -1; } - sigprocmask(SIG_SETMASK, &savemask, NULL); if (WIFEXITED(ws) && !WEXITSTATUS(ws)) { return 0; } else { diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.greg.c similarity index 100% rename from libc/log/backtrace3.c rename to libc/log/backtrace3.greg.c diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index e41969b78..ff313a715 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -35,7 +35,7 @@ */ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, const char *opchar) { - __restore_tty(1); + __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)); diff --git a/libc/log/die.c b/libc/log/die.c index 9c2ba9f1e..ac36f77ad 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -35,7 +35,7 @@ relegated wontreturn void __die(void) { int rc; static bool once; if (_lockcmpxchg(&once, false, true)) { - __restore_tty(1); + __restore_tty(); if (IsDebuggerPresent(false)) { DebugBreak(); } diff --git a/libc/log/internal.h b/libc/log/internal.h index e2353016a..14a644ca8 100644 --- a/libc/log/internal.h +++ b/libc/log/internal.h @@ -10,12 +10,11 @@ COSMOPOLITAN_C_START_ extern hidden bool __nocolor; extern hidden int kCrashSigs[7]; extern hidden bool g_isrunningundermake; -extern hidden struct termios g_oldtermios; extern hidden struct sigaction g_oldcrashacts[7]; void __start_fatal(const char *, int) hidden; void __oncrash(int, struct siginfo *, struct ucontext *) relegated; -void __restore_tty(int); +void __restore_tty(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/log.h b/libc/log/log.h index 090a7a52b..3c804d0a1 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -77,15 +77,15 @@ extern unsigned __log_level; /* log level for runtime check */ // log a message with the specified log level (not checking if LOGGABLE) #define LOGF(LEVEL, FMT, ...) \ do { \ - ++g_ftrace; \ - flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } while (0) // die with an error message without backtrace and debugger invocation #define DIEF(FMT, ...) \ do { \ - ++g_ftrace; \ + --g_ftrace; \ flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ exit(1); \ unreachable; \ @@ -93,7 +93,7 @@ extern unsigned __log_level; /* log level for runtime check */ #define FATALF(FMT, ...) \ do { \ - ++g_ftrace; \ + --g_ftrace; \ ffatalf(kLogFatal, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ unreachable; \ } while (0) @@ -101,78 +101,78 @@ extern unsigned __log_level; /* log level for runtime check */ #define ERRORF(FMT, ...) \ do { \ if (LOGGABLE(kLogError)) { \ - ++g_ftrace; \ - flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define WARNF(FMT, ...) \ do { \ if (LOGGABLE(kLogWarn)) { \ - ++g_ftrace; \ - flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define INFOF(FMT, ...) \ do { \ if (LOGGABLE(kLogInfo)) { \ - ++g_ftrace; \ - flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define VERBOSEF(FMT, ...) \ do { \ if (LOGGABLE(kLogVerbose)) { \ - ++g_ftrace; \ - fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define DEBUGF(FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogDebug))) { \ - ++g_ftrace; \ - fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define NOISEF(FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogNoise))) { \ - ++g_ftrace; \ - fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define FLOGF(F, FMT, ...) \ do { \ if (LOGGABLE(kLogInfo)) { \ - ++g_ftrace; \ - flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define FWARNF(F, FMT, ...) \ do { \ if (LOGGABLE(kLogWarn)) { \ - ++g_ftrace; \ - flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define FFATALF(F, FMT, ...) \ do { \ - ++g_ftrace; \ + --g_ftrace; \ ffatalf(kLogFatal, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ unreachable; \ } while (0) @@ -180,18 +180,18 @@ extern unsigned __log_level; /* log level for runtime check */ #define FDEBUGF(F, FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogDebug))) { \ - ++g_ftrace; \ - fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) #define FNOISEF(F, FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogNoise))) { \ - ++g_ftrace; \ - fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ --g_ftrace; \ + fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ + ++g_ftrace; \ } \ } while (0) @@ -204,9 +204,9 @@ extern unsigned __log_level; /* log level for runtime check */ int e = errno; \ autotype(FORM) Ax = (FORM); \ if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \ - ++g_ftrace; \ - __logerrno(__FILE__, __LINE__, #FORM); \ --g_ftrace; \ + __logerrno(__FILE__, __LINE__, #FORM); \ + ++g_ftrace; \ errno = e; \ } \ Ax; \ @@ -217,9 +217,9 @@ extern unsigned __log_level; /* log level for runtime check */ int e = errno; \ autotype(FORM) Ax = (FORM); \ if (Ax == NULL && LOGGABLE(kLogWarn)) { \ - ++g_ftrace; \ - __logerrno(__FILE__, __LINE__, #FORM); \ --g_ftrace; \ + __logerrno(__FILE__, __LINE__, #FORM); \ + ++g_ftrace; \ errno = e; \ } \ Ax; \ diff --git a/libc/log/log.mk b/libc/log/log.mk index 9186ad300..593dd3982 100644 --- a/libc/log/log.mk +++ b/libc/log/log.mk @@ -41,6 +41,7 @@ LIBC_LOG_A_DIRECTDEPS = \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV \ + LIBC_SYSV_CALLS \ LIBC_TIME \ LIBC_TINYMATH \ LIBC_UNICODE \ @@ -58,8 +59,8 @@ $(LIBC_LOG_A).pkg: \ $(LIBC_LOG_A_OBJS) \ $(foreach x,$(LIBC_LOG_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/log/backtrace2.o \ -o/$(MODE)/libc/log/backtrace3.o: \ +o/$(MODE)/libc/log/backtrace2.greg.o \ +o/$(MODE)/libc/log/backtrace3.greg.o: \ OVERRIDE_CFLAGS += \ -fno-sanitize=all @@ -67,6 +68,7 @@ o/$(MODE)/libc/log/checkfail.o: \ OVERRIDE_CFLAGS += \ -mgeneral-regs-only +o/$(MODE)/libc/log/restoretty.greg.o \ o/$(MODE)/libc/log/attachdebugger.o \ o/$(MODE)/libc/log/backtrace2.o \ o/$(MODE)/libc/log/backtrace3.o \ diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 1416bc3ec..3bdfc1fd7 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -296,7 +296,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si, : 0); } if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) { - __restore_tty(1); + __restore_tty(); ShowCrashReport(err, sig, si, ctx); __restorewintty(); _Exit(128 + sig); diff --git a/libc/log/restoretty.c b/libc/log/restoretty.greg.c similarity index 79% rename from libc/log/restoretty.c rename to libc/log/restoretty.greg.c index 02966ef0f..3175a7b3c 100644 --- a/libc/log/restoretty.c +++ b/libc/log/restoretty.greg.c @@ -17,8 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/metatermios.internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/termios.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/log/color.internal.h" #include "libc/log/internal.h" @@ -26,7 +29,7 @@ #include "libc/sysv/consts/termios.h" /** - * @fileoverview Terminal Restoration Helper + * @fileoverview Terminal Restoration Helper for System Five. * * This is used by the crash reporting functions, e.g. __die(), to help * ensure the terminal is in an unborked state after a crash happens. @@ -37,26 +40,28 @@ #define DISABLE_MOUSE "\e[?1000;1002;1015;1006l" #define ANSI_RESTORE RESET_COLOR SHOW_CURSOR DISABLE_MOUSE -struct termios g_oldtermios; +static bool __isrestorable; +static union metatermios __oldtermios; -static textstartup void g_oldtermios_init() { - int e = errno; - tcgetattr(1, &g_oldtermios); +static textstartup void __oldtermios_init() { + int e; + e = errno; + if (sys_ioctl(0, TCGETS, &__oldtermios) != -1) { + __isrestorable = true; + } errno = e; } -const void *const g_oldtermios_ctor[] initarray = { - g_oldtermios_init, +const void *const __oldtermios_ctor[] initarray = { + __oldtermios_init, }; -void __restore_tty(int fd) { +void __restore_tty(void) { int e; - if (!__isworker) { + if (__isrestorable && !__isworker && !__nocolor) { e = errno; - if (g_oldtermios.c_lflag && !__nocolor && isatty(fd)) { - write(fd, ANSI_RESTORE, strlen(ANSI_RESTORE)); - tcsetattr(fd, TCSAFLUSH, &g_oldtermios); - } + sys_write(0, ANSI_RESTORE, strlen(ANSI_RESTORE)); + sys_ioctl(0, TCSETSF, &__oldtermios); errno = e; } } diff --git a/libc/log/startfatal.c b/libc/log/startfatal.c index 5fda7877f..599b1bd22 100644 --- a/libc/log/startfatal.c +++ b/libc/log/startfatal.c @@ -27,7 +27,7 @@ * @note this is support code for __check_fail(), __assert_fail(), etc. */ relegated void __start_fatal(const char *file, int line) { - __restore_tty(1); + __restore_tty(); kprintf("%r%serror%s:%s:%d:%s%s: ", !__nocolor ? "\e[J\e[30;101m" : "", !__nocolor ? "\e[94;49m" : "", file, line, program_invocation_short_name, !__nocolor ? "\e[0m" : ""); diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 0211d37ac..093436195 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -249,7 +249,7 @@ textwindows int sys_fork_nt(void) { kNtFileFlagOverlapped, 0); if (pid != -1 && reader != -1 && writer != -1) { p = stpcpy(forkvar, "_FORK="); - p += uint64toarray_radix10(reader, p); + p = FormatUint64(p, reader); bzero(&startinfo, sizeof(startinfo)); startinfo.cb = sizeof(struct NtStartupInfo); startinfo.dwFlags = kNtStartfUsestdhandles; diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 9060b36ba..6f79f9b15 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -31,6 +31,7 @@ #include "libc/stdio/stdio.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" +#include "libc/time/clockstonanos.internal.h" #pragma weak stderr @@ -89,7 +90,7 @@ privileged noinstrument noasan noubsan void ftracer(void) { frame = frame->next; if (frame->addr != g_lastaddr) { kprintf("+ %*s%t %d\r\n", GetNestingLevel(frame) * 2, "", frame->addr, - (long)(unsignedsubtract(stamp, g_laststamp) / 3.3)); + ClocksToNanos(stamp, g_laststamp)); g_laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); g_lastaddr = frame->addr; } @@ -97,14 +98,15 @@ privileged noinstrument noasan noubsan void ftracer(void) { noreentry = 0; } -textstartup void ftrace_install(void) { +textstartup int ftrace_install(void) { if (GetSymbolTable()) { g_lastaddr = -1; g_laststamp = kStartTsc; g_skew = GetNestingLevelImpl(__builtin_frame_address(0)); ftrace_enabled = 1; - __hook(ftrace_hook, GetSymbolTable()); + return __hook(ftrace_hook, GetSymbolTable()); } else { kprintf("error: --ftrace failed to open symbol table\r\n"); + return -1; } } diff --git a/libc/runtime/ismemtracked.greg.c b/libc/runtime/ismemtracked.greg.c new file mode 100644 index 000000000..ba0b7b2ee --- /dev/null +++ b/libc/runtime/ismemtracked.greg.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/runtime/memtrack.internal.h" + +bool IsMemtracked(int x, int y) { + unsigned i; + i = FindMemoryInterval(&_mmi, x); + if (i == _mmi.i) return false; + if (x < _mmi.p[i].x) return false; + for (;;) { + if (y <= _mmi.p[i].y) return true; + if (++i == _mmi.i) return false; + if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false; + } +} diff --git a/libc/runtime/memtrack.greg.c b/libc/runtime/memtrack.greg.c index b59e8b64e..2d69982d6 100644 --- a/libc/runtime/memtrack.greg.c +++ b/libc/runtime/memtrack.greg.c @@ -36,8 +36,8 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" -static noasan void *MoveMemoryIntervals(struct MemoryInterval *d, - const struct MemoryInterval *s, int n) { +static void *MoveMemoryIntervals(struct MemoryInterval *d, + const struct MemoryInterval *s, int n) { /* asan runtime depends on this function */ int i; assert(n >= 0); @@ -53,8 +53,7 @@ static noasan void *MoveMemoryIntervals(struct MemoryInterval *d, return d; } -static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, - int n) { +static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) { /* asan runtime depends on this function */ assert(i >= 0); assert(i + n <= mm->i); @@ -62,7 +61,7 @@ static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, mm->i -= n; } -static noasan bool ExtendMemoryIntervals(struct MemoryIntervals *mm) { +static bool ExtendMemoryIntervals(struct MemoryIntervals *mm) { int prot, flags; char *base, *shad; size_t gran, size; @@ -99,7 +98,7 @@ static noasan bool ExtendMemoryIntervals(struct MemoryIntervals *mm) { return true; } -noasan int CreateMemoryInterval(struct MemoryIntervals *mm, int i) { +int CreateMemoryInterval(struct MemoryIntervals *mm, int i) { /* asan runtime depends on this function */ int rc; rc = 0; @@ -111,15 +110,15 @@ noasan int CreateMemoryInterval(struct MemoryIntervals *mm, int i) { return 0; } -static noasan int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) { +static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) { if (CreateMemoryInterval(mm, i) == -1) return -1; mm->p[i].y = x - 1; mm->p[i + 1].x = y + 1; return 0; } -noasan int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, - void wf(struct MemoryIntervals *, int, int)) { +int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, + void wf(struct MemoryIntervals *, int, int)) { unsigned l, r; assert(y >= x); assert(AreMemoryIntervalsOk(mm)); @@ -158,9 +157,9 @@ noasan int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, return 0; } -noasan int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, - int prot, int flags, bool readonlyfile, - bool iscow, long offset, long size) { +int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, + int prot, int flags, bool readonlyfile, bool iscow, + long offset, long size) { /* asan runtime depends on this function */ unsigned i; assert(y >= x); diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 233a2926c..b6ee29e36 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -50,8 +50,9 @@ struct MemoryIntervals { extern hidden struct MemoryIntervals _mmi; -const char *DescribeFrame(int); +bool IsMemtracked(int, int) hidden; void PrintSystemMappings(int) hidden; +const char *DescribeFrame(int) hidden; char *DescribeMapping(int, int, char[hasatleast 8]) hidden; bool AreMemoryIntervalsOk(const struct MemoryIntervals *) nosideeffect hidden; void PrintMemoryIntervals(int, const struct MemoryIntervals *) hidden; @@ -175,18 +176,6 @@ forceinline unsigned FindMemoryInterval(const struct MemoryIntervals *mm, return l; } -forceinline bool IsMemtracked(int x, int y) { - unsigned i; - i = FindMemoryInterval(&_mmi, x); - if (i == _mmi.i) return false; - if (x < _mmi.p[i].x) return false; - for (;;) { - if (y <= _mmi.p[i].y) return true; - if (++i == _mmi.i) return false; - if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false; - } -} - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ */ diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index c6a4273ce..10e438f7a 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -203,9 +203,8 @@ 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" : "", @@ -245,8 +244,8 @@ textstartup void __printargs(const char *prologue) { if ((n = poll(pfds, ARRAYLEN(pfds), 0)) != -1) { for (i = 0; i < ARRAYLEN(pfds); ++i) { if (i && (pfds[i].revents & POLLNVAL)) continue; - PRINT(" ☼ %d (revents=%#hx F_GETFL=%#x)", i, pfds[i].revents, - fcntl(i, F_GETFL)); + PRINT(" ☼ %d (revents=%#hx fcntl(F_GETFL)=%#x isatty()=%hhhd)", i, + pfds[i].revents, fcntl(i, F_GETFL), isatty(i)); } } else { PRINT(" poll() returned %d %m", n); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 9181de853..0604d98dd 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -98,7 +98,7 @@ void _weakfree(void *); void free_s(void *) paramsnonnull() libcesque; int close_s(int *) paramsnonnull() libcesque; int OpenExecutable(void); -void ftrace_install(void); +int ftrace_install(void); long GetResourceLimit(int); long GetMaxFd(void); char *GetProgramExecutableName(void); diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index f8e42ef33..db205c38b 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -70,8 +70,9 @@ o/$(MODE)/libc/runtime/getdosargv.o \ o/$(MODE)/libc/runtime/getdosenviron.o \ o/$(MODE)/libc/runtime/hook.greg.o \ o/$(MODE)/libc/runtime/isheap.o \ -o/$(MODE)/libc/runtime/memtrack.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/sock/inet_ntoa.c b/libc/sock/inet_ntoa.c index cd6c0d8b1..648815920 100644 --- a/libc/sock/inet_ntoa.c +++ b/libc/sock/inet_ntoa.c @@ -25,12 +25,12 @@ char *inet_ntoa(struct in_addr in) { static char buf[16]; char *p = buf; - p += int64toarray_radix10((in.s_addr >> 000) & 255, p); + p = FormatUint32(p, (in.s_addr >> 000) & 255); *p++ = '.'; - p += int64toarray_radix10((in.s_addr >> 010) & 255, p); + p = FormatUint32(p, (in.s_addr >> 010) & 255); *p++ = '.'; - p += int64toarray_radix10((in.s_addr >> 020) & 255, p); + p = FormatUint32(p, (in.s_addr >> 020) & 255); *p++ = '.'; - p += int64toarray_radix10((in.s_addr >> 030) & 255, p); + p = FormatUint32(p, (in.s_addr >> 030) & 255); return buf; } diff --git a/libc/testlib/quota.c b/libc/testlib/quota.c index 7bfbf6243..815e7e994 100644 --- a/libc/testlib/quota.c +++ b/libc/testlib/quota.c @@ -53,19 +53,19 @@ static relegated void DieBecauseOfQuota(int rc, const char *message) { } static relegated void OnXcpu(int sig) { - __restore_tty(2); + __restore_tty(); DieBecauseOfQuota(23, "\n\nSIGXCPU: ran out of cpu"); } static relegated void OnXfsz(int sig) { - __restore_tty(2); + __restore_tty(); DieBecauseOfQuota(25, "\n\nSIGXFSZ: exceeded maximum file size"); } relegated void __oom_hook(size_t request) { int e; uint64_t toto, newlim; - __restore_tty(2); + __restore_tty(); e = errno; toto = CountMappedBytes(); kprintf("\n\nWE REQUIRE MORE VESPENE GAS"); diff --git a/libc/time/strftime.c b/libc/time/strftime.c index 70082babd..f49462291 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -48,7 +48,7 @@ static char *strftime_secs(char *p, const char *pe, const struct tm *t) { int64_t s; tmp = *t; /* Make a copy, mktime(3) modifies the tm struct. */ s = mktime(&tmp); - int64toarray_radix10(s, ibuf); + FormatInt64(ibuf, s); return strftime_add(p, pe, ibuf); } @@ -329,13 +329,13 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format, if (t->tm_isdst == 0) #ifdef USG_COMPAT diff = -timezone; -#else /* !defined USG_COMPAT */ +#else /* !defined USG_COMPAT */ continue; #endif /* !defined USG_COMPAT */ else #ifdef ALTZONE diff = -altzone; -#else /* !defined ALTZONE */ +#else /* !defined ALTZONE */ continue; #endif /* !defined ALTZONE */ #endif /* !defined TM_GMTOFF */ diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index 504123a64..ddfbae830 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -46,6 +46,13 @@ TEST(open, eexist) { ASSERT_SYS(EEXIST, -1, open("exists", O_WRONLY | O_CREAT | O_EXCL)); } +TEST(open, enametoolong) { + size_t n = 260; + char *s = gc(xcalloc(1, n + 1)); + memset(s, 'J', n); + ASSERT_SYS(ENAMETOOLONG, -1, creat(s, 0644)); +} + TEST(open, testOpenExistingForWriteOnly_seeksToStart) { char buf[8] = {0}; ASSERT_SYS(0, 0, xbarf("hello.txt", "hello", -1)); diff --git a/test/libc/fmt/itoa64radix10_test.c b/test/libc/fmt/formatint64_test.c similarity index 83% rename from test/libc/fmt/itoa64radix10_test.c rename to test/libc/fmt/formatint64_test.c index c9387aa0f..533789f54 100644 --- a/test/libc/fmt/itoa64radix10_test.c +++ b/test/libc/fmt/formatint64_test.c @@ -22,29 +22,29 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" -TEST(int64toarray_radix10, test) { +TEST(FormatInt64, test) { char buf[21]; - EXPECT_EQ(1, int64toarray_radix10(0, buf)); + EXPECT_EQ(1, FormatInt64(buf, 0) - buf); EXPECT_STREQ("0", buf); - EXPECT_EQ(1, int64toarray_radix10(1, buf)); + EXPECT_EQ(1, FormatInt64(buf, 1) - buf); EXPECT_STREQ("1", buf); - EXPECT_EQ(2, int64toarray_radix10(-1, buf)); + EXPECT_EQ(2, FormatInt64(buf, -1) - buf); EXPECT_STREQ("-1", buf); - EXPECT_EQ(19, int64toarray_radix10(INT64_MAX, buf)); + EXPECT_EQ(19, FormatInt64(buf, INT64_MAX) - buf); EXPECT_STREQ("9223372036854775807", buf); - EXPECT_EQ(20, int64toarray_radix10(INT64_MIN, buf)); + EXPECT_EQ(20, FormatInt64(buf, INT64_MIN) - buf); EXPECT_STREQ("-9223372036854775808", buf); } -TEST(uint64toarray_radix10, test) { +TEST(FormatUint64, test) { char buf[21]; - EXPECT_EQ(1, uint64toarray_radix10(0, buf)); + EXPECT_EQ(1, FormatUint64(buf, 0) - buf); EXPECT_STREQ("0", buf); - EXPECT_EQ(4, uint64toarray_radix10(1024, buf)); + EXPECT_EQ(4, FormatUint64(buf, 1024) - buf); EXPECT_STREQ("1024", buf); - EXPECT_EQ(20, uint64toarray_radix10(UINT64_MAX, buf)); + EXPECT_EQ(20, FormatUint64(buf, UINT64_MAX) - buf); EXPECT_STREQ("18446744073709551615", buf); - EXPECT_EQ(19, uint64toarray_radix10(INT64_MIN, buf)); + EXPECT_EQ(19, FormatUint64(buf, INT64_MIN) - buf); EXPECT_STREQ("9223372036854775808", buf); } @@ -70,5 +70,5 @@ TEST(uint128toarray_radix10, test) { BENCH(itoa64radix10, bench) { char b[21]; - EZBENCH2("itoa64radix10", donothing, uint64toarray_radix10(UINT64_MAX, b)); + EZBENCH2("itoa64radix10", donothing, FormatUint64(b, UINT64_MAX)); } diff --git a/test/libc/fmt/formatint64thousands_test.c b/test/libc/fmt/formatint64thousands_test.c index c0158d2a8..ae338e41a 100644 --- a/test/libc/fmt/formatint64thousands_test.c +++ b/test/libc/fmt/formatint64thousands_test.c @@ -76,8 +76,6 @@ TEST(FormatInt64Thousands, testNegative) { BENCH(FormatInt64Thousands, bench) { char s[27]; - EZBENCH2("int64toarray_radix10(MAX)", donothing, - int64toarray_radix10(INT64_MAX, s)); EZBENCH2("FormatInt64Thousands(MAX)", donothing, FormatInt64Thousands(s, INT64_MAX)); EZBENCH2("FormatInt64Thousands(MIN)", donothing, diff --git a/test/libc/fmt/formatoctal32_test.c b/test/libc/fmt/formatoctal32_test.c new file mode 100644 index 000000000..ae808806e --- /dev/null +++ b/test/libc/fmt/formatoctal32_test.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/fmt/itoa.h" +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +char buf[13]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(FormatOctal32, test1) { + EXPECT_EQ(1, FormatOctal32(buf, 0, true) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatOctal32, test2) { + EXPECT_EQ(1, FormatOctal32(buf, 0, false) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatOctal32, test3) { + EXPECT_EQ(2, FormatOctal32(buf, 1, true) - buf); + EXPECT_STREQ("01", buf); +} + +TEST(FormatOctal32, test4) { + EXPECT_EQ(1, FormatOctal32(buf, 1, false) - buf); + EXPECT_STREQ("1", buf); +} + +TEST(FormatOctal32, test5) { + EXPECT_EQ(12, FormatOctal32(buf, 037777777777, true) - buf); + EXPECT_STREQ("037777777777", buf); +} + +TEST(FormatOctal32, test6) { + EXPECT_EQ(11, FormatOctal32(buf, 037777777777, false) - buf); + EXPECT_STREQ("37777777777", buf); +} diff --git a/test/libc/fmt/formatoctal64_test.c b/test/libc/fmt/formatoctal64_test.c new file mode 100644 index 000000000..421b48f17 --- /dev/null +++ b/test/libc/fmt/formatoctal64_test.c @@ -0,0 +1,66 @@ +/*-*- 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/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +char buf[25]; + +void SetUp(void) { + memset(buf, 0x55, sizeof(buf)); +} + +TEST(FormatOctal64, test1) { + EXPECT_EQ(1, FormatOctal64(buf, 0, true) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatOctal64, test2) { + EXPECT_EQ(1, FormatOctal64(buf, 0, false) - buf); + EXPECT_STREQ("0", buf); +} + +TEST(FormatOctal64, test3) { + EXPECT_EQ(2, FormatOctal64(buf, 1, true) - buf); + EXPECT_STREQ("01", buf); +} + +TEST(FormatOctal64, test4) { + EXPECT_EQ(1, FormatOctal64(buf, 1, false) - buf); + EXPECT_STREQ("1", buf); +} + +TEST(FormatOctal64, test5) { + EXPECT_EQ(23, FormatOctal64(buf, 01777777777777777777777UL, true) - buf); + EXPECT_STREQ("01777777777777777777777", buf); +} + +TEST(FormatOctal64, test6) { + EXPECT_EQ(22, FormatOctal64(buf, 01777777777777777777777UL, false) - buf); + EXPECT_STREQ("1777777777777777777777", buf); +} + +BENCH(FormatOctal64, bench) { + EZBENCH2("FormatUint64", donothing, + FormatUint64(buf, 01777777777777777777777UL)); + EZBENCH2("FormatOctal64", donothing, + FormatOctal64(buf, 01777777777777777777777UL, true)); + EZBENCH2("FormatOctal32", donothing, FormatOctal32(buf, 037777777777U, true)); +} diff --git a/test/libc/fmt/palandprintf_test.c b/test/libc/fmt/palandprintf_test.c index 4ee153a66..1e1e680d2 100644 --- a/test/libc/fmt/palandprintf_test.c +++ b/test/libc/fmt/palandprintf_test.c @@ -641,6 +641,7 @@ BENCH(palandprintf, bench) { EZBENCH2("%g M_PI", donothing, Format("%g", VEIL("x", M_PI))); EZBENCH2("%a M_PI", donothing, Format("%a", VEIL("x", M_PI))); EZBENCH2("%e M_PI", donothing, Format("%e", VEIL("x", M_PI))); + EZBENCH2("ULONG_MAX %lo", donothing, Format("%lo", VEIL("r", ULONG_MAX))); EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); EZBENCH2("INT_MIN %,d", donothing, Format("%,d", VEIL("r", INT_MIN))); @@ -649,7 +650,6 @@ BENCH(palandprintf, bench) { EZBENCH2("LONG_MIN %ld", donothing, Format("%ld", VEIL("r", LONG_MIN))); EZBENCH2("INT128_MIN %jjd", donothing, Format("%jjd", INT128_MIN)); EZBENCH2("INT128_MIN %jjx", donothing, Format("%jjx", INT128_MIN)); - EZBENCH2("int64toarray 23", donothing, int64toarray_radix10(23, buffer)); - EZBENCH2("int64toarray min", donothing, - int64toarray_radix10(INT_MIN, buffer)); + EZBENCH2("int64toarray 23", donothing, FormatInt64(buffer, 23)); + EZBENCH2("int64toarray min", donothing, FormatInt64(buffer, INT_MIN)); } diff --git a/test/libc/intrin/kprintf_test.c b/test/libc/intrin/kprintf_test.c index e5c35a217..cf4f9ab09 100644 --- a/test/libc/intrin/kprintf_test.c +++ b/test/libc/intrin/kprintf_test.c @@ -43,7 +43,8 @@ */ static uint64_t Rando(void) { uint64_t x; - do x = lemur64(); + do + x = lemur64(); while (((x ^ READ64LE("!!!!!!!!")) - 0x0101010101010101) & ~(x ^ READ64LE("!!!!!!!!")) & 0x8080808080808080); return x; @@ -375,6 +376,8 @@ BENCH(printf, bench) { kusnprintf(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷")); EZBENCH2("snprintf astral", donothing, snprintf_(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷")); + EZBENCH2("kusnprintf octal", donothing, + kusnprintf(b, 128, "%#lo", ULONG_MAX)); EZBENCH2("kusnprintf long", donothing, kusnprintf(b, 128, "%ld", LONG_MAX)); EZBENCH2("snprintf long", donothing, snprintf_(b, 128, "%ld", LONG_MAX)); EZBENCH2("kusnprintf thou", donothing, kusnprintf(b, 128, "%'ld", LONG_MAX)); diff --git a/test/libc/stdio/dirstream_test.c b/test/libc/stdio/dirstream_test.c index 7e9961dcc..5f2ce3641 100644 --- a/test/libc/stdio/dirstream_test.c +++ b/test/libc/stdio/dirstream_test.c @@ -33,6 +33,14 @@ STATIC_YOINK("usr/share/zoneinfo/New_York"); char testlib_enable_tmp_setup_teardown; +DIR *dir; +struct dirent *ent; + +void SetUp(void) { + dir = 0; + ent = 0; +} + TEST(opendir, efault) { ASSERT_SYS(EFAULT, NULL, opendir(0)); if (!IsAsan()) return; // not possible @@ -50,10 +58,8 @@ TEST(opendir, enotdir) { } TEST(dirstream, testDots) { - DIR *dir; int hasdot = 0; int hasdotdot = 0; - struct dirent *ent; ASSERT_SYS(0, 0, close(creat("foo", 0644))); ASSERT_NE(NULL, (dir = opendir("."))); while ((ent = readdir(dir))) { @@ -72,8 +78,6 @@ TEST(dirstream, testDots) { } TEST(dirstream, test) { - DIR *dir; - struct dirent *ent; bool hasfoo = false; bool hasbar = false; char *dpath, *file1, *file2; @@ -104,21 +108,17 @@ TEST(dirstream, test) { TEST(dirstream, zipTest) { bool foundNewYork = false; - DIR *d; - struct dirent *e; const char *path = "/zip/usr/share/zoneinfo/"; ASSERT_NE(0, _gc(xiso8601ts(NULL))); - ASSERT_NE(NULL, (d = opendir(path))); - while ((e = readdir(d))) { - foundNewYork |= !strcmp(e->d_name, "New_York"); + ASSERT_NE(NULL, (dir = opendir(path))); + while ((ent = readdir(dir))) { + foundNewYork |= !strcmp(ent->d_name, "New_York"); } - closedir(d); + closedir(dir); EXPECT_TRUE(foundNewYork); } TEST(rewinddir, test) { - DIR *dir; - struct dirent *ent; bool hasfoo = false; bool hasbar = false; char *dpath, *file1, *file2; diff --git a/test/net/http/parsehttpmessage_test.c b/test/net/http/parsehttpmessage_test.c index 893b953c5..bb2897e38 100644 --- a/test/net/http/parsehttpmessage_test.c +++ b/test/net/http/parsehttpmessage_test.c @@ -528,7 +528,7 @@ GET / HTTP/1.1\r\n\ X-In-Your-Way-A: a\r\n\ X-In-Your-Way-B: b\r\n\ X-In-Your-Way-C: b\r\n\ -Accept-Encoding: deflate\r\n\ +Accept-Encoding:deflate\r\n\ ACCEPT-ENCODING: gzip\r\n\ ACCEPT-encoding: bzip2\r\n\ \r\n"; diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index 7bc87223b..177ee4294 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -19,7 +19,6 @@ #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/fmt/conv.h" -#include "libc/log/check.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/goodsocket.internal.h" @@ -91,7 +90,7 @@ char *SendHttpRequest(const char *s) { bool Matches(const char *regex, const char *str) { bool r; regex_t re; - CHECK_EQ(REG_OK, regcomp(&re, regex, 0)); + EXPECT_EQ(REG_OK, regcomp(&re, regex, 0)); r = regexec(&re, str, 0, 0, 0) == REG_OK; regfree(&re); return r; @@ -103,15 +102,22 @@ TEST(redbean, testOptions) { int pid, pipefds[2]; sigset_t chldmask, savemask; sigaddset(&chldmask, SIGCHLD); - CHECK_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); + EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); ASSERT_NE(-1, pipe(pipefds)); ASSERT_NE(-1, (pid = fork())); if (!pid) { + setpgrp(); + close(0); + open("/dev/null", O_RDWR); + close(1); + dup(0); + close(2); + open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644); close(pipefds[0]); dup2(pipefds[1], 1); sigprocmask(SIG_SETMASK, &savemask, NULL); execv("bin/redbean.com", - (char *const[]){"bin/redbean.com", "-szp0", "-l127.0.0.1", 0}); + (char *const[]){"bin/redbean.com", "-vvszp0", "-l127.0.0.1", 0}); _exit(127); } EXPECT_NE(-1, close(pipefds[1])); @@ -129,7 +135,8 @@ TEST(redbean, testOptions) { EXPECT_EQ(0, close(pipefds[0])); EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); - CHECK_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); + EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); + if (g_testlib_failed) fputs(gc(xslurp("log", 0)), stderr); } TEST(redbean, testPipeline) { @@ -138,15 +145,20 @@ TEST(redbean, testPipeline) { int pid, pipefds[2]; sigset_t chldmask, savemask; sigaddset(&chldmask, SIGCHLD); - CHECK_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); + EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); ASSERT_NE(-1, pipe(pipefds)); ASSERT_NE(-1, (pid = fork())); if (!pid) { + setpgrp(); + close(0); + open("/dev/null", O_RDWR); + close(2); + open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644); close(pipefds[0]); dup2(pipefds[1], 1); sigprocmask(SIG_SETMASK, &savemask, NULL); execv("bin/redbean.com", - (char *const[]){"bin/redbean.com", "-szp0", "-l127.0.0.1", 0}); + (char *const[]){"bin/redbean.com", "-vvszp0", "-l127.0.0.1", 0}); _exit(127); } EXPECT_NE(-1, close(pipefds[1])); @@ -173,5 +185,6 @@ TEST(redbean, testPipeline) { EXPECT_EQ(0, close(pipefds[0])); EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); - CHECK_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); + EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); + if (g_testlib_failed) fputs(gc(xslurp("log", 0)), stderr); } diff --git a/third_party/dlmalloc/dlmalloc.c b/third_party/dlmalloc/dlmalloc.greg.c similarity index 85% rename from third_party/dlmalloc/dlmalloc.c rename to third_party/dlmalloc/dlmalloc.greg.c index 4cb486612..6bc34ac80 100644 --- a/third_party/dlmalloc/dlmalloc.c +++ b/third_party/dlmalloc/dlmalloc.greg.c @@ -17,6 +17,7 @@ #include "libc/bits/weaken.h" #include "libc/intrin/kprintf.h" #include "libc/mem/mem.h" +#include "third_party/dlmalloc/dlmalloc.h" // clang-format off #define FOOTERS 0 @@ -100,7 +101,7 @@ #define FOOTERS 0 #endif /* FOOTERS */ #ifndef ABORT -#define ABORT abort() +#define ABORT dlmalloc_abort() #endif /* ABORT */ #ifndef ABORT_ON_ASSERT_FAILURE #define ABORT_ON_ASSERT_FAILURE 1 @@ -215,588 +216,6 @@ #define FORCEINLINE forceinline #define NOINLINE dontinline -#if !ONLY_MSPACES - -/* ------------------- Declarations of public routines ------------------- */ - -/* - malloc(size_t n) - Returns a pointer to a newly allocated chunk of at least n bytes, or - null if no space is available, in which case errno is set to ENOMEM - on ANSI C systems. - - If n is zero, malloc returns a minimum-sized chunk. (The minimum - size is 16 bytes on most 32bit systems, and 32 bytes on 64bit - systems.) Note that size_t is an unsigned type, so calls with - arguments that would be negative if signed are interpreted as - requests for huge amounts of space, which will often fail. The - maximum supported value of n differs across systems, but is in all - cases less than the maximum representable value of a size_t. -*/ -DLMALLOC_EXPORT void* dlmalloc(size_t); - -/* - free(void* p) - Releases the chunk of memory pointed to by p, that had been previously - allocated using malloc or a related routine such as realloc. - It has no effect if p is null. If p was not malloced or already - freed, free(p) will by default cause the current program to abort. -*/ -DLMALLOC_EXPORT void dlfree(void*); - -/* - calloc(size_t n_elements, size_t element_size); - Returns a pointer to n_elements * element_size bytes, with all locations - set to zero. -*/ -DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); - -/* - realloc(void* p, size_t n) - Returns a pointer to a chunk of size n that contains the same data - as does chunk p up to the minimum of (n, p's size) bytes, or null - if no space is available. - - The returned pointer may or may not be the same as p. The algorithm - prefers extending p in most cases when possible, otherwise it - employs the equivalent of a malloc-copy-free sequence. - - If p is null, realloc is equivalent to malloc. - - If space is not available, realloc returns null, errno is set (if on - ANSI) and p is NOT freed. - - if n is for fewer bytes than already held by p, the newly unused - space is lopped off and freed if possible. realloc with a size - argument of zero (re)allocates a minimum-sized chunk. - - The old unix realloc convention of allowing the last-free'd chunk - to be used as an argument to realloc is not supported. -*/ -DLMALLOC_EXPORT void* dlrealloc(void*, size_t); - -/* - realloc_in_place(void* p, size_t n) - Resizes the space allocated for p to size n, only if this can be - done without moving p (i.e., only if there is adjacent space - available if n is greater than p's current allocated size, or n is - less than or equal to p's size). This may be used instead of plain - realloc if an alternative allocation strategy is needed upon failure - to expand space; for example, reallocation of a buffer that must be - memory-aligned or cleared. You can use realloc_in_place to trigger - these alternatives only when needed. - - Returns p if successful; otherwise null. -*/ -DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); - -/* - memalign(size_t alignment, size_t n); - Returns a pointer to a newly allocated chunk of n bytes, aligned - in accord with the alignment argument. - - The alignment argument should be a power of two. If the argument is - not a power of two, the nearest greater power is used. - 8-byte alignment is guaranteed by normal malloc calls, so don't - bother calling memalign with an argument of 8 or less. - - Overreliance on memalign is a sure way to fragment space. -*/ -DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); - -/* - int posix_memalign(void** pp, size_t alignment, size_t n); - Allocates a chunk of n bytes, aligned in accord with the alignment - argument. Differs from memalign only in that it (1) assigns the - allocated memory to *pp rather than returning it, (2) fails and - returns EINVAL if the alignment is not a power of two (3) fails and - returns ENOMEM if memory cannot be allocated. -*/ -DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); - -/* - valloc(size_t n); - Equivalent to memalign(pagesize, n), where pagesize is the page - size of the system. If the pagesize is unknown, 4096 is used. -*/ -DLMALLOC_EXPORT void* dlvalloc(size_t); - -/* - mallopt(int parameter_number, int parameter_value) - Sets tunable parameters The format is to provide a - (parameter-number, parameter-value) pair. mallopt then sets the - corresponding parameter to the argument value if it can (i.e., so - long as the value is meaningful), and returns 1 if successful else - 0. To workaround the fact that mallopt is specified to use int, - not size_t parameters, the value -1 is specially treated as the - maximum unsigned size_t value. - - SVID/XPG/ANSI defines four standard param numbers for mallopt, - normally defined in malloc.h. None of these are use in this malloc, - so setting them has no effect. But this malloc also supports other - options in mallopt. See below for details. Briefly, supported - parameters are as follows (listed defaults are for "typical" - configurations). - - Symbol param # default allowed param values - M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) - M_GRANULARITY -2 page size any power of 2 >= page size - M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) -*/ -DLMALLOC_EXPORT int dlmallopt(int, int); - -/* - malloc_footprint(); - Returns the number of bytes obtained from the system. The total - number of bytes allocated by malloc, realloc etc., is less than this - value. Unlike mallinfo, this function returns only a precomputed - result, so can be called frequently to monitor memory consumption. - Even if locks are otherwise defined, this function does not use them, - so results might not be up to date. -*/ -DLMALLOC_EXPORT size_t dlmalloc_footprint(void); - -/* - malloc_max_footprint(); - Returns the maximum number of bytes obtained from the system. This - value will be greater than current footprint if deallocated space - has been reclaimed by the system. The peak number of bytes allocated - by malloc, realloc etc., is less than this value. Unlike mallinfo, - this function returns only a precomputed result, so can be called - frequently to monitor memory consumption. Even if locks are - otherwise defined, this function does not use them, so results might - not be up to date. -*/ -DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); - -/* - malloc_footprint_limit(); - Returns the number of bytes that the heap is allowed to obtain from - the system, returning the last value returned by - malloc_set_footprint_limit, or the maximum size_t value if - never set. The returned value reflects a permission. There is no - guarantee that this number of bytes can actually be obtained from - the system. -*/ -DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); - -/* - malloc_set_footprint_limit(); - Sets the maximum number of bytes to obtain from the system, causing - failure returns from malloc and related functions upon attempts to - exceed this value. The argument value may be subject to page - rounding to an enforceable limit; this actual value is returned. - Using an argument of the maximum possible size_t effectively - disables checks. If the argument is less than or equal to the - current malloc_footprint, then all future allocations that require - additional system memory will fail. However, invocation cannot - retroactively deallocate existing used memory. -*/ -DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); - -#if MALLOC_INSPECT_ALL -/* - malloc_inspect_all(void(*handler)(void *start, - void *end, - size_t used_bytes, - void* callback_arg), - void* arg); - Traverses the heap and calls the given handler for each managed - region, skipping all bytes that are (or may be) used for bookkeeping - purposes. Traversal does not include include chunks that have been - directly memory mapped. Each reported region begins at the start - address, and continues up to but not including the end address. The - first used_bytes of the region contain allocated data. If - used_bytes is zero, the region is unallocated. The handler is - invoked with the given callback argument. If locks are defined, they - are held during the entire traversal. It is a bad idea to invoke - other malloc functions from within the handler. - - For example, to count the number of in-use chunks with size greater - than 1000, you could write: - static int count = 0; - void count_chunks(void* start, void* end, size_t used, void* arg) { - if (used >= 1000) ++count; - } - then: - malloc_inspect_all(count_chunks, NULL); - - malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. -*/ -DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), - void* arg); - -#endif /* MALLOC_INSPECT_ALL */ - -/* - mallinfo() - Returns (by copy) a struct containing various summary statistics: - - arena: current total non-mmapped bytes allocated from system - ordblks: the number of free chunks - smblks: always zero. - hblks: current number of mmapped regions - hblkhd: total bytes held in mmapped regions - usmblks: the maximum total allocated space. This will be greater - than current total if trimming has occurred. - fsmblks: always zero - uordblks: current total allocated space (normal or mmapped) - fordblks: total free space - keepcost: the maximum number of bytes that could ideally be released - back to system via malloc_trim. ("ideally" means that - it ignores page restrictions etc.) - - Because these fields are ints, but internal bookkeeping may - be kept as longs, the reported values may wrap around zero and - thus be inaccurate. -*/ -DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); - -/* - independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); - - independent_calloc is similar to calloc, but instead of returning a - single cleared space, it returns an array of pointers to n_elements - independent elements that can hold contents of size elem_size, each - of which starts out cleared, and can be independently freed, - realloc'ed etc. The elements are guaranteed to be adjacently - allocated (this is not guaranteed to occur with multiple callocs or - mallocs), which may also improve cache locality in some - applications. - - The "chunks" argument is optional (i.e., may be null, which is - probably the most typical usage). If it is null, the returned array - is itself dynamically allocated and should also be freed when it is - no longer needed. Otherwise, the chunks array must be of at least - n_elements in length. It is filled in with the pointers to the - chunks. - - In either case, independent_calloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and "chunks" - is null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be freed when it is no longer needed. This can be - done all at once using bulk_free. - - independent_calloc simplifies and speeds up implementations of many - kinds of pools. It may also be useful when constructing large data - structures that initially have a fixed number of fixed-sized nodes, - but the number is not known at compile time, and some of the nodes - may later need to be freed. For example: - - struct Node { int item; struct Node* next; }; - - struct Node* build_list() { - struct Node** pool; - int n = read_number_of_nodes_needed(); - if (n <= 0) return 0; - pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); - if (pool == 0) die(); - // organize into a linked list... - struct Node* first = pool[0]; - for (i = 0; i < n-1; ++i) - pool[i]->next = pool[i+1]; - free(pool); // Can now free the array (or not, if it is needed later) - return first; - } -*/ -DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); - -/* - independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); - - independent_comalloc allocates, all at once, a set of n_elements - chunks with sizes indicated in the "sizes" array. It returns - an array of pointers to these elements, each of which can be - independently freed, realloc'ed etc. The elements are guaranteed to - be adjacently allocated (this is not guaranteed to occur with - multiple callocs or mallocs), which may also improve cache locality - in some applications. - - The "chunks" argument is optional (i.e., may be null). If it is null - the returned array is itself dynamically allocated and should also - be freed when it is no longer needed. Otherwise, the chunks array - must be of at least n_elements in length. It is filled in with the - pointers to the chunks. - - In either case, independent_comalloc returns this pointer array, or - null if the allocation failed. If n_elements is zero and chunks is - null, it returns a chunk representing an array with zero elements - (which should be freed if not wanted). - - Each element must be freed when it is no longer needed. This can be - done all at once using bulk_free. - - independent_comallac differs from independent_calloc in that each - element may have a different size, and also that it does not - automatically clear elements. - - independent_comalloc can be used to speed up allocation in cases - where several structs or objects must always be allocated at the - same time. For example: - - struct Head { ... } - struct Foot { ... } - - void send_message(char* msg) { - int msglen = strlen(msg); - size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; - void* chunks[3]; - if (independent_comalloc(3, sizes, chunks) == 0) - die(); - struct Head* head = (struct Head*)(chunks[0]); - char* body = (char*)(chunks[1]); - struct Foot* foot = (struct Foot*)(chunks[2]); - // ... - } - - In general though, independent_comalloc is worth using only for - larger values of n_elements. For small values, you probably won't - detect enough difference from series of malloc calls to bother. - - Overuse of independent_comalloc can increase overall memory usage, - since it cannot reuse existing noncontiguous small chunks that - might be available for some of the elements. -*/ -DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); - -/* - bulk_free(void* array[], size_t n_elements) - Frees and clears (sets to null) each non-null pointer in the given - array. This is likely to be faster than freeing them one-by-one. - If footers are used, pointers that have been allocated in different - mspaces are not freed or cleared, and the count of all such pointers - is returned. For large arrays of pointers with poor locality, it - may be worthwhile to sort this array before calling bulk_free. -*/ -DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); - -/* - pvalloc(size_t n); - Equivalent to valloc(minimum-page-that-holds(n)), that is, - round up n to nearest pagesize. - */ -DLMALLOC_EXPORT void* dlpvalloc(size_t); - -/* - malloc_trim(size_t pad); - - If possible, gives memory back to the system (via negative arguments - to sbrk) if there is unused memory at the `high' end of the malloc - pool or in unused MMAP segments. You can call this after freeing - large blocks of memory to potentially reduce the system-level memory - requirements of a program. However, it cannot guarantee to reduce - memory. Under some allocation patterns, some large free blocks of - memory will be locked between two used chunks, so they cannot be - given back to the system. - - The `pad' argument to malloc_trim represents the amount of free - trailing space to leave untrimmed. If this argument is zero, only - the minimum amount of memory to maintain internal data structures - will be left. Non-zero arguments can be supplied to maintain enough - trailing space to service future expected allocations without having - to re-obtain memory from the system. - - Malloc_trim returns 1 if it actually released any memory, else 0. -*/ -DLMALLOC_EXPORT int dlmalloc_trim(size_t); - -/* - malloc_stats(); - Prints on stderr the amount of space obtained from the system (both - via sbrk and mmap), the maximum amount (which may be more than - current if malloc_trim and/or munmap got called), and the current - number of bytes allocated via malloc (or realloc, etc) but not yet - freed. Note that this is the number of bytes allocated, not the - number requested. It will be larger than the number requested - because of alignment and bookkeeping overhead. Because it includes - alignment wastage as being in use, this figure may be greater than - zero even when no user-level chunks are allocated. - - The reported current and maximum system memory can be inaccurate if - a program makes other calls to system memory allocation functions - (normally sbrk) outside of malloc. - - malloc_stats prints only the most commonly interesting statistics. - More information can be obtained by calling mallinfo. -*/ -DLMALLOC_EXPORT void dlmalloc_stats(void); - -/* - malloc_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. malloc_usable_size can be more useful in - debugging and assertions, for example: - - p = malloc(n); - assert(malloc_usable_size(p) >= 256); -*/ -size_t dlmalloc_usable_size(void*); - -#endif /* ONLY_MSPACES */ - -#if MSPACES - -/* - mspace is an opaque type representing an independent - region of space that supports mspace_malloc, etc. -*/ -typedef void* mspace; - -/* - create_mspace creates and returns a new independent space with the - given initial capacity, or, if 0, the default granularity size. It - returns null if there is no system memory available to create the - space. If argument locked is non-zero, the space uses a separate - lock to control access. The capacity of the space will grow - dynamically as needed to service mspace_malloc requests. You can - control the sizes of incremental increases of this space by - compiling with a different DEFAULT_GRANULARITY or dynamically - setting with mallopt(M_GRANULARITY, value). -*/ -DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); - -/* - destroy_mspace destroys the given space, and attempts to return all - of its memory back to the system, returning the total number of - bytes freed. After destruction, the results of access to all memory - used by the space become undefined. -*/ -DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); - -/* - create_mspace_with_base uses the memory supplied as the initial base - of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this - space is used for bookkeeping, so the capacity must be at least this - large. (Otherwise 0 is returned.) When this initial space is - exhausted, additional memory will be obtained from the system. - Destroying this space will deallocate all additionally allocated - space (if possible) but not the initial base. -*/ -DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); - -/* - mspace_track_large_chunks controls whether requests for large chunks - are allocated in their own untracked mmapped regions, separate from - others in this mspace. By default large chunks are not tracked, - which reduces fragmentation. However, such chunks are not - necessarily released to the system upon destroy_mspace. Enabling - tracking by setting to true may increase fragmentation, but avoids - leakage when relying on destroy_mspace to release all memory - allocated using this space. The function returns the previous - setting. -*/ -DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); - - -/* - mspace_malloc behaves as malloc, but operates within - the given space. -*/ -DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); - -/* - mspace_free behaves as free, but operates within - the given space. - - If compiled with FOOTERS==1, mspace_free is not actually needed. - free may be called instead of mspace_free because freed chunks from - any space are handled by their originating spaces. -*/ -DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); - -/* - mspace_realloc behaves as realloc, but operates within - the given space. - - If compiled with FOOTERS==1, mspace_realloc is not actually - needed. realloc may be called instead of mspace_realloc because - realloced chunks from any space are handled by their originating - spaces. -*/ -DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); - -/* - mspace_calloc behaves as calloc, but operates within - the given space. -*/ -DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); - -/* - mspace_memalign behaves as memalign, but operates within - the given space. -*/ -DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); - -/* - mspace_independent_calloc behaves as independent_calloc, but - operates within the given space. -*/ -DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void* chunks[]); - -/* - mspace_independent_comalloc behaves as independent_comalloc, but - operates within the given space. -*/ -DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void* chunks[]); - -/* - mspace_footprint() returns the number of bytes obtained from the - system for this space. -*/ -DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); - -/* - mspace_max_footprint() returns the peak number of bytes obtained from the - system for this space. -*/ -DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); - - -#if !NO_MALLINFO -/* - mspace_mallinfo behaves as mallinfo, but reports properties of - the given space. -*/ -DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); -#endif /* NO_MALLINFO */ - -/* - malloc_usable_size(void* p) behaves the same as malloc_usable_size; -*/ -DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); - -/* - mspace_malloc_stats behaves as malloc_stats, but reports - properties of the given space. -*/ -DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); - -/* - mspace_trim behaves as malloc_trim, but - operates within the given space. -*/ -DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); - -/* - An alias for mallopt. -*/ -DLMALLOC_EXPORT int mspace_mallopt(int, int); - -#endif /* MSPACES */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif /* __cplusplus */ - /* ======================================================================== To make a fully customizable malloc.h header file, cut everything @@ -4364,7 +3783,7 @@ int dlmallopt(int param_number, int value) { return change_mparam(param_number, value); } -size_t dlmalloc_usable_size(void* mem) { +size_t dlmalloc_usable_size(const void* mem) { mchunkptr p; size_t bytes; if (mem) { diff --git a/third_party/dlmalloc/dlmalloc.h b/third_party/dlmalloc/dlmalloc.h index ad98b7595..6de3f7623 100644 --- a/third_party/dlmalloc/dlmalloc.h +++ b/third_party/dlmalloc/dlmalloc.h @@ -505,6 +505,8 @@ void mspace_inspect_all(mspace msp, void (*handler)(void*, void*, size_t, void*), void* arg); +void dlmalloc_abort(void); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_THIRD_PARTY_DLMALLOC_DLMALLOC_H_ */ diff --git a/third_party/dlmalloc/dlmalloc.mk b/third_party/dlmalloc/dlmalloc.mk index e12187fb6..2c27169fa 100644 --- a/third_party/dlmalloc/dlmalloc.mk +++ b/third_party/dlmalloc/dlmalloc.mk @@ -52,6 +52,7 @@ $(THIRD_PARTY_DLMALLOC_A).pkg: \ $(THIRD_PARTY_DLMALLOC_A_OBJS): \ OVERRIDE_CFLAGS += \ $(NO_MAGIC) \ + -ffreestanding \ -ffunction-sections \ -fdata-sections diff --git a/third_party/dlmalloc/dlmalloc_abort.greg.c b/third_party/dlmalloc/dlmalloc_abort.greg.c new file mode 100644 index 000000000..12ea05742 --- /dev/null +++ b/third_party/dlmalloc/dlmalloc_abort.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/bits/weaken.h" +#include "libc/intrin/kprintf.h" +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "third_party/dlmalloc/dlmalloc.h" + +void dlmalloc_abort(void) { + kprintf("dlmalloc_abort()%n"); + if (weaken(__die)) weaken(__die)(); + _Exit(44); +} diff --git a/third_party/dlmalloc/vespene.c b/third_party/dlmalloc/vespene.greg.c similarity index 100% rename from third_party/dlmalloc/vespene.c rename to third_party/dlmalloc/vespene.greg.c diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index cc7a0f94c..7952909ec 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -161,6 +161,7 @@ #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" #include "libc/unicode/unicode.h" #include "net/http/escape.h" #include "third_party/linenoise/linenoise.h" @@ -189,6 +190,18 @@ 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 BLOCKING_READ() rc = linenoiseRead(l->ifd, seq, sizeof(seq), l, false) + struct abuf { char *b; int len; @@ -206,6 +219,7 @@ struct linenoiseRing { }; struct linenoiseState { + int state; /* state machine */ int ifd; /* terminal stdin file descriptor */ int ofd; /* terminal stdout file descriptor */ struct winsize ws; /* rows and columns in terminal */ @@ -223,6 +237,12 @@ struct linenoiseState { char seq[2][16]; /* keystroke history for yanking code */ char final; /* set to true on last update */ char dirty; /* if an update was squashed */ + linenoiseCompletions lc; + struct abuf ab; + int i, j, perline, itemlen; + // for reverse search + int fail, matlen, oldindex, olderpos; + const char *oldprompt; }; static const unsigned short kMirrorLeft[][2] = { @@ -258,7 +278,7 @@ static const char *const kUnsupported[] = {"dumb", "cons25", "emacs"}; static int gotint; static int gotcont; static int gotwinch; -static char rawmode; +static char rawmode = -1; static char maskmode; static char ispaused; static char iscapital; @@ -270,15 +290,11 @@ static struct sigaction orig_cont; static struct sigaction orig_winch; static struct termios orig_termios; static char *history[LINENOISE_MAX_HISTORY]; -static linenoisePollCallback *pollCallback; static linenoiseXlatCallback *xlatCallback; static linenoiseHintsCallback *hintsCallback; static linenoiseFreeHintsCallback *freeHintsCallback; static linenoiseCompletionCallback *completionCallback; -static void linenoiseAtExit(void); -static void linenoiseRefreshLine(struct linenoiseState *); - static unsigned GetMirror(const unsigned short A[][2], size_t n, unsigned c) { int l, m, r; l = 0; @@ -396,7 +412,7 @@ static int linenoiseIsUnsupportedTerm(void) { return res; } -static int linenoiseIsTerminal(void) { +int linenoiseIsTerminal(void) { static int once, res; if (!once) { res = isatty(fileno(stdin)) && isatty(fileno(stdout)) && @@ -406,7 +422,7 @@ static int linenoiseIsTerminal(void) { return res; } -static int linenoiseIsTeletype(void) { +int linenoiseIsTeletype(void) { static int once, res; if (!once) { res = linenoiseIsTerminal() || @@ -416,7 +432,7 @@ static int linenoiseIsTeletype(void) { return res; } -static char *GetLine(FILE *f) { +char *linenoiseGetLine(FILE *f) { ssize_t rc; char *p = 0; size_t n, c = 0; @@ -632,33 +648,37 @@ static void linenoiseUnpause(int fd) { } } -static int enableRawMode(int fd) { +int linenoiseEnableRawMode(int fd) { struct termios raw; struct sigaction sa; - if (tcgetattr(fd, &orig_termios) != -1) { - raw = orig_termios; - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - raw.c_oflag &= ~OPOST; - raw.c_iflag |= IUTF8; - raw.c_cflag |= CS8; - raw.c_cc[VMIN] = 1; - raw.c_cc[VTIME] = 0; - if (tcsetattr(fd, TCSANOW, &raw) != -1) { - sa.sa_flags = 0; - sa.sa_handler = linenoiseOnCont; - sigemptyset(&sa.sa_mask); - sigaction(SIGCONT, &sa, &orig_cont); - sa.sa_handler = linenoiseOnWinch; - sigaction(SIGWINCH, &sa, &orig_winch); - rawmode = fd; - gotwinch = 0; - gotcont = 0; - return 0; + if (rawmode == -1) { + if (tcgetattr(fd, &orig_termios) != -1) { + raw = orig_termios; + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_oflag &= ~OPOST; + raw.c_iflag |= IUTF8; + raw.c_cflag |= CS8; + raw.c_cc[VMIN] = 1; + raw.c_cc[VTIME] = 0; + if (tcsetattr(fd, TCSANOW, &raw) != -1) { + sa.sa_flags = 0; + sa.sa_handler = linenoiseOnCont; + sigemptyset(&sa.sa_mask); + sigaction(SIGCONT, &sa, &orig_cont); + sa.sa_handler = linenoiseOnWinch; + sigaction(SIGWINCH, &sa, &orig_winch); + rawmode = fd; + gotwinch = 0; + gotcont = 0; + return 0; + } } + errno = ENOTTY; + return -1; + } else { + return 0; } - errno = ENOTTY; - return -1; } void linenoiseDisableRawMode(void) { @@ -676,10 +696,6 @@ static int linenoiseWrite(int fd, const void *p, size_t n) { size_t wrote; do { for (;;) { - if (gotint) { - errno = EINTR; - return -1; - } if (ispaused) { return 0; } @@ -727,40 +743,39 @@ static void linenoiseDebug(struct linenoiseState *l, const char *fmt, ...) { } static int linenoisePoll(struct linenoiseState *l, int fd) { - int rc, ms; - for (ms = LINENOISE_POLL_MS;;) { - if (pollCallback) { - rc = pollCallback(fd, ms); - } else { - rc = poll((struct pollfd[]){{fd, POLLIN}}, 1, ms); - } - if (rc) { - return rc; - } else { - linenoiseRefreshLine(l); - } + int rc; + if ((rc = poll((struct pollfd[]){{fd, POLLIN}}, 1, 0))) { + return rc; + } else { + l->dirty = true; + return eagain(); } } static ssize_t linenoiseRead(int fd, char *buf, size_t size, - struct linenoiseState *l) { + struct linenoiseState *l, int block) { ssize_t rc; int refreshme; - do { + for (;;) { refreshme = 0; if (gotint) { errno = EINTR; return -1; } if (gotcont && rawmode != -1) { - enableRawMode(rawmode); + linenoiseEnableRawMode(rawmode); if (l) refreshme = 1; } if (l && gotwinch) refreshme = 1; if (refreshme) linenoiseRefreshLine(l); - if (linenoisePoll(l, fd) == -1) return -1; + if (!block && linenoisePoll(l, fd) == -1) return -1; rc = readansi(fd, buf, size); - } while (rc == -1 && errno == EINTR); + if (rc == -1 && errno == EINTR) { + if (!block) break; + } else { + break; + } + } if (l && rc > 0) { memcpy(l->seq[1], l->seq[0], sizeof(l->seq[0])); memset(l->seq[0], 0, sizeof(l->seq[0])); @@ -782,7 +797,7 @@ static ssize_t linenoiseRead(int fd, char *buf, size_t size, * @param ofd is output file descriptor * @return window size */ -static struct winsize GetTerminalSize(struct winsize ws, int ifd, int ofd) { +struct winsize linenoiseGetTerminalSize(struct winsize ws, int ifd, int ofd) { int x; ssize_t n; char *p, *s, b[16]; @@ -793,13 +808,13 @@ static struct winsize GetTerminalSize(struct winsize ws, int ifd, int ofd) { if ((!ws.ws_col && (s = getenv("COLUMNS")) && (x = ParseUnsigned(s, 0)))) { ws.ws_col = x; } - if (((!ws.ws_col || !ws.ws_row) && linenoiseRead(ifd, 0, 0, 0) != -1 && + 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 */ - (n = linenoiseRead(ifd, b, sizeof(b), 0)) != -1 && + (n = linenoiseRead(ifd, b, sizeof(b), 0, 1)) != -1 && n && b[0] == 033 && b[1] == '[' && b[n - 1] == 'R')) { p = b + 2; if ((x = ParseUnsigned(p, &p))) ws.ws_row = x; @@ -920,75 +935,6 @@ Finished: return i; } -/** - * Performs tab completion similar in behavior to bash and readline. - */ -static ssize_t linenoiseCompleteLine(struct linenoiseState *ls, char *seq, - int size) { - char *s; - ssize_t nread; - struct abuf ab; - linenoiseCompletions lc; - struct linenoiseState saved; - size_t i, j, k, n, m, perline, itemlen; - // we know that the user pressed tab once - nread = 0; - bzero(&lc, sizeof(lc)); - i = Backwards(ls, ls->pos, iswname); - j = Forwards(ls, ls->pos, iswname); - s = strndup(ls->buf + i, j - i); - completionCallback(s, &lc); - m = GetCommonPrefixLength(&lc); - if (m > j - i) { - // on common prefix (or single completion) we complete and return - n = i + m + (ls->len - j); - if (linenoiseGrow(ls, n + 1)) { - memmove(ls->buf + i + m, ls->buf + i + j, ls->len - j + 1); - memcpy(ls->buf + i, lc.cvec[0], m); - ls->len = ls->pos = n; - } - linenoiseRefreshLine(ls); - nread = linenoiseRead(ls->ifd, seq, size, ls); - } else if (lc.len > 1) { - // 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 - for (i = 0; i < lc.len; ++i) { - s = lc.cvec[i]; - lc.cvec[i] = VisualizeControlCodes(s, -1, 0); - free(s); - } - for (;;) { - nread = linenoiseRead(ls->ifd, seq, size, ls); - if (nread == 1 && seq[0] == '\t') { - itemlen = linenoiseMaxCompletionWidth(&lc) + 4; - perline = MAX(1, (ls->ws.ws_col - 1) / itemlen); - abInit(&ab); - abAppends(&ab, "\r\n\033[K"); - for (i = 0; i < lc.len;) { - for (j = 0; i < lc.len && j < perline; ++j, ++i) { - n = GetMonospaceWidth(lc.cvec[i], strlen(lc.cvec[i]), 0); - abAppends(&ab, lc.cvec[i]); - for (k = n; k < itemlen; ++k) { - abAppendw(&ab, ' '); - } - } - abAppendw(&ab, READ16LE("\r\n")); - } - ab.len -= 2; - abAppends(&ab, "\n"); - linenoiseWriteStr(ls->ofd, ab.b); - linenoiseRefreshLine(ls); - abFree(&ab); - } else { - break; - } - } - } - linenoiseFreeCompletions(&lc); - return nread; -} - static void linenoiseEditHistoryGoto(struct linenoiseState *l, int i) { size_t n; if (historylen <= 1) return; @@ -1009,91 +955,17 @@ static void linenoiseEditHistoryMove(struct linenoiseState *l, int dx) { linenoiseEditHistoryGoto(l, l->hindex + dx); } -static char *linenoiseMakeSearchPrompt(struct abuf *ab, int fail, const char *s, - int n) { - ab->len = 0; - abAppendw(ab, '('); - if (fail) abAppends(ab, "failed "); - abAppends(ab, "reverse-i-search `\e[4m"); - abAppend(ab, s, n); - abAppends(ab, "\033[24m"); - abAppends(ab, s + n); - abAppendw(ab, READ32LE("') ")); - return ab->b; -} - -static int linenoiseSearch(struct linenoiseState *l, char *seq, int size) { - char *p; +static char *linenoiseMakeSearchPrompt(int fail, const char *s, int n) { struct abuf ab; - struct abuf prompt; - const char *oldprompt, *q; - int i, j, k, rc, fail, added, oldpos, matlen, oldindex; - if (historylen <= 1) return 0; abInit(&ab); - abInit(&prompt); - oldpos = l->pos; - oldprompt = l->prompt; - oldindex = l->hindex; - for (fail = matlen = 0;;) { - l->prompt = linenoiseMakeSearchPrompt(&prompt, fail, ab.b, matlen); - linenoiseRefreshLine(l); - fail = 1; - added = 0; - j = l->pos; - i = l->hindex; - rc = linenoiseRead(l->ifd, seq, size, l); - if (rc > 0) { - if (seq[0] == CTRL('?') || seq[0] == CTRL('H')) { - if (ab.len) { - --ab.len; - matlen = MIN(matlen, ab.len); - } - } else if (seq[0] == CTRL('R')) { - if (j) { - --j; - } else if (i + 1 < historylen) { - ++i; - j = strlen(history[historylen - 1 - i]); - } - } else if (seq[0] == CTRL('G')) { - linenoiseEditHistoryGoto(l, oldindex); - l->pos = oldpos; - rc = 0; - break; - } else if (iswcntrl(seq[0])) { /* only sees canonical c0 */ - break; - } else { - abAppend(&ab, seq, rc); - added = rc; - } - } else { - break; - } - while (i < historylen) { - p = history[historylen - 1 - i]; - k = strlen(p); - j = j >= 0 ? MIN(k, j + ab.len) : k; - if ((q = FindSubstringReverse(p, j, ab.b, ab.len))) { - linenoiseEditHistoryGoto(l, i); - l->pos = q - p; - fail = 0; - if (added) { - matlen += added; - added = 0; - } - break; - } else { - i = i + 1; - j = -1; - } - } - } - l->prompt = oldprompt; - linenoiseRefreshLine(l); - abFree(&prompt); - abFree(&ab); - linenoiseRefreshLine(l); - return rc; + abAppendw(&ab, '('); + if (fail) abAppends(&ab, "failed "); + abAppends(&ab, "reverse-i-search `\e[4m"); + abAppend(&ab, s, n); + abAppends(&ab, "\033[24m"); + abAppends(&ab, s + n); + abAppendw(&ab, READ32LE("') ")); + return ab.b; } static void linenoiseRingFree(void) { @@ -1236,7 +1108,7 @@ static void linenoiseRefreshLineImpl(struct linenoiseState *l, int force) { oldsize = l->ws; if ((resized = gotwinch) && rawmode != -1) { gotwinch = 0; - l->ws = GetTerminalSize(l->ws, l->ifd, l->ofd); + l->ws = linenoiseGetTerminalSize(l->ws, l->ifd, l->ofd); } hasflip = !l->final && !linenoiseMirror(l, flip); @@ -1392,7 +1264,7 @@ StartOver: abFree(&ab); } -static void linenoiseRefreshLine(struct linenoiseState *l) { +void linenoiseRefreshLine(struct linenoiseState *l) { linenoiseRefreshLineImpl(l, 0); } @@ -1740,17 +1612,6 @@ static size_t linenoiseEscape(char *d, const char *s, size_t n) { return p - d; } -static void linenoiseEditInsertEscape(struct linenoiseState *l) { - size_t m; - ssize_t n; - char seq[16]; - char esc[sizeof(seq) * 4]; - if ((n = linenoiseRead(l->ifd, seq, sizeof(seq), l)) > 0) { - m = linenoiseEscape(esc, seq, n); - linenoiseEditInsert(l, esc, m); - } -} - static void linenoiseEditInterrupt(struct linenoiseState *l) { gotint = SIGINT; } @@ -1769,12 +1630,6 @@ static void linenoiseEditPause(struct linenoiseState *l) { } static void linenoiseEditCtrlq(struct linenoiseState *l) { - if (ispaused) { - linenoiseUnpause(l->ofd); - linenoiseRefreshLineForce(l); - } else { - linenoiseEditInsertEscape(l); - } } /** @@ -1916,214 +1771,433 @@ static void linenoiseEditSlurp(struct linenoiseState *l) { free(stack); } +struct linenoiseState *linenoiseBegin(const char *prompt, int ifd, int ofd) { + struct linenoiseState *l; + if (!(l = calloc(1, sizeof(*l)))) { + return 0; + } + if (!(l->buf = malloc((l->buflen = 32)))) { + free(l); + return 0; + } + 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->dirty = true; + l->final = 0; + l->hindex = 0; + l->len = 0; + l->mark = 0; + l->oldpos = 0; + l->pos = 0; + l->yi = 0; + l->yj = 0; +} + +void linenoiseEnd(struct linenoiseState *l) { + if (l) { + linenoiseFreeCompletions(&l->lc); + abFree(&l->ab); + free(l->oldprompt); + free(l->prompt); + free(l->buf); + free(l); + } +} + /** * Runs linenoise engine. * * This function is the core of the line editing capability of linenoise. * It expects 'fd' to be already in "raw mode" so that every key pressed - * will be returned ASAP to read(). + * will be returned ASAP to read(). The exit conditions are: * - * The resulting string is put into 'buf' when the user type enter, or - * when ctrl+d is typed. + * 1. ret > 0 / buf ≠ 0 / errno = ? -- means we got some + * 2. ret = 0 / buf ≠ 0 / errno = ? -- means empty line + * 3. ret = 0 / buf = 0 / errno = ? -- means eof + * 4. ret = -1 / buf = ? / errno ≠ 0 -- means error * - * Returns chomped character count in buf >=0 or -1 on eof / error + * @param l is linenoise reader object created by linenoiseBegin() + * @param prompt if non-null is copied and replaces current prompt + * @param block if false will cause -1 / EAGAIN if there's no data + * @return chomped character count in buf >=0 or -1 on eof / error */ -static ssize_t linenoiseEdit(int stdin_fd, int stdout_fd, const char *prompt, - char **obuf) { - int st; +ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf, + bool block) { ssize_t rc; - uint64_t w; - size_t nread; - char *p, seq[16]; - struct rune rune; - struct linenoiseState l; - bzero(&l, sizeof(l)); - if (!(l.buf = malloc((l.buflen = 32)))) return -1; - l.buf[0] = 0; - l.ifd = stdin_fd; - l.ofd = stdout_fd; - l.prompt = prompt ? prompt : ""; - l.ws = GetTerminalSize(l.ws, l.ifd, l.ofd); - linenoiseHistoryAdd(""); - linenoiseWriteStr(l.ofd, l.prompt); - for (;;) { - if (l.dirty) linenoiseRefreshLineForce(&l); - rc = linenoiseRead(l.ifd, seq, sizeof(seq), &l); - if (rc > 0) { - if (seq[0] == CTRL('R')) { - rc = linenoiseSearch(&l, seq, sizeof(seq)); - if (!rc) continue; - } else if (seq[0] == '\t' && completionCallback) { - rc = linenoiseCompleteLine(&l, seq, sizeof(seq)); - if (!rc) continue; - } - } - if (rc > 0) { - nread = rc; - } else if (!rc && l.len) { - nread = 1; - seq[0] = '\r'; - seq[1] = 0; - } else { - free(history[--historylen]); - history[historylen] = 0; - free(l.buf); - return -1; - } - switch (seq[0]) { - CASE(CTRL('P'), linenoiseEditUp(&l)); - CASE(CTRL('E'), linenoiseEditEnd(&l)); - CASE(CTRL('N'), linenoiseEditDown(&l)); - CASE(CTRL('A'), linenoiseEditHome(&l)); - CASE(CTRL('B'), linenoiseEditLeft(&l)); - CASE(CTRL('@'), linenoiseEditMark(&l)); - CASE(CTRL('Y'), linenoiseEditYank(&l)); - CASE(CTRL('Q'), linenoiseEditCtrlq(&l)); - CASE(CTRL('F'), linenoiseEditRight(&l)); - CASE(CTRL('\\'), linenoiseEditQuit(&l)); - CASE(CTRL('S'), linenoiseEditPause(&l)); - CASE(CTRL('?'), linenoiseEditRubout(&l)); - CASE(CTRL('H'), linenoiseEditRubout(&l)); - CASE(CTRL('L'), linenoiseEditRefresh(&l)); - CASE(CTRL('Z'), linenoiseEditSuspend(&l)); - CASE(CTRL('U'), linenoiseEditKillLeft(&l)); - CASE(CTRL('T'), linenoiseEditTranspose(&l)); - CASE(CTRL('K'), linenoiseEditKillRight(&l)); - CASE(CTRL('W'), linenoiseEditRuboutWord(&l)); - case CTRL('C'): - if (linenoiseRead(l.ifd, seq, sizeof(seq), &l) != 1) break; - switch (seq[0]) { - CASE(CTRL('C'), linenoiseEditInterrupt(&l)); - CASE(CTRL('B'), linenoiseEditBarf(&l)); - CASE(CTRL('S'), linenoiseEditSlurp(&l)); - default: - break; - } - break; - case CTRL('X'): - if (l.seq[1][0] == CTRL('X')) { - linenoiseEditGoto(&l); - } - break; - case CTRL('D'): - if (l.len) { - linenoiseEditDelete(&l); - } else { - free(history[--historylen]); - history[historylen] = 0; - free(l.buf); - return -1; - } - break; - case '\r': - l.final = 1; + char seq[16]; + + gotint = 0; + if (prompt && (!l->prompt || strcmp(prompt, l->prompt))) { + free(l->prompt); + l->prompt = strdup(prompt); + } + + switch (l->state) { + for (;;) { + DUFF_ROUTINE_READ(0); + HandleRead: + if (!rc && l->len) { + rc = 1; + seq[0] = '\r'; + seq[1] = 0; + } else if (!rc || rc == -1) { free(history[--historylen]); history[historylen] = 0; - linenoiseEditEnd(&l); - linenoiseRefreshLineForce(&l); - if ((p = realloc(l.buf, l.len + 1))) l.buf = p; - *obuf = l.buf; - return l.len; - case 033: - if (nread < 2) break; - switch (seq[1]) { - CASE('<', linenoiseEditBof(&l)); - CASE('>', linenoiseEditEof(&l)); - CASE('y', linenoiseEditRotate(&l)); - CASE('\\', linenoiseEditSqueeze(&l)); - CASE('b', linenoiseEditLeftWord(&l)); - CASE('f', linenoiseEditRightWord(&l)); - CASE('h', linenoiseEditRuboutWord(&l)); - CASE('d', linenoiseEditDeleteWord(&l)); - CASE('l', linenoiseEditLowercaseWord(&l)); - CASE('u', linenoiseEditUppercaseWord(&l)); - CASE('c', linenoiseEditCapitalizeWord(&l)); - CASE('t', linenoiseEditTransposeWords(&l)); - CASE(CTRL('B'), linenoiseEditLeftExpr(&l)); - CASE(CTRL('F'), linenoiseEditRightExpr(&l)); - CASE(CTRL('H'), linenoiseEditRuboutWord(&l)); - case '[': - if (nread < 3) break; - if (seq[2] >= '0' && seq[2] <= '9') { - if (nread < 4) break; - if (seq[3] == '~') { + linenoiseReset(l); + if (!rc) *obuf = 0; + return rc; + } + + // handle reverse history search + if (seq[0] == CTRL('R')) { + int fail, added, oldpos; + if (historylen <= 1) continue; + l->ab.len = 0; + l->olderpos = l->pos; + l->oldprompt = l->prompt; + l->oldindex = l->hindex; + l->prompt = 0; + for (fail = l->matlen = 0;;) { + free(l->prompt); + l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen); + DUFF_ROUTINE_READ(1); + fail = 1; + added = 0; + l->j = l->pos; + l->i = l->hindex; + if (rc > 0) { + if (seq[0] == CTRL('?') || seq[0] == CTRL('H')) { + if (l->ab.len) { + --l->ab.len; + l->matlen = MIN(l->matlen, l->ab.len); + } + } else if (seq[0] == CTRL('R')) { + if (l->j) { + --l->j; + } else if (l->i + 1 < historylen) { + ++l->i; + l->j = strlen(history[historylen - 1 - l->i]); + } + } 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; + } else { + abAppend(&l->ab, seq, rc); + added = rc; + } + } else { + break; + } + while (l->i < historylen) { + int k; + char *p; + const char *q; + p = history[historylen - 1 - l->i]; + k = strlen(p); + l->j = l->j >= 0 ? MIN(k, l->j + l->ab.len) : k; + if ((q = FindSubstringReverse(p, l->j, l->ab.b, l->ab.len))) { + linenoiseEditHistoryGoto(l, l->i); + l->pos = q - p; + fail = 0; + if (added) { + l->matlen += added; + added = 0; + } + break; + } else { + l->i = l->i + 1; + l->j = -1; + } + } + } + free(l->prompt); + l->prompt = l->oldprompt; + l->oldprompt = 0; + linenoiseRefreshLine(l); + goto HandleRead; + } + + // handle tab and tab-tab completion + if (seq[0] == '\t' && completionCallback) { + size_t i, j, k, n, m, perline, itemlen; + // 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); + completionCallback(s, &l->lc); + free(s); + } + m = GetCommonPrefixLength(&l->lc); + if (m > j - i || (m == j - i && l->lc.len == 1)) { + // on common prefix (or single completion) we complete and return + n = i + m + (l->len - j); + if (linenoiseGrow(l, n + 1)) { + memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1); + memcpy(l->buf + i, l->lc.cvec[0], m); + l->len = l->pos = n; + } + continue; + } + if (l->lc.len > 1) { + // 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 + for (i = 0; i < l->lc.len; ++i) { + char *s = l->lc.cvec[i]; + l->lc.cvec[i] = VisualizeControlCodes(s, -1, 0); + free(s); + } + for (;;) { + DUFF_ROUTINE_READ(3); + if (rc == 1 && seq[0] == '\t') { + struct abuf ab; + 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]); + for (k = n; k < itemlen; ++k) { + abAppendw(&ab, ' '); + } + } + abAppendw(&ab, READ16LE("\r\n")); + } + ab.len -= 2; + abAppends(&ab, "\n"); + linenoiseWriteStr(l->ofd, ab.b); + linenoiseRefreshLine(l); + abFree(&ab); + } else { + goto HandleRead; + } + } + } + } + + // handle (1) emacs keyboard combos + // (2) otherwise sigint exit + if (seq[0] == CTRL('C')) { + DUFF_ROUTINE_READ(4); + if (rc == 1) { + switch (seq[0]) { + CASE(CTRL('C'), linenoiseEditInterrupt(l)); + CASE(CTRL('B'), linenoiseEditBarf(l)); + CASE(CTRL('S'), linenoiseEditSlurp(l)); + default: + goto HandleRead; + } + continue; + } else { + goto HandleRead; + } + } + + // handle (1) unpausing terminal after ctrl-s + // (2) otherwise raw keystroke inserts + if (seq[0] == CTRL('Q')) { + if (ispaused) { + linenoiseUnpause(l->ofd); + } else { + DUFF_ROUTINE_READ(5); + if (rc > 0) { + char esc[sizeof(seq) * 4]; + size_t m = linenoiseEscape(esc, seq, rc); + linenoiseEditInsert(l, esc, m); + } else { + goto HandleRead; + } + } + continue; + } + + // handle keystrokes that don't need read() + switch (seq[0]) { + CASE(CTRL('P'), linenoiseEditUp(l)); + CASE(CTRL('E'), linenoiseEditEnd(l)); + CASE(CTRL('N'), linenoiseEditDown(l)); + CASE(CTRL('A'), linenoiseEditHome(l)); + CASE(CTRL('B'), linenoiseEditLeft(l)); + CASE(CTRL('@'), linenoiseEditMark(l)); + CASE(CTRL('Y'), linenoiseEditYank(l)); + CASE(CTRL('F'), linenoiseEditRight(l)); + CASE(CTRL('\\'), linenoiseEditQuit(l)); + CASE(CTRL('S'), linenoiseEditPause(l)); + CASE(CTRL('?'), linenoiseEditRubout(l)); + CASE(CTRL('H'), linenoiseEditRubout(l)); + CASE(CTRL('L'), linenoiseEditRefresh(l)); + CASE(CTRL('Z'), linenoiseEditSuspend(l)); + CASE(CTRL('U'), linenoiseEditKillLeft(l)); + CASE(CTRL('T'), linenoiseEditTranspose(l)); + CASE(CTRL('K'), linenoiseEditKillRight(l)); + CASE(CTRL('W'), linenoiseEditRuboutWord(l)); + + case CTRL('X'): + if (l->seq[1][0] == CTRL('X')) { + linenoiseEditGoto(l); + } + break; + + case CTRL('D'): + if (l->len) { + linenoiseEditDelete(l); + } else { + free(history[--historylen]); + history[historylen] = 0; + linenoiseReset(l); + *obuf = 0; + return 0; + } + 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; + switch (seq[1]) { + CASE('<', linenoiseEditBof(l)); + CASE('>', linenoiseEditEof(l)); + CASE('y', linenoiseEditRotate(l)); + CASE('\\', linenoiseEditSqueeze(l)); + CASE('b', linenoiseEditLeftWord(l)); + CASE('f', linenoiseEditRightWord(l)); + CASE('h', linenoiseEditRuboutWord(l)); + CASE('d', linenoiseEditDeleteWord(l)); + CASE('l', linenoiseEditLowercaseWord(l)); + CASE('u', linenoiseEditUppercaseWord(l)); + CASE('c', linenoiseEditCapitalizeWord(l)); + CASE('t', linenoiseEditTransposeWords(l)); + CASE(CTRL('B'), linenoiseEditLeftExpr(l)); + CASE(CTRL('F'), linenoiseEditRightExpr(l)); + CASE(CTRL('H'), linenoiseEditRuboutWord(l)); + + case '[': + // handle ansi csi sequences + if (rc < 3) break; + if (seq[2] >= '0' && seq[2] <= '9') { + if (rc < 4) break; + if (seq[3] == '~') { + switch (seq[2]) { + CASE('1', linenoiseEditHome(l)); // \e[1~ + CASE('3', linenoiseEditDelete(l)); // \e[3~ + CASE('4', linenoiseEditEnd(l)); // \e[4~ + default: + break; + } + } + } else { switch (seq[2]) { - CASE('1', linenoiseEditHome(&l)); /* \e[1~ */ - CASE('3', linenoiseEditDelete(&l)); /* \e[3~ */ - CASE('4', linenoiseEditEnd(&l)); /* \e[4~ */ + CASE('A', linenoiseEditUp(l)); + CASE('B', linenoiseEditDown(l)); + CASE('C', linenoiseEditRight(l)); + CASE('D', linenoiseEditLeft(l)); + CASE('H', linenoiseEditHome(l)); + CASE('F', linenoiseEditEnd(l)); default: break; } } - } else { + break; + + case 'O': + if (rc < 3) break; switch (seq[2]) { - CASE('A', linenoiseEditUp(&l)); - CASE('B', linenoiseEditDown(&l)); - CASE('C', linenoiseEditRight(&l)); - CASE('D', linenoiseEditLeft(&l)); - CASE('H', linenoiseEditHome(&l)); - CASE('F', linenoiseEditEnd(&l)); + CASE('A', linenoiseEditUp(l)); + CASE('B', linenoiseEditDown(l)); + CASE('C', linenoiseEditRight(l)); + CASE('D', linenoiseEditLeft(l)); + CASE('H', linenoiseEditHome(l)); + CASE('F', linenoiseEditEnd(l)); default: break; } - } - break; - case 'O': - if (nread < 3) break; - switch (seq[2]) { - CASE('A', linenoiseEditUp(&l)); - CASE('B', linenoiseEditDown(&l)); - CASE('C', linenoiseEditRight(&l)); - CASE('D', linenoiseEditLeft(&l)); - CASE('H', linenoiseEditHome(&l)); - CASE('F', linenoiseEditEnd(&l)); - default: - break; - } - break; - case 033: - if (nread < 3) break; - switch (seq[2]) { - case '[': - if (nread < 4) break; - switch (seq[3]) { - CASE('C', linenoiseEditRightExpr(&l)); /* \e\e[C alt-right */ - CASE('D', linenoiseEditLeftExpr(&l)); /* \e\e[D alt-left */ - default: - break; - } - break; - case 'O': - if (nread < 4) break; - switch (seq[3]) { - CASE('C', linenoiseEditRightExpr(&l)); /* \e\eOC alt-right */ - CASE('D', linenoiseEditLeftExpr(&l)); /* \e\eOD alt-left */ - default: - break; - } - break; - default: - break; - } - break; - default: - break; - } - break; - default: - if (!iswcntrl(seq[0])) { /* only sees canonical c0 */ - if (xlatCallback) { - rune = GetUtf8(seq, nread); - w = tpenc(xlatCallback(rune.c)); - nread = 0; - do { - seq[nread++] = w; - } while ((w >>= 8)); + break; + + case '\e': + if (rc < 3) break; + switch (seq[2]) { + case '[': + if (rc < 4) break; + switch (seq[3]) { + CASE('C', linenoiseEditRightExpr(l)); // \e\e[C alt-right + CASE('D', linenoiseEditLeftExpr(l)); // \e\e[D alt-left + default: + break; + } + break; + case 'O': + if (rc < 4) break; + switch (seq[3]) { + CASE('C', linenoiseEditRightExpr(l)); // \e\eOC alt-right + CASE('D', linenoiseEditLeftExpr(l)); // \e\eOD alt-left + default: + break; + } + break; + default: + break; + } + break; + default: + break; } - linenoiseEditInsert(&l, seq, nread); - } - break; + break; + + default: + // handle normal keystrokes + if (!iswcntrl(seq[0])) { // only sees canonical c0 + if (xlatCallback) { + uint64_t w; + struct rune rune; + rune = GetUtf8(seq, rc); + w = tpenc(xlatCallback(rune.c)); + rc = 0; + do { + seq[rc++] = w; + } while ((w >>= 8)); + } + linenoiseEditInsert(l, seq, rc); + } + break; + } } + default: + unreachable; } } @@ -2142,12 +2216,6 @@ void linenoiseHistoryFree(void) { historylen = 0; } -static void linenoiseAtExit(void) { - linenoiseDisableRawMode(); - linenoiseHistoryFree(); - linenoiseRingFree(); -} - int linenoiseHistoryAdd(const char *line) { char *linecopy; if (!LINENOISE_MAX_HISTORY) return 0; @@ -2241,6 +2309,33 @@ int linenoiseHistoryLoad(const char *filename) { return rc; } +/** + * Returns appropriate system config location. + * @return path needing free or null if prog is null + */ +char *linenoiseGetHistoryPath(const char *prog) { + struct abuf path; + const char *a, *b; + if (!prog) return 0; + if (strchr(prog, '/') || strchr(prog, '.')) return strdup(prog); + abInit(&path); + b = ""; + if (!(a = getenv("HOME"))) { + if (!(a = getenv("HOMEDRIVE")) || !(b = getenv("HOMEPATH"))) { + a = ""; + } + } + if (*a) { + abAppends(&path, a); + abAppends(&path, b); + abAppendw(&path, '/'); + } + abAppendw(&path, '.'); + abAppends(&path, prog); + abAppends(&path, "_history"); + return path.b; +} + /** * Reads line interactively. * @@ -2253,24 +2348,24 @@ int linenoiseHistoryLoad(const char *filename) { char *linenoiseRaw(const char *prompt, int infd, int outfd) { char *buf; ssize_t rc; - static char once; struct sigaction sa[3]; - if (!once) atexit(linenoiseAtExit), once = 1; - if (enableRawMode(infd) == -1) return 0; - buf = 0; - gotint = 0; + struct linenoiseState *l; + if (linenoiseEnableRawMode(infd) == -1) return 0; sigemptyset(&sa->sa_mask); sa->sa_flags = SA_NODEFER; sa->sa_handler = linenoiseOnInt; sigaction(SIGINT, sa, sa + 1); sigaction(SIGQUIT, sa, sa + 2); - rc = linenoiseEdit(infd, outfd, prompt, &buf); + l = linenoiseBegin(prompt, infd, outfd); + rc = linenoiseEdit(l, 0, &buf, true); + linenoiseEnd(l); linenoiseDisableRawMode(); sigaction(SIGQUIT, sa + 2, 0); sigaction(SIGINT, sa + 1, 0); if (gotint) { - free(buf); - buf = 0; + if (rc != -1) { + free(buf); + } raise(gotint); errno = EINTR; gotint = 0; @@ -2280,7 +2375,6 @@ char *linenoiseRaw(const char *prompt, int infd, int outfd) { linenoiseWriteStr(outfd, "\n"); return buf; } else { - free(buf); return 0; } } @@ -2298,7 +2392,7 @@ static int linenoiseFallback(const char *prompt, char **res) { fputs(prompt, stdout); fflush(stdout); } - *res = GetLine(stdin); + *res = linenoiseGetLine(stdin); return 1; } else { return 0; @@ -2355,49 +2449,35 @@ char *linenoise(const char *prompt) { * noting that on eof your errno is not changed */ char *linenoiseWithHistory(const char *prompt, const char *prog) { - char *line, *res; - struct abuf path; - const char *a, *b; + char *path, *line, *res; if (linenoiseFallback(prompt, &res)) return res; fflush(stdout); - abInit(&path); - if (prog) { - if (strchr(prog, '/') || strchr(prog, '.')) { - abAppends(&path, prog); - } else { - b = ""; - if (!(a = getenv("HOME"))) { - if (!(a = getenv("HOMEDRIVE")) || !(b = getenv("HOMEPATH"))) { - a = ""; - } - } - if (*a) { - abAppends(&path, a); - abAppends(&path, b); - abAppendw(&path, '/'); - } - abAppendw(&path, '.'); - abAppends(&path, prog); - abAppends(&path, "_history"); - } - } - if (path.len) { - if (linenoiseHistoryLoad(path.b) == -1) { + if ((path = linenoiseGetHistoryPath(prog))) { + if (linenoiseHistoryLoad(path) == -1) { kprintf("%r%s: failed to load history: %m%n", path); + free(path); + path = 0; } } line = linenoise(prompt); - if (path.len && line && *line) { + if (path && line && *line) { /* history here is inefficient but helpful when the user has multiple * repls open at the same time, so history propagates between them */ - linenoiseHistoryLoad(path.b); + linenoiseHistoryLoad(path); linenoiseHistoryAdd(line); - linenoiseHistorySave(path.b); + linenoiseHistorySave(path); } - abFree(&path); + free(path); return line; } +/** + * Returns 0 otherwise SIGINT or SIGQUIT if interrupt was received. + */ +int linenoiseGetInterrupt(void) { + return gotint; +} + /** * Registers tab completion callback. */ @@ -2432,13 +2512,6 @@ void linenoiseSetXlatCallback(linenoiseXlatCallback *fn) { xlatCallback = fn; } -/** - * Sets terminal fd pollin callback. - */ -void linenoiseSetPollCallback(linenoisePollCallback *fn) { - pollCallback = fn; -} - /** * Adds completion. * @@ -2466,8 +2539,14 @@ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { */ void linenoiseFreeCompletions(linenoiseCompletions *lc) { size_t i; - for (i = 0; i < lc->len; i++) free(lc->cvec[i]); - if (lc->cvec) free(lc->cvec); + if (lc->cvec) { + for (i = 0; i < lc->len; i++) { + free(lc->cvec[i]); + } + free(lc->cvec); + } + lc->cvec = 0; + lc->len = 0; } /** @@ -2490,3 +2569,17 @@ void linenoiseMaskModeEnable(void) { void linenoiseMaskModeDisable(void) { maskmode = 0; } + +static void linenoiseAtExit(void) { + linenoiseDisableRawMode(); + linenoiseHistoryFree(); + linenoiseRingFree(); +} + +static textstartup void linenoiseInit() { + atexit(linenoiseAtExit); +} + +const void *const linenoiseCtor[] initarray = { + linenoiseInit, +}; diff --git a/third_party/linenoise/linenoise.h b/third_party/linenoise/linenoise.h index dd8cf0cfe..49726bc49 100644 --- a/third_party/linenoise/linenoise.h +++ b/third_party/linenoise/linenoise.h @@ -1,8 +1,11 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_LINENOISE_LINENOISE_H_ #define COSMOPOLITAN_THIRD_PARTY_LINENOISE_LINENOISE_H_ +#include "libc/calls/struct/winsize.h" +#include "libc/stdio/stdio.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +struct linenoiseState; typedef struct linenoiseCompletions { size_t len; char **cvec; @@ -13,17 +16,14 @@ typedef char *(linenoiseHintsCallback)(const char *, const char **, const char **); typedef void(linenoiseFreeHintsCallback)(void *); typedef wint_t(linenoiseXlatCallback)(wint_t); -typedef int(linenoisePollCallback)(int, int); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseSetHintsCallback(linenoiseHintsCallback *); void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); void linenoiseSetXlatCallback(linenoiseXlatCallback *); -void linenoiseSetPollCallback(linenoisePollCallback *); char *linenoise(const char *) dontdiscard; -char *linenoiseRaw(const char *, int, int) dontdiscard; char *linenoiseWithHistory(const char *, const char *) dontdiscard; int linenoiseHistoryAdd(const char *); int linenoiseHistorySave(const char *); @@ -33,8 +33,22 @@ void linenoiseHistoryFree(void); void linenoiseClearScreen(int); void linenoiseMaskModeEnable(void); void linenoiseMaskModeDisable(void); +int linenoiseEnableRawMode(int); void linenoiseDisableRawMode(void); void linenoiseFree(void *); +int linenoiseIsTerminal(void); +int linenoiseIsTeletype(void); + +char *linenoiseGetHistoryPath(const char *); +struct linenoiseState *linenoiseBegin(const char *, int, int); +ssize_t linenoiseEdit(struct linenoiseState *, const char *, char **, bool); +int linenoiseGetInterrupt(void); +void linenoiseEnd(struct linenoiseState *); + +char *linenoiseGetLine(FILE *); +struct winsize linenoiseGetTerminalSize(struct winsize, int, int); +void linenoiseRefreshLine(struct linenoiseState *); +char *linenoiseRaw(const char *, int, int) dontdiscard; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/third_party/linenoise/linenoise.mk b/third_party/linenoise/linenoise.mk index b19eb14d4..016c7ff31 100644 --- a/third_party/linenoise/linenoise.mk +++ b/third_party/linenoise/linenoise.mk @@ -25,6 +25,7 @@ THIRD_PARTY_LINENOISE_A_DIRECTDEPS = \ LIBC_SOCK \ LIBC_STDIO \ LIBC_RUNTIME \ + LIBC_LOG \ LIBC_SYSV_CALLS \ LIBC_STR \ LIBC_UNICODE \ diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 3af46ec6f..4b446c088 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -1,6 +1,9 @@ #define lua_c #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/nomultics.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/log/check.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" @@ -13,8 +16,13 @@ #include "third_party/lua/lualib.h" // clang-format off +bool lua_repl_blocking; +bool lua_repl_isterminal; +_Alignas(64) char lualock; +struct linenoiseState *lua_repl_linenoise; static lua_State *globalL; static const char *g_progname; +static const char *g_historypath; /* ** {================================================================== @@ -111,14 +119,35 @@ static int incomplete (lua_State *L, int status) { /* ** Prompt the user, read a line, and push it into the Lua stack. */ -static int pushline (lua_State *L, int firstline) { +static ssize_t pushline (lua_State *L, int firstline) { char *b; size_t l; + ssize_t rc; + char *prmt; globalL = L; - const char *prmt = get_prompt(L, firstline); - if (!(b = linenoiseWithHistory(prmt, g_progname))) - return 0; /* no input (prompt will be popped by caller) */ - lua_pop(L, 1); /* remove prompt */ + if (lua_repl_isterminal) { + prmt = strdup(get_prompt(L, firstline)); + lua_pop(L, 1); /* remove prompt */ + _spunlock(&lualock); + rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); + free(prmt); + if (rc != -1) { + if (b && *b) { + linenoiseHistoryLoad(g_historypath); + linenoiseHistoryAdd(b); + linenoiseHistorySave(g_historypath); + } + } + _spinlock(&lualock); + } else { + _spunlock(&lualock); + b = linenoiseGetLine(stdin); + _spinlock(&lualock); + rc = b ? 1 : -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 */ @@ -164,9 +193,10 @@ static void lstop (lua_State *L, lua_Debug *ar) { static int multiline (lua_State *L) { for (;;) { /* repeat until gets a complete statement */ size_t len; + ssize_t rc; const char *line = lua_tolstring(L, 1, &len); /* get what it has */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ - if (!incomplete(L, status) || !pushline(L, 0)) { + if (!incomplete(L, status) || pushline(L, 0) != 1) { return status; /* cannot or should not try to add continuation line */ } lua_pushliteral(L, "\n"); /* add newline... */ @@ -176,11 +206,38 @@ static int multiline (lua_State *L) { } -void lua_initrepl(const char *progname) { +void lua_initrepl(lua_State *L, const char *progname) { + const char *prompt; + _spinlock(&lualock); g_progname = progname; - linenoiseSetCompletionCallback(lua_readline_completions); - linenoiseSetHintsCallback(lua_readline_hint); - linenoiseSetFreeHintsCallback(free); + if ((lua_repl_isterminal = linenoiseIsTerminal())) { + linenoiseSetCompletionCallback(lua_readline_completions); + linenoiseSetHintsCallback(lua_readline_hint); + linenoiseSetFreeHintsCallback(free); + 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); + free(g_historypath); + g_historypath = 0; + } + } + lua_repl_linenoise = linenoiseBegin(prompt, 0, 1); + lua_pop(L, 1); /* remove prompt */ + __nomultics = 2; + __replmode = true; + } + _spunlock(&lualock); +} + + +void lua_freerepl(void) { + _spinlock(&lualock); + __nomultics = false; + __replmode = false; + linenoiseEnd(lua_repl_linenoise); + free(g_historypath); + _spunlock(&lualock); } @@ -189,16 +246,24 @@ void lua_initrepl(const char *progname) { ** adding "return " in front of it) and second as a statement. Return ** the final status of load/call with the resulting function (if any) ** in the top of the stack. +** +** returns -1 on eof +** returns -2 on error */ int lua_loadline (lua_State *L) { + ssize_t rc; int status; lua_settop(L, 0); - if (!pushline(L, 1)) - return -1; /* no input */ + _spinlock(&lualock); + if ((rc = pushline(L, 1)) != 1) { + _spunlock(&lualock); + 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); return status; } diff --git a/third_party/lua/lrepl.h b/third_party/lua/lrepl.h index 2ac8d6629..f1d6ac11c 100644 --- a/third_party/lua/lrepl.h +++ b/third_party/lua/lrepl.h @@ -5,9 +5,15 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +extern char lualock; +extern bool lua_repl_blocking; +extern bool lua_repl_isterminal; +extern struct linenoiseState *lua_repl_linenoise; + +void lua_freerepl(void); int lua_loadline(lua_State *); void lua_l_print(lua_State *); -void lua_initrepl(const char *); +void lua_initrepl(lua_State *, const char *); int lua_report(lua_State *, int); int lua_runchunk(lua_State *, int, int); void lua_l_message(const char *, const char *); diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 22d91550e..876d78ec7 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -10,14 +10,19 @@ #include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/log/log.h" #include "libc/runtime/gc.h" #include "libc/runtime/stack.h" +#include "libc/sock/sock.h" #include "libc/str/str.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/poll.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" @@ -275,8 +280,27 @@ static void doREPL (lua_State *L) { int status; const char *oldprogname = progname; progname = NULL; /* no 'progname' on errors in interactive mode */ - lua_initrepl(LUA_PROGNAME); - while ((status = lua_loadline(L)) != -1) { + lua_initrepl(L, LUA_PROGNAME); + for (;;) { + linenoiseEnableRawMode(0); + TryAgain: + status = lua_loadline(L); + if (status == -2 && errno == EAGAIN) { + errno = 0; + poll(&(struct pollfd){0, POLLIN}, 1, -1); + goto TryAgain; + } + linenoiseDisableRawMode(); + if (status == -1) { + break; + } else if (status == -2) { + lua_pushfstring(L, "read error: %s", strerror(errno)); + lua_report(L, status); + lua_freerepl(); + progname = oldprogname; + return; + } + lua_writeline(); if (status == LUA_OK) status = lua_runchunk(L, 0, LUA_MULTRET); if (status == LUA_OK) { @@ -285,6 +309,7 @@ static void doREPL (lua_State *L) { lua_report(L, status); } } + lua_freerepl(); lua_settop(L, 0); /* clear stack */ lua_writeline(); progname = oldprogname; @@ -343,7 +368,7 @@ static int pmain (lua_State *L) { int main (int argc, char **argv) { int status, result; lua_State *L; - if (!IsModeDbg()) { + if (IsModeDbg()) { ShowCrashReports(); } /* if (IsModeDbg()) ShowCrashReports(); */ diff --git a/tool/build/ar.c b/tool/build/ar.c index 875935e6a..2f62f31eb 100644 --- a/tool/build/ar.c +++ b/tool/build/ar.c @@ -103,22 +103,21 @@ static void AppendArg(struct Args *l, char *s) { static void MakeHeader(struct Header *h, const char *name, int ref, int mode, int size) { size_t n; - char buf[24]; memset(h, ' ', sizeof(*h)); n = strlen(name); memcpy(h->name, name, n); if (ref != -1) { - memcpy(h->name + n, buf, uint64toarray_radix10(ref, buf)); + FormatUint32(h->name + n, ref); } if (strcmp(name, "//") != 0) { h->date[0] = '0'; h->uid[0] = '0'; h->gid[0] = '0'; - memcpy(h->mode, buf, uint64toarray_radix8(mode & 0777, buf)); + FormatOctal32(h->mode, mode & 0777, false); } h->fmag[0] = '`'; h->fmag[1] = '\n'; - memcpy(h->size, buf, uint64toarray_radix10(size, buf)); + FormatUint32(h->size, size); } int main(int argc, char *argv[]) { diff --git a/tool/build/blinkenlights.c b/tool/build/blinkenlights.c index a6640d41d..620ddae04 100644 --- a/tool/build/blinkenlights.c +++ b/tool/build/blinkenlights.c @@ -1271,7 +1271,7 @@ static void DrawXmm(struct Panel *p, long i, long r) { uint64toarray_fixed16(ival, buf, xmmtype.size[r] * 8); } } else { - int64toarray_radix10(SignExtend(ival, xmmtype.size[r] * 8), buf); + FormatInt64(buf, SignExtend(ival, xmmtype.size[r] * 8)); } break; default: @@ -2270,7 +2270,8 @@ 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/lib/cga.c b/tool/build/lib/cga.c index 74b1b914c..016b641ab 100644 --- a/tool/build/lib/cga.c +++ b/tool/build/lib/cga.c @@ -30,9 +30,9 @@ size_t FormatCga(uint8_t bgfg, char buf[hasatleast 11]) { char *p = buf; *p++ = '\e'; *p++ = '['; - p += uint64toarray_radix10(kCgaToAnsi[(bgfg & 0xF0) >> 4] + 10, p); + p = FormatUint32(p, kCgaToAnsi[(bgfg & 0xF0) >> 4] + 10); *p++ = ';'; - p += uint64toarray_radix10(kCgaToAnsi[bgfg & 0x0F], p); + p = FormatUint32(p, kCgaToAnsi[bgfg & 0x0F]); *p++ = 'm'; *p = '\0'; return p - buf; diff --git a/tool/build/lib/disarg.c b/tool/build/lib/disarg.c index a5e68581b..6c56d5e43 100644 --- a/tool/build/lib/disarg.c +++ b/tool/build/lib/disarg.c @@ -128,7 +128,7 @@ static char *DisRegisterWord(struct Dis *d, uint32_t rde, char *p, bool g, static char *DisInt(char *p, int64_t x) { if (-15 <= x && x <= 15) { - p += int64toarray_radix10(x, p); + p = FormatInt64(p, x); } else if (x == INT64_MIN) { p = stpcpy(p, "-0x"); p += uint64toarray_radix16(INT64_MIN, p); @@ -319,7 +319,8 @@ static char *DisRegMem(struct Dis *d, uint32_t rde, char *p, } static dontinline char *DisE(struct Dis *d, uint32_t rde, char *p, - char *f(struct Dis *, uint32_t, char *, bool, int)) { + char *f(struct Dis *, uint32_t, char *, bool, + int)) { if (IsModrmRegister(rde)) { return f(d, rde, p, Rexb(rde), ModrmRm(rde)); } else { @@ -471,13 +472,13 @@ static char *DisOne(struct Dis *d, uint32_t rde, char *p) { static char *DisJbs(struct Dis *d, uint32_t rde, char *p) { if (d->xedd->op.disp > 0) *p++ = '+'; - p += int64toarray_radix10(d->xedd->op.disp, p); + p = FormatInt64(p, d->xedd->op.disp); return p; } static char *DisJb(struct Dis *d, uint32_t rde, char *p) { if (d->xedd->op.disp > 0) *p++ = '+'; - p += uint64toarray_radix10(d->xedd->op.disp & 0xff, p); + p = FormatUint32(p, d->xedd->op.disp & 0xff); return p; } @@ -522,7 +523,7 @@ static char *DisXmm(struct Dis *d, uint32_t rde, char *p, const char *s, p = HighStart(p, g_high.reg); *p++ = '%'; p = stpcpy(p, s); - p += uint64toarray_radix10(reg, p); + p = FormatUint32(p, reg); p = HighEnd(p); return p; } diff --git a/tool/build/lib/high.c b/tool/build/lib/high.c index ef875d260..f81801223 100644 --- a/tool/build/lib/high.c +++ b/tool/build/lib/high.c @@ -25,7 +25,7 @@ struct High g_high; char *HighStart(char *p, int h) { if (h) { p = stpcpy(p, "\e[38;5;"); - p += uint64toarray_radix10(h, p); + p = FormatUint32(p, h); p = stpcpy(p, "m"); g_high.active = true; } diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index 30f4448fb..da27b8099 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -693,9 +693,9 @@ static void PtyReportCursorPosition(struct Pty *pty) { p = buf; *p++ = '\e'; *p++ = '['; - p += uint64toarray_radix10((pty->y + 1) & 0x7fff, p); + p = FormatInt32(p, (pty->y + 1) & 0x7fff); *p++ = ';'; - p += uint64toarray_radix10((pty->x + 1) & 0x7fff, p); + p = FormatInt32(p, (pty->x + 1) & 0x7fff); *p++ = 'R'; PtyWriteInput(pty, buf, p - buf); } @@ -1178,7 +1178,8 @@ ssize_t PtyWriteInput(struct Pty *pty, const void *data, size_t n) { m = pty->input.n; if (i + n * 2 + 1 > m) { m = MAX(m, 8); - do m += m >> 1; + do + m += m >> 1; while (i + n * 2 + 1 > m); if (!(p = realloc(p, m))) { return -1; @@ -1229,18 +1230,18 @@ ssize_t PtyRead(struct Pty *pty, void *buf, size_t size) { static char *PtyEncodeRgb(char *p, int rgb) { *p++ = '2'; *p++ = ';'; - p += uint64toarray_radix10((rgb & 0x0000ff) >> 000, p); + p = FormatUint32(p, (rgb & 0x0000ff) >> 000); *p++ = ';'; - p += uint64toarray_radix10((rgb & 0x00ff00) >> 010, p); + p = FormatUint32(p, (rgb & 0x00ff00) >> 010); *p++ = ';'; - p += uint64toarray_radix10((rgb & 0xff0000) >> 020, p); + p = FormatUint32(p, (rgb & 0xff0000) >> 020); return p; } static char *PtyEncodeXterm256(char *p, int xt) { *p++ = '5'; *p++ = ';'; - p += uint64toarray_radix10(xt, p); + p = FormatUint32(p, xt); return p; } diff --git a/tool/build/strace.c b/tool/build/strace.c index f92b4c10c..c7a003eed 100644 --- a/tool/build/strace.c +++ b/tool/build/strace.c @@ -661,7 +661,7 @@ static const char *GetErrnoName(int x) { const char *s; static char buf[16]; if ((s = strerror_short(x))) return s; - int64toarray_radix10(x, buf); + FormatInt64(buf, x); return buf; } diff --git a/tool/lambda/lib/debug.c b/tool/lambda/lib/debug.c index 52764cc6b..52d230e85 100644 --- a/tool/lambda/lib/debug.c +++ b/tool/lambda/lib/debug.c @@ -58,7 +58,7 @@ void PrintClosure(struct Closure *c, const char *name, int indent, FILE *f) { fputs(": ", f); Print(c->term, 0, GetDepth(c->envp), f); fputs(" +", f); - int64toarray_radix10(c->refs, ibuf); + FormatInt64(ibuf, c->refs); fputs(ibuf, f); fputc('\n', f); PrintClosure(c->envp, "envp", indent + 1, f); diff --git a/tool/lambda/lib/print.c b/tool/lambda/lib/print.c index 2dae71695..a8a9625b8 100644 --- a/tool/lambda/lib/print.c +++ b/tool/lambda/lib/print.c @@ -632,7 +632,7 @@ void PrintVar(int i, FILE* f) { char ibuf[22]; switch (style) { case 0: - int64toarray_radix10(i, ibuf); + FormatInt64(ibuf, i); fputs(ibuf, f); break; case 1: @@ -642,7 +642,7 @@ void PrintVar(int i, FILE* f) { fputwc(FREEBIES[~i], f); } else { ibuf[0] = '?'; - int64toarray_radix10(i, ibuf + 1); + FormatInt64(ibuf + 1, i); fputs(ibuf, f); } break; @@ -889,7 +889,7 @@ void PrintDebruijn(int x, int head, int depth, FILE* f) { PrintVar(mem[x + 1], f); } else { fputc(L'‼', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } } else if (mem[x] == IOP) { @@ -905,19 +905,19 @@ void PrintDebruijn(int x, int head, int depth, FILE* f) { fputs(asciiname ? "gro" : "⋯", f); } else { fputc(L'!', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } } else { fputc(L'!', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } return; } Overflow: fputc(L'‼', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } @@ -1172,7 +1172,7 @@ void PrintLambda(int x, int head, int depth, int apps, FILE* f) { PrintVar(depth - 1 - mem[x + 1], f); } else { fputc(L'‼', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } } else if (mem[x] == APP) { @@ -1198,12 +1198,12 @@ void PrintLambda(int x, int head, int depth, int apps, FILE* f) { fputs(asciiname ? "gro" : "⋯", f); } else { fputc(L'!', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } } else { fputc(L'!', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } if (close) { @@ -1213,7 +1213,7 @@ void PrintLambda(int x, int head, int depth, int apps, FILE* f) { } Overflow: fputc(L'‼', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } @@ -1239,7 +1239,7 @@ void PrintBinary(int x, int head, int depth, FILE* f) { PrintVar(mem[x + 1], f); } else { fputc(L'‼', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } } else if (mem[x] == APP) { @@ -1263,14 +1263,14 @@ void PrintBinary(int x, int head, int depth, FILE* f) { fputwc(L'⋯', f); } else { fputc(L'!', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } return; } Overflow: fputc(L'‼', f); - int64toarray_radix10(x, ibuf); + FormatInt64(ibuf, x); fputs(ibuf, f); } diff --git a/tool/net/demo/.init.lua b/tool/net/demo/.init.lua index acb360df4..70c86319e 100644 --- a/tool/net/demo/.init.lua +++ b/tool/net/demo/.init.lua @@ -20,17 +20,17 @@ db:exec[[ INSERT INTO test (content) VALUES ('Hello Sqlite3'); ]] --- this intercepts all requests if it's defined -function OnHttpRequest() - if HasParam('magic') then - Write('

\r\n') - Write('OnHttpRequest() has intercepted your request
\r\n') - Write('because you specified the magic parameter\r\n') - Write('

\r\n')
-      Write(EscapeHtml(LoadAsset('/.init.lua')))
-      Write('
\r\n') - else - Route() -- this asks redbean to do the default thing - end - SetHeader('Server', 'redbean!') -end +-- -- this intercepts all requests if it's defined +-- function OnHttpRequest() +-- if HasParam('magic') then +-- Write('

\r\n') +-- Write('OnHttpRequest() has intercepted your request
\r\n') +-- Write('because you specified the magic parameter\r\n') +-- Write('

\r\n')
+--       Write(EscapeHtml(LoadAsset('/.init.lua')))
+--       Write('
\r\n') +-- else +-- Route() -- this asks redbean to do the default thing +-- end +-- SetHeader('Server', 'redbean!') +-- end diff --git a/tool/net/net.mk b/tool/net/net.mk index 1d1c39e8f..edede4163 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -90,7 +90,7 @@ 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/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ @@ -208,7 +208,7 @@ 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/lre.o \ o/$(MODE)/tool/net/lunix.o \ o/$(MODE)/tool/net/lmaxmind.o \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 7374d4954..ca65139e0 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -39,6 +39,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/nomultics.internal.h" +#include "libc/intrin/spinlock.h" #include "libc/log/backtrace.internal.h" #include "libc/log/check.h" #include "libc/log/log.h" @@ -50,10 +51,12 @@ #include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" +#include "libc/nexgen32e/nt2sysv.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtscp.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/runtime.h" +#include "libc/nt/thread.h" #include "libc/rand/rand.h" #include "libc/runtime/clktck.h" #include "libc/runtime/directmap.internal.h" @@ -362,6 +365,7 @@ static bool printport; static bool daemonize; static bool logrusage; static bool logbodies; +static bool isterminal; static bool sslcliused; static bool loglatency; static bool terminated; @@ -835,12 +839,13 @@ static void DescribeAddress(char buf[40], uint32_t addr, uint16_t port) { char *p; const char *s; p = buf; - p += uint64toarray_radix10((addr & 0xFF000000) >> 030, p), *p++ = '.'; - p += uint64toarray_radix10((addr & 0x00FF0000) >> 020, p), *p++ = '.'; - p += uint64toarray_radix10((addr & 0x0000FF00) >> 010, p), *p++ = '.'; - p += uint64toarray_radix10((addr & 0x000000FF) >> 000, p), *p++ = ':'; - p += uint64toarray_radix10(port, p); - *p++ = '\0'; + p = FormatUint64(p, (addr & 0xFF000000) >> 030), *p++ = '.'; + p = FormatUint64(p, (addr & 0x00FF0000) >> 020), *p++ = '.'; + p = FormatUint64(p, (addr & 0x0000FF00) >> 010), *p++ = '.'; + p = FormatUint64(p, (addr & 0x000000FF) >> 000), *p++ = ':'; + p = FormatUint64(p, port); + *p = '\0'; + assert(p - buf < 40); } static inline void GetServerAddr(uint32_t *ip, uint16_t *port) { @@ -1029,7 +1034,7 @@ static void Daemonize(void) { umask(0); if (pidpath) { fd = open(pidpath, O_CREAT | O_WRONLY, 0644); - WRITE(fd, ibuf, uint64toarray_radix10(getpid(), ibuf)); + WRITE(fd, ibuf, FormatInt32(ibuf, getpid()) - ibuf); close(fd); } if (!logpath) ProgramLogPath("/dev/null"); @@ -1486,6 +1491,7 @@ static void CertsDestroy(void) { static void WipeServingKeys(void) { size_t i; + long double t = nowl(); if (uniprocess) return; mbedtls_ssl_ticket_free(&ssltick); mbedtls_ssl_key_cert_free(conf.key_cert), conf.key_cert = 0; @@ -2083,7 +2089,7 @@ static char *AppendExpires(char *p, int64_t t) { static char *AppendCache(char *p, int64_t seconds) { if (seconds < 0) return p; p = stpcpy(p, "Cache-Control: max-age="); - p += uint64toarray_radix10(seconds, p); + p = FormatUint64(p, seconds); if (seconds) { p = stpcpy(p, ", public"); } else { @@ -2095,21 +2101,21 @@ static char *AppendCache(char *p, int64_t seconds) { static inline char *AppendContentLength(char *p, size_t n) { p = stpcpy(p, "Content-Length: "); - p += uint64toarray_radix10(n, p); + p = FormatUint64(p, n); return AppendCrlf(p); } static char *AppendContentRange(char *p, long a, long b, long c) { p = stpcpy(p, "Content-Range: bytes "); if (a >= 0 && b > 0) { - p += uint64toarray_radix10(a, p); + p = FormatUint64(p, a); *p++ = '-'; - p += uint64toarray_radix10(a + b - 1, p); + p = FormatUint64(p, a + b - 1); } else { *p++ = '*'; } *p++ = '/'; - p += uint64toarray_radix10(c, p); + p = FormatUint64(p, c); return AppendCrlf(p); } @@ -6396,6 +6402,7 @@ static bool StreamResponse(char *p) { static bool HandleMessageActual(void) { int rc; char *p; + long double now; if ((rc = ParseHttpMessage(&msg, inbuf.p, amtread)) != -1) { if (!rc) return false; hdrsize = rc; @@ -6437,8 +6444,11 @@ static bool HandleMessageActual(void) { LockInc(&shared->c.messageshandled); ++messageshandled; if (loglatency || LOGGABLE(kLogDebug)) { - LOGF(kLogDebug, "(stat) %`'.*s latency %,ldµs", msg.uri.b - msg.uri.a, - inbuf.p + msg.uri.a, (long)((nowl() - startrequest) * 1e6L)); + now = nowl(); + LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs", + msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, + (long)((now - startrequest) * 1e6L), + (long)((now - startconnection) * 1e6L)); } if (!generator) { return TransmitResponse(p); @@ -6702,7 +6712,6 @@ static int EnableSandbox(void) { } } -// returns 0 otherwise -1 if worker needs to unwind stack and exit static int HandleConnection(size_t i) { int pid, rc = 0; clientaddrsize = sizeof(clientaddr); @@ -6731,10 +6740,16 @@ static int HandleConnection(size_t i) { __kbirth = rdtsc(); } if (funtrace) { - ftrace_install(); + if (ftrace_install() != -1) { + g_ftrace = 1; + } else { + WARNF("ftrace failed to install %m"); + } } } - CHECK_NE(-1, EnableSandbox()); + if (sandboxed) { + CHECK_NE(-1, EnableSandbox()); + } if (hasonworkerstart) { CallSimpleHook("OnWorkerStart"); } @@ -6824,45 +6839,6 @@ static int HandleConnection(size_t i) { return rc; } -// returns 2 if we should stay in the redbean event loop -// returns 1 if poll() says stdin has user input available -// returns 0 if poll() timed out after ms -// returns -1 if worker is unwinding exit -static int HandlePoll(int ms) { - size_t i; - int nfds; - if ((nfds = poll(polls, 1 + servers.n, ms)) != -1) { - for (i = 0; i < servers.n; ++i) { - if (polls[1 + i].revents) { - serveraddr = &servers.p[i].addr; - ishandlingconnection = true; - if (HandleConnection(i) == -1) return -1; - ishandlingconnection = false; - } - } - // are we polling stdin for the repl? - if (polls[0].fd >= 0) { - if (polls[0].revents) { - return 1; // user entered a keystroke - } else if (!nfds) { - return 0; // let linenoise know it timed out - } - } - } else { - if (errno == EINTR || errno == EAGAIN) { - LockInc(&shared->c.pollinterrupts); - } else if (errno == ENOMEM) { - LockInc(&shared->c.enomems); - WARNF("(srvr) %s ran out of memory"); - meltdown = true; - } else { - DIEF("(srvr) poll error: %m"); - } - errno = 0; - } - return 2; -} - static void RestoreApe(void) { char *p; size_t n; @@ -6883,6 +6859,97 @@ static void RestoreApe(void) { } } +static int HandleReadline(void) { + int status; + for (;;) { + status = lua_loadline(GL); + 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; + } else if (errno == EAGAIN) { + errno = 0; + return 0; + } else { + OnTerm(SIGIO); // error + return -1; + } + } + write(1, "\r\n", 2); + linenoiseDisableRawMode(); + _spinlock(&lualock); + if (status == LUA_OK) { + status = lua_runchunk(GL, 0, LUA_MULTRET); + } + if (status == LUA_OK) { + lua_l_print(GL); + } else { + lua_report(GL, status); + } + _spunlock(&lualock); + if (lua_repl_isterminal) { + linenoiseEnableRawMode(0); + } + } +} + +static int HandlePoll(int ms) { + int rc, nfds; + size_t pollid, serverid; + if ((nfds = poll(polls, 1 + servers.n, ms)) != -1) { + if (nfds) { + // handle pollid/o events + for (pollid = 0; pollid < 1 + servers.n; ++pollid) { + if (!polls[pollid].revents) continue; + if (polls[pollid].fd < 0) continue; + if (polls[pollid].fd) { + // handle listen socket + 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; + if (rc == -1) return -1; + } else { + // handle standard input + rc = HandleReadline(); + if (rc == -1) return rc; + } + } + } else if (__replmode) { + // handle refresh repl line + if (!IsWindows()) { + rc = HandleReadline(); + if (rc < 0) return rc; + } else { + linenoiseRefreshLine(lua_repl_linenoise); + } + } + } else { + if (errno == EINTR || errno == EAGAIN) { + LockInc(&shared->c.pollinterrupts); + } else if (errno == ENOMEM) { + LockInc(&shared->c.enomems); + WARNF("(srvr) %s ran out of memory"); + meltdown = true; + } else { + DIEF("(srvr) poll error: %m"); + } + errno = 0; + } + return 0; +} + static void Listen(void) { char ipbuf[16]; size_t i, j, n; @@ -6960,10 +7027,9 @@ static void HandleShutdown(void) { } // this function coroutines with linenoise -static int EventLoop(int fd, int ms) { - int rc; +static int EventLoop(int ms) { long double t; - rc = -1; + VERBOSEF("EventLoop()"); while (!terminated) { errno = 0; if (zombied) { @@ -6977,54 +7043,47 @@ static int EventLoop(int fd, int ms) { } else if ((t = nowl()) - lastheartbeat > HEARTBEAT / 1000.) { lastheartbeat = t; HandleHeartbeat(); - } else if ((rc = HandlePoll(ms)) != 2) { - break; // return control to linenoise + } else if (HandlePoll(ms) == -1) { + break; } } - return rc; + return -1; } static void ReplEventLoop(void) { - int status; - long double t; - lua_State *L = GL; + DEBUGF("ReplEventLoop()"); polls[0].fd = 0; - __nomultics = 2; - __replmode = true; - lua_initrepl("redbean"); - linenoiseSetPollCallback(EventLoop); - for (;;) { - if ((status = lua_loadline(L)) == -1) { - if (errno == EINTR) { - LockInc(&shared->c.pollinterrupts); - if (terminated) { - break; - } else { - continue; - } - } else { - break; - } - } - if (status == LUA_OK) { - status = lua_runchunk(L, 0, LUA_MULTRET); - } - if (status == LUA_OK) { - lua_l_print(L); - } else { - lua_report(L, status); - } + lua_initrepl(GL, "redbean"); + if (lua_repl_isterminal) { + linenoiseEnableRawMode(0); } - if (!terminated && !isexitingworker) { - OnTerm(SIGHUP); // eof event - } - lua_settop(L, 0); // clear stack - lua_writeline(); - __replmode = false; - __nomultics = 0; + EventLoop(100); + linenoiseDisableRawMode(); + lua_freerepl(); + lua_settop(GL, 0); // clear stack polls[0].fd = -1; } +static uint32_t WindowsReplThread(void *arg) { + DEBUGF("WindowsReplThread()"); + lua_repl_blocking = true; + lua_initrepl(GL, "redbean"); + if (lua_repl_isterminal) { + linenoiseEnableRawMode(0); + } + for (;;) { + if (HandleReadline() == -1) { + break; + } + } + linenoiseDisableRawMode(); + lua_freerepl(); + _spinlock(&lualock); + lua_settop(GL, 0); // clear stack + _spunlock(&lualock); + return 0; +} + static void SigInit(void) { xsigaction(SIGINT, OnInt, 0, 0, 0); xsigaction(SIGHUP, OnHup, 0, 0, 0); @@ -7217,13 +7276,6 @@ void RedBean(int argc, char *argv[]) { if (daemonize) { Daemonize(); } else { - // xxx: create process group to make it easier to propagate SIGTERM - // to children. the downside to doing this seems to be that - // ctrl-c isn't propagating as expected when running redbean - // underneath strace.com :| - if (!IsWindows()) { - setpgrp(); - } if (logpath) { close(2); open(logpath, O_APPEND | O_WRONLY | O_CREAT, 0640); @@ -7240,14 +7292,18 @@ void RedBean(int argc, char *argv[]) { isinitialized = true; CallSimpleHookIfDefined("OnServerStart"); #ifdef STATIC - EventLoop(-1, HEARTBEAT); + EventLoop(HEARTBEAT); #else - GetResolvConf(); // for effect GetHostsTxt(); // for effect - if (!IsWindows() && isatty(0)) { - ReplEventLoop(); + GetResolvConf(); // for effect + if (daemonize || !linenoiseIsTerminal()) { + EventLoop(HEARTBEAT); + } else if (IsWindows()) { + uint32_t tid; + CreateThread(0, 0, NT2SYSV(WindowsReplThread), 0, 0, &tid); + EventLoop(100); } else { - EventLoop(-1, HEARTBEAT); + ReplEventLoop(); } #endif if (!isexitingworker) { diff --git a/tool/viz/bf.c b/tool/viz/bf.c index ad2e8c582..2b8433a9a 100644 --- a/tool/viz/bf.c +++ b/tool/viz/bf.c @@ -83,7 +83,7 @@ void bf(int fd) { obuf[n++] = ';'; obuf[n++] = '5'; obuf[n++] = ';'; - n += int64toarray_radix10(fg, obuf + n); + n = FormatInt64(obuf + n, fg) - (obuf + n); obuf[n++] = 'm'; } obuf[n++] = "0123456789abcdef"[c >> 4]; diff --git a/tool/viz/lib/formatstringtable-assembly.c b/tool/viz/lib/formatstringtable-assembly.c index 82367adae..2aa23929c 100644 --- a/tool/viz/lib/formatstringtable-assembly.c +++ b/tool/viz/lib/formatstringtable-assembly.c @@ -79,7 +79,7 @@ static const char *GetStorageSpecifier(const char *type, int *out_width, static void EmitSection(long yn, long xn, int w, int arrayalign, int emit(), void *a) { char alignstr[21]; - uint64toarray_radix10(arrayalign, alignstr); + FormatUint32(alignstr, arrayalign); if (arrayalign <= 8 && yn * xn * w == 8) { emit("\t.rodata.cst", a); emit("8\n", a); @@ -108,8 +108,8 @@ void *FormatStringTableAsAssembly(long yn, long xn, const char *const T[yn][xn], char ynstr[21], xnstr[21]; name = firstnonnull(name, "M"); storage = GetStorageSpecifier(firstnonnull(type, "long"), &w, &align); - uint64toarray_radix10(yn, ynstr); - uint64toarray_radix10(xn, xnstr); + FormatUint64(ynstr, yn); + FormatUint64(xnstr, xn); EmitSection(yn, xn, w, GetArrayAlignment(yn, xn, w, align), emit, a); emit(name, a); emit(":", a); diff --git a/tool/viz/lib/formatstringtable-code.c b/tool/viz/lib/formatstringtable-code.c index 1fbfe724e..a4612a790 100644 --- a/tool/viz/lib/formatstringtable-code.c +++ b/tool/viz/lib/formatstringtable-code.c @@ -24,8 +24,8 @@ void *FormatStringTableAsCode(long yn, long xn, const char *const T[yn][xn], int emit(), void *arg, const char *type, const char *name, const char *ignored) { char ynstr[21], xnstr[21]; - uint64toarray_radix10(yn, ynstr); - uint64toarray_radix10(xn, xnstr); + FormatUint64(ynstr, yn); + FormatUint64(xnstr, xn); emit(type, arg); emit(" ", arg); emit(firstnonnull(name, "M"), arg); diff --git a/tool/viz/life.c b/tool/viz/life.c index 73adffe54..a648022b3 100644 --- a/tool/viz/life.c +++ b/tool/viz/life.c @@ -341,7 +341,7 @@ static void AppendChar(char c) { static void AppendInt(long x) { char ibuf[21]; - AppendData(ibuf, int64toarray_radix10(x, ibuf)); + AppendData(ibuf, FormatInt64(ibuf, x) - ibuf); } /*───────────────────────────────────────────────────────────────────────────│─╗ diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index 5d801d459..4f16e59d9 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -743,7 +743,7 @@ static void Render(void) { fg = InvertXtermGreyscale(fg); } p = stpcpy(p, "\e[38;5;"); - p += int64toarray_radix10(fg, p); + p = FormatInt64(p, fg); *p++ = 'm'; } w = tpenc(kCp437[c]); @@ -769,14 +769,14 @@ static void Render(void) { } p = stpcpy(p, " memzoom\e[0m "); if (!pid) { - p += uint64toarray_radix10(MIN(offset / (long double)size * 100, 100), p); + p = FormatUint32(p, MIN(offset / (long double)size * 100, 100)); p = stpcpy(p, "%-"); - p += uint64toarray_radix10( - MIN((offset + ((tyn * txn) << zoom)) / (long double)size * 100, 100), - p); + p = FormatUint32( + p, + MIN((offset + ((tyn * txn) << zoom)) / (long double)size * 100, 100)); p = stpcpy(p, "% "); } - p += uint64toarray_radix10(1L << zoom, p); + p = FormatUint32(p, 1L << zoom); p = stpcpy(p, "x\e[J"); PreventBufferbloat(); for (i = 0, n = p - buffer; i < n; i += got) { @@ -910,10 +910,10 @@ static void GetOpts(int argc, char *argv[]) { } if (pid) { p = stpcpy(path, "/proc/"); - p += int64toarray_radix10(pid, p); + p = FormatInt64(p, pid); stpcpy(p, "/mem"); p = stpcpy(mapspath, "/proc/"); - p += int64toarray_radix10(pid, p); + p = FormatInt64(p, pid); stpcpy(p, "/maps"); } else { if (optind == argc) { diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index 4d54bb71d..23e21478e 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -857,8 +857,8 @@ static void OpenVideo(void) { plm_set_video_decode_callback(plm_, OnVideo, NULL); plm_set_audio_decode_callback(plm_, OnAudio, NULL); plm_set_loop(plm_, false); - int64toarray_radix10((chans_ = 2), chansstr_); - int64toarray_radix10((srate_ = plm_get_samplerate(plm_)), sratestr_); + FormatInt64(chansstr_, (chans_ = 2)); + FormatInt64(sratestr_, (srate_ = plm_get_samplerate(plm_))); if (plm_get_num_audio_streams(plm_) && OpenSpeaker()) { plm_set_audio_enabled(plm_, true, 0); } else {