diff --git a/Makefile b/Makefile index c87d438fa..68ed09649 100644 --- a/Makefile +++ b/Makefile @@ -189,13 +189,13 @@ include examples/package/build.mk #-φ-examples/package/new.sh include test/test.mk -OBJS = $(foreach x,$(PKGS),$($(x)_OBJS)) -SRCS = $(foreach x,$(PKGS),$($(x)_SRCS)) -HDRS = $(foreach x,$(PKGS),$($(x)_HDRS)) -INCS = $(foreach x,$(PKGS),$($(x)_INCS)) -BINS = $(foreach x,$(PKGS),$($(x)_BINS)) -TESTS = $(foreach x,$(PKGS),$($(x)_TESTS)) -CHECKS = $(foreach x,$(PKGS),$($(x)_CHECKS)) +OBJS = $(foreach x,$(PKGS),$($(x)_OBJS)) +SRCS := $(foreach x,$(PKGS),$($(x)_SRCS)) +HDRS := $(foreach x,$(PKGS),$($(x)_HDRS)) +INCS = $(foreach x,$(PKGS),$($(x)_INCS)) +BINS = $(foreach x,$(PKGS),$($(x)_BINS)) +TESTS = $(foreach x,$(PKGS),$($(x)_TESTS)) +CHECKS = $(foreach x,$(PKGS),$($(x)_CHECKS)) bins: $(BINS) check: $(CHECKS) @@ -206,11 +206,17 @@ tags: TAGS HTAGS o/$(MODE)/.x: @mkdir -p $(@D) && touch $@ +ifneq ($(findstring 4.,,$(MAKE_VERSION)),$(MAKE_VERSION)) o/$(MODE)/srcs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) $(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x))) - o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) $(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x))) +else +o/$(MODE)/srcs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) + $(MAKE) MODE=rel -j8 -pn bopit 2>/dev/null | sed -ne '/^SRCS/ {s/.*:= //;s/ */\n/g;p;q}' >$@ +o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) + $(MAKE) MODE=rel -j8 -pn bopit 2>/dev/null | sed -ne '/^HDRS/ {s/.*:= //;s/ */\n/g;p;q}' >$@ +endif o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt $(SRCS) $(HDRS) $(INCS) @$(COMPILE) -AMKDEPS $(MKDEPS) -o $@ -r o/$(MODE)/ o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt diff --git a/README.md b/README.md index b082a5af9..09c149fd2 100644 --- a/README.md +++ b/README.md @@ -73,3 +73,4 @@ find o -name \*.com | xargs ls -rShal | less | FreeBSD | 12 | 2018 | | OpenBSD | 6.4 | 2018 | | NetBSD | 9.1 | 2020 | +| GNU Make | 3.80 | 2010 | diff --git a/libc/calls/setitimer.c b/libc/calls/setitimer.c index 205ca9acc..872c545e3 100644 --- a/libc/calls/setitimer.c +++ b/libc/calls/setitimer.c @@ -52,6 +52,8 @@ * * Be sure to check for EINTR on your i/o calls, for best low latency. * + * Timers are not inherited across fork. + * * @param which can be ITIMER_REAL, ITIMER_VIRTUAL, etc. * @param newvalue specifies the interval ({0,0} means one-shot) and * duration ({0,0} means disarm) in microseconds ∈ [0,999999] and diff --git a/libc/fmt/kerrornames.S b/libc/fmt/kerrornames.S new file mode 100644 index 000000000..7574a5b9a --- /dev/null +++ b/libc/fmt/kerrornames.S @@ -0,0 +1,164 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" + + .macro .e e + .long \e - kErrorNames + .long 1f - kErrorNames + .section .rodata.str1.1 +1: .string "\e" + .previous + .endm + + .section .rodata + .align 4 +kErrorNames: + .e ENOSYS + .e EPERM + .e ENOENT + .e ESRCH + .e EINTR + .e EIO + .e ENXIO + .e E2BIG + .e ENOEXEC + .e EBADF + .e ECHILD + .e EAGAIN + .e ENOMEM + .e EACCES + .e EFAULT + .e ENOTBLK + .e EBUSY + .e EEXIST + .e EXDEV + .e ENODEV + .e ENOTDIR + .e EISDIR + .e EINVAL + .e ENFILE + .e EMFILE + .e ENOTTY + .e ETXTBSY + .e EFBIG + .e ENOSPC + .e EDQUOT + .e ESPIPE + .e EROFS + .e EMLINK + .e EPIPE + .e EDOM + .e ERANGE + .e EDEADLK + .e ENAMETOOLONG + .e ENOLCK + .e ENOTEMPTY + .e ELOOP + .e ENOMSG + .e EIDRM + .e ETIME + .e EPROTO + .e EOVERFLOW + .e EILSEQ + .e EUSERS + .e ENOTSOCK + .e EDESTADDRREQ + .e EMSGSIZE + .e EPROTOTYPE + .e ENOPROTOOPT + .e EPROTONOSUPPORT + .e ESOCKTNOSUPPORT + .e ENOTSUP + .e EOPNOTSUPP + .e EPFNOSUPPORT + .e EAFNOSUPPORT + .e EADDRINUSE + .e EADDRNOTAVAIL + .e ENETDOWN + .e ENETUNREACH + .e ENETRESET + .e ECONNABORTED + .e ECONNRESET + .e ENOBUFS + .e EISCONN + .e ENOTCONN + .e ESHUTDOWN + .e ETOOMANYREFS + .e ETIMEDOUT + .e ECONNREFUSED + .e EHOSTDOWN + .e EHOSTUNREACH + .e EALREADY + .e EINPROGRESS + .e ESTALE + .e EREMOTE + .e EBADMSG + .e ECANCELED + .e EOWNERDEAD + .e ENOTRECOVERABLE + .e ENONET + .e ERESTART + .e ECHRNG + .e EL2NSYNC + .e EL3HLT + .e EL3RST + .e ELNRNG + .e EUNATCH + .e ENOCSI + .e EL2HLT + .e EBADE + .e EBADR + .e EXFULL + .e ENOANO + .e EBADRQC + .e EBADSLT + .e ENOSTR + .e ENODATA + .e ENOSR + .e ENOPKG + .e ENOLINK + .e EADV + .e ESRMNT + .e ECOMM + .e EMULTIHOP + .e EDOTDOT + .e ENOTUNIQ + .e EBADFD + .e EREMCHG + .e ELIBACC + .e ELIBBAD + .e ELIBSCN + .e ELIBMAX + .e ELIBEXEC + .e ESTRPIPE + .e EUCLEAN + .e ENOTNAM + .e ENAVAIL + .e EISNAM + .e EREMOTEIO + .e ENOMEDIUM + .e EMEDIUMTYPE + .e ENOKEY + .e EKEYEXPIRED + .e EKEYREVOKED + .e EKEYREJECTED + .e ERFKILL + .e EHWPOISON + .long 0 + .endobj kErrorNames,globl,hidden diff --git a/libc/fmt/pflink.h b/libc/fmt/pflink.h index 40f7f2af5..65c503155 100644 --- a/libc/fmt/pflink.h +++ b/libc/fmt/pflink.h @@ -18,20 +18,20 @@ * format strings are constexprs that only contain directives. */ -#define PFLINK(FMT) \ - ({ \ - if (___PFLINK(FMT, strpbrk, "faAeEgG")) STATIC_YOINK("__fmt_dtoa"); \ - if (___PFLINK(FMT, strpbrk, "cmrqs")) { \ - if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \ - if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \ - if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \ - ___PFLINK(FMT, strpbrk, "0123456789"))) { \ - STATIC_YOINK("strnwidth"); \ - STATIC_YOINK("strnwidth16"); \ - STATIC_YOINK("wcsnwidth"); \ - } \ - } \ - FMT; \ +#define PFLINK(FMT) \ + ({ \ + if (___PFLINK(FMT, strpbrk, "faAeg")) STATIC_YOINK("__fmt_dtoa"); \ + if (___PFLINK(FMT, strpbrk, "cmrqs")) { \ + if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \ + if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \ + if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \ + ___PFLINK(FMT, strpbrk, "0123456789"))) { \ + STATIC_YOINK("strnwidth"); \ + STATIC_YOINK("strnwidth16"); \ + STATIC_YOINK("wcsnwidth"); \ + } \ + } \ + FMT; \ }) #define SFLINK(FMT) \ diff --git a/libc/fmt/strerror_r.c b/libc/fmt/strerror_r.c index 0e54cddcb..9a17d23cc 100644 --- a/libc/fmt/strerror_r.c +++ b/libc/fmt/strerror_r.c @@ -27,149 +27,17 @@ #include "libc/nt/runtime.h" #include "libc/str/str.h" -const struct Error { - const long *x; - const char *s; -} kErrors[] = { - {&ENOSYS, "ENOSYS"}, - {&EPERM, "EPERM"}, - {&ENOENT, "ENOENT"}, - {&ESRCH, "ESRCH"}, - {&EINTR, "EINTR"}, - {&EIO, "EIO"}, - {&ENXIO, "ENXIO"}, - {&E2BIG, "E2BIG"}, - {&ENOEXEC, "ENOEXEC"}, - {&EBADF, "EBADF"}, - {&ECHILD, "ECHILD"}, - {&EAGAIN, "EAGAIN"}, - {&ENOMEM, "ENOMEM"}, - {&EACCES, "EACCES"}, - {&EFAULT, "EFAULT"}, - {&ENOTBLK, "ENOTBLK"}, - {&EBUSY, "EBUSY"}, - {&EEXIST, "EEXIST"}, - {&EXDEV, "EXDEV"}, - {&ENODEV, "ENODEV"}, - {&ENOTDIR, "ENOTDIR"}, - {&EISDIR, "EISDIR"}, - {&EINVAL, "EINVAL"}, - {&ENFILE, "ENFILE"}, - {&EMFILE, "EMFILE"}, - {&ENOTTY, "ENOTTY"}, - {&ETXTBSY, "ETXTBSY"}, - {&EFBIG, "EFBIG"}, - {&ENOSPC, "ENOSPC"}, - {&EDQUOT, "EDQUOT"}, - {&ESPIPE, "ESPIPE"}, - {&EROFS, "EROFS"}, - {&EMLINK, "EMLINK"}, - {&EPIPE, "EPIPE"}, - {&EDOM, "EDOM"}, - {&ERANGE, "ERANGE"}, - {&EDEADLK, "EDEADLK"}, - {&ENAMETOOLONG, "ENAMETOOLONG"}, - {&ENOLCK, "ENOLCK"}, - {&ENOTEMPTY, "ENOTEMPTY"}, - {&ELOOP, "ELOOP"}, - {&ENOMSG, "ENOMSG"}, - {&EIDRM, "EIDRM"}, - {&ETIME, "ETIME"}, - {&EPROTO, "EPROTO"}, - {&EOVERFLOW, "EOVERFLOW"}, - {&EILSEQ, "EILSEQ"}, - {&EUSERS, "EUSERS"}, - {&ENOTSOCK, "ENOTSOCK"}, - {&EDESTADDRREQ, "EDESTADDRREQ"}, - {&EMSGSIZE, "EMSGSIZE"}, - {&EPROTOTYPE, "EPROTOTYPE"}, - {&ENOPROTOOPT, "ENOPROTOOPT"}, - {&EPROTONOSUPPORT, "EPROTONOSUPPORT"}, - {&ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"}, - {&ENOTSUP, "ENOTSUP"}, - {&EOPNOTSUPP, "EOPNOTSUPP"}, - {&EPFNOSUPPORT, "EPFNOSUPPORT"}, - {&EAFNOSUPPORT, "EAFNOSUPPORT"}, - {&EADDRINUSE, "EADDRINUSE"}, - {&EADDRNOTAVAIL, "EADDRNOTAVAIL"}, - {&ENETDOWN, "ENETDOWN"}, - {&ENETUNREACH, "ENETUNREACH"}, - {&ENETRESET, "ENETRESET"}, - {&ECONNABORTED, "ECONNABORTED"}, - {&ECONNRESET, "ECONNRESET"}, - {&ENOBUFS, "ENOBUFS"}, - {&EISCONN, "EISCONN"}, - {&ENOTCONN, "ENOTCONN"}, - {&ESHUTDOWN, "ESHUTDOWN"}, - {&ETOOMANYREFS, "ETOOMANYREFS"}, - {&ETIMEDOUT, "ETIMEDOUT"}, - {&ECONNREFUSED, "ECONNREFUSED"}, - {&EHOSTDOWN, "EHOSTDOWN"}, - {&EHOSTUNREACH, "EHOSTUNREACH"}, - {&EALREADY, "EALREADY"}, - {&EINPROGRESS, "EINPROGRESS"}, - {&ESTALE, "ESTALE"}, - {&EREMOTE, "EREMOTE"}, - {&EBADMSG, "EBADMSG"}, - {&ECANCELED, "ECANCELED"}, - {&EOWNERDEAD, "EOWNERDEAD"}, - {&ENOTRECOVERABLE, "ENOTRECOVERABLE"}, - {&ENONET, "ENONET"}, - {&ERESTART, "ERESTART"}, - {&ECHRNG, "ECHRNG"}, - {&EL2NSYNC, "EL2NSYNC"}, - {&EL3HLT, "EL3HLT"}, - {&EL3RST, "EL3RST"}, - {&ELNRNG, "ELNRNG"}, - {&EUNATCH, "EUNATCH"}, - {&ENOCSI, "ENOCSI"}, - {&EL2HLT, "EL2HLT"}, - {&EBADE, "EBADE"}, - {&EBADR, "EBADR"}, - {&EXFULL, "EXFULL"}, - {&ENOANO, "ENOANO"}, - {&EBADRQC, "EBADRQC"}, - {&EBADSLT, "EBADSLT"}, - {&ENOSTR, "ENOSTR"}, - {&ENODATA, "ENODATA"}, - {&ENOSR, "ENOSR"}, - {&ENOPKG, "ENOPKG"}, - {&ENOLINK, "ENOLINK"}, - {&EADV, "EADV"}, - {&ESRMNT, "ESRMNT"}, - {&ECOMM, "ECOMM"}, - {&EMULTIHOP, "EMULTIHOP"}, - {&EDOTDOT, "EDOTDOT"}, - {&ENOTUNIQ, "ENOTUNIQ"}, - {&EBADFD, "EBADFD"}, - {&EREMCHG, "EREMCHG"}, - {&ELIBACC, "ELIBACC"}, - {&ELIBBAD, "ELIBBAD"}, - {&ELIBSCN, "ELIBSCN"}, - {&ELIBMAX, "ELIBMAX"}, - {&ELIBEXEC, "ELIBEXEC"}, - {&ESTRPIPE, "ESTRPIPE"}, - {&EUCLEAN, "EUCLEAN"}, - {&ENOTNAM, "ENOTNAM"}, - {&ENAVAIL, "ENAVAIL"}, - {&EISNAM, "EISNAM"}, - {&EREMOTEIO, "EREMOTEIO"}, - {&ENOMEDIUM, "ENOMEDIUM"}, - {&EMEDIUMTYPE, "EMEDIUMTYPE"}, - {&ENOKEY, "ENOKEY"}, - {&EKEYEXPIRED, "EKEYEXPIRED"}, - {&EKEYREVOKED, "EKEYREVOKED"}, - {&EKEYREJECTED, "EKEYREJECTED"}, - {&ERFKILL, "ERFKILL"}, - {&EHWPOISON, "EHWPOISON"}, -}; +extern const struct Error { + int x; + int s; +} kErrorNames[]; -static const char *geterrname(long x) { +static const char *GetErrorName(long x) { int i; if (x) { - for (i = 0; i < ARRAYLEN(kErrors); ++i) { - if (x == *kErrors[i].x) { - return kErrors[i].s; + for (i = 0; kErrorNames[i].x; ++i) { + if (x == *(const long *)((uintptr_t)kErrorNames + kErrorNames[i].x)) { + return (const char *)((uintptr_t)kErrorNames + kErrorNames[i].s); } } } @@ -184,7 +52,7 @@ int strerror_r(int err, char *buf, size_t size) { char *p; const char *s; err &= 0xFFFF; - s = geterrname(err); + s = GetErrorName(err); p = buf; if (strlen(s) + 1 + 5 + 1 + 1 <= size) { p = stpcpy(p, s); diff --git a/libc/runtime/openexecutable.S b/libc/runtime/openexecutable.S index 71babb8fd..24750acd6 100644 --- a/libc/runtime/openexecutable.S +++ b/libc/runtime/openexecutable.S @@ -40,9 +40,7 @@ OpenExecutable: pushq MAP_ANONYMOUS(%rip) # -0x28(%rbp) pushq MAP_PRIVATE(%rip) # -0x30(%rbp) pushq MAP_FIXED(%rip) # -0x38(%rbp) - pushq MAP_SHARED(%rip) # -0x40(%rbp) - pushq __NR_mprotect(%rip) # -0x48(%rbp) - pushq __NR_mprotect(%rip) # -0x50(%rbp) + pushq __NR_mprotect(%rip) # -0x40(%rbp) push %rbx # code buffer push %r12 # data buffer push %r14 # filename @@ -98,7 +96,7 @@ OpenExecutable: rep movsb // Change protection. - mov -0x48(%rbp),%eax # __NR_mprotect + mov -0x40(%rbp),%eax # __NR_mprotect mov %rbx,%rdi mov $PAGESIZE,%esi mov $PROT_READ|PROT_EXEC,%edx @@ -133,7 +131,7 @@ OpenExecutable: mov $ape_rom_filesz,%esi mov $PROT_READ|PROT_EXEC,%edx mov -0x38(%rbp),%r10d # MAP_FIXED - or -0x40(%rbp),%r10d # MAP_SHARED + or -0x30(%rbp),%r10d # MAP_PRIVATE mov %r15d,%r8d mov $ape_rom_offset,%r9d push %r9 # openbsd:pad diff --git a/libc/runtime/peekall.S b/libc/runtime/peekall.S index a1f82ebc1..85510fd1f 100644 --- a/libc/runtime/peekall.S +++ b/libc/runtime/peekall.S @@ -24,6 +24,7 @@ _peekall: .leafprologue ezlea _base,si ezlea _end,cx + add $0x1000,%rsi 0: mov (%rsi),%eax add $PAGESIZE,%rsi cmp %rcx,%rsi diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index dfdadf27e..4667ecc13 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -33,10 +33,10 @@ * allocated automatically, also NUL-terminated is guaranteed * @param n is the capacity of s (in/out) * @param delim is the stop char (and NUL is implicitly too) - * @return number of bytes read, including delim, excluding NUL, or -1 - * w/ errno on EOF or error; see ferror() and feof() + * @return number of bytes read >0, including delim, excluding NUL, + * or -1 w/ errno on EOF or error; see ferror() and feof() * @note this function can't punt EINTR to caller - * @see getline(), gettok_r() + * @see getline(), chomp(), gettok_r() */ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) { char *p; diff --git a/libc/str/strchr.c b/libc/str/strchr.c index c74aac9fb..885a4ad8e 100644 --- a/libc/str/strchr.c +++ b/libc/str/strchr.c @@ -17,16 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/str/str.h" -noasan static const unsigned char *strchr_x64(const unsigned char *p, - uint64_t c) { +noasan static const char *strchr_x64(const char *p, uint64_t c) { unsigned a, b; uint64_t w, x, y; for (c *= 0x0101010101010101;; p += 8) { - w = (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 | - (uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 | - (uint64_t)p[1] << 010 | (uint64_t)p[0] << 000; + w = READ64LE(p); if ((x = ~(w ^ c) & ((w ^ c) - 0x0101010101010101) & 0x8080808080808080) | (y = ~w & (w - 0x0101010101010101) & 0x8080808080808080)) { if (x) { @@ -63,7 +61,7 @@ char *strchr(const char *s, int c) { if ((*s & 0xff) == c) return s; if (!*s) return NULL; } - r = (char *)strchr_x64((const unsigned char *)s, c); + r = strchr_x64(s, c); assert(!r || *r || !c); return r; } diff --git a/libc/str/strstr.c b/libc/str/strstr.c index 70129c56a..af79548c3 100644 --- a/libc/str/strstr.c +++ b/libc/str/strstr.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/safemacros.internal.h" #include "libc/str/str.h" /** @@ -29,6 +30,8 @@ */ char *strstr(const char *haystack, const char *needle) { size_t i; + if (!*needle) return haystack; + haystack = firstnonnull(strchr(haystack, *needle), haystack); for (;;) { for (i = 0;;) { if (!needle[i]) return (/*unconst*/ char *)haystack; diff --git a/libc/str/tprecode16to8.c b/libc/str/tprecode16to8.c index 652dafd60..844127384 100644 --- a/libc/str/tprecode16to8.c +++ b/libc/str/tprecode16to8.c @@ -49,6 +49,9 @@ static noasan axdx_t tprecode16to8_sse2(char *dst, size_t dstsize, /** * Transcodes UTF-16 to UTF-8. * + * This is a low-level function intended for the core runtime. Use + * utf16toutf8() for a much better API that uses malloc(). + * * @param dst is output buffer * @param dstsize is bytes in dst * @param src is NUL-terminated UTF-16 input string diff --git a/libc/str/tprecode8to16.c b/libc/str/tprecode8to16.c index f06e7d104..e93be6dfd 100644 --- a/libc/str/tprecode8to16.c +++ b/libc/str/tprecode8to16.c @@ -46,6 +46,9 @@ static inline noasan axdx_t tprecode8to16_sse2(char16_t *dst, size_t dstsize, /** * Transcodes UTF-8 to UTF-16. * + * This is a low-level function intended for the core runtime. Use + * utf8toutf16() for a much better API that uses malloc(). + * * @param dst is output buffer * @param dstsize is shorts in dst * @param src is NUL-terminated UTF-8 input string diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index 408541d12..10a6f1f8c 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -74,6 +74,7 @@ COSMOPOLITAN_C_START_ * and if the test succeeds it'll be removed along with any contents. */ extern char testlib_enable_tmp_setup_teardown; +extern char testlib_enable_tmp_setup_teardown_once; /** * User-defined test setup function. @@ -82,6 +83,7 @@ extern char testlib_enable_tmp_setup_teardown; * defined by the linkage. */ void SetUp(void); +void SetUpOnce(void); /** * User-defined test cleanup function. @@ -90,6 +92,7 @@ void SetUp(void); * defined by the linkage. */ void TearDown(void); +void TearDownOnce(void); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § testing library » assert or die ─╬─│┼ diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 74f41d78e..50bf8d8aa 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -29,6 +29,10 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" +static int x; +static char cwd[PATH_MAX]; +static char tmp[PATH_MAX]; + void testlib_finish(void) { if (g_testlib_failed) { fprintf(stderr, "%u / %u %s\n", g_testlib_failed, g_testlib_ran, @@ -42,6 +46,18 @@ wontreturn void testlib_abort(void) { unreachable; } +static void SetupTmpDir(void) { + snprintf(tmp, sizeof(tmp), "o/tmp/%s.%d.%d", program_invocation_short_name, + getpid(), x++); + CHECK_NE(-1, makedirs(tmp, 0755), "tmp=%s", tmp); + CHECK_NE(-1, chdir(tmp), "tmp=%s", tmp); +} + +static void TearDownTmpDir(void) { + CHECK_NE(-1, chdir(cwd)); + CHECK_NE(-1, rmrf(tmp)); +} + /** * Runs all test case functions in sorted order. */ @@ -62,18 +78,12 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, * * @see ape/ape.lds */ - int x; - char cwd[PATH_MAX]; - char tmp[PATH_MAX]; const testfn_t *fn; + CHECK_NOTNULL(getcwd(cwd, sizeof(cwd))); + if (weaken(testlib_enable_tmp_setup_teardown_once)) SetupTmpDir(); + if (weaken(SetUpOnce)) weaken(SetUpOnce)(); for (x = 0, fn = start; fn != end; ++fn) { - if (weaken(testlib_enable_tmp_setup_teardown)) { - CHECK_NOTNULL(getcwd(cwd, sizeof(cwd))); - snprintf(tmp, sizeof(tmp), "o/tmp/%s.%d.%d", - program_invocation_short_name, getpid(), x++); - CHECK_NE(-1, makedirs(tmp, 0755), "tmp=%s", tmp); - CHECK_NE(-1, chdir(tmp), "tmp=%s", tmp); - } + if (weaken(testlib_enable_tmp_setup_teardown)) SetupTmpDir(); if (weaken(SetUp)) weaken(SetUp)(); errno = 0; SetLastError(0); @@ -83,9 +93,8 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, (*fn)(); sys_getpid(); if (weaken(TearDown)) weaken(TearDown)(); - if (weaken(testlib_enable_tmp_setup_teardown)) { - CHECK_NE(-1, chdir(cwd)); - CHECK_NE(-1, rmrf(tmp)); - } + if (weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir(); } + if (weaken(TearDownOnce)) weaken(TearDownOnce)(); + if (weaken(testlib_enable_tmp_setup_teardown_once)) TearDownTmpDir(); } diff --git a/libc/x/utf16toutf8.c b/libc/x/utf16toutf8.c new file mode 100644 index 000000000..8924f905a --- /dev/null +++ b/libc/x/utf16toutf8.c @@ -0,0 +1,91 @@ +/*-*- 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/bits/bits.h" +#include "libc/intrin/packsswb.h" +#include "libc/intrin/pandn.h" +#include "libc/intrin/pcmpgtb.h" +#include "libc/intrin/pcmpgtw.h" +#include "libc/intrin/pmovmskb.h" +#include "libc/intrin/punpckhbw.h" +#include "libc/intrin/punpcklbw.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/str/str.h" +#include "libc/str/thompike.h" +#include "libc/str/tpenc.h" +#include "libc/str/utf16.h" +#include "libc/x/x.h" + +static const int16_t kDel16[8] = {127, 127, 127, 127, 127, 127, 127, 127}; + +/** + * Transcodes UTF-16 to UTF-8. + * + * @param p is input value + * @param n if -1 implies strlen + * @param z if non-NULL receives output length + */ +char *utf16toutf8(const char16_t *p, size_t n, size_t *z) { + char *r, *q; + wint_t x, y; + unsigned m, j, w; + const char16_t *e; + int16_t v1[8], v2[8], v3[8], vz[8]; + if (z) *z = 0; + if (n == -1) n = p ? strlen16(p) : 0; + if ((q = r = malloc(n * 4 + 8 + 1))) { + for (e = p + n; p < e;) { + if (p + 8 < e) { /* 17x ascii */ + memset(vz, 0, 16); + do { + memcpy(v1, p, 16); + pcmpgtw(v2, v1, vz); + pcmpgtw(v3, v1, kDel16); + pandn((void *)v2, (void *)v3, (void *)v2); + if (pmovmskb((void *)v2) != 0xFFFF) break; + packsswb((void *)v1, v1, v1); + memcpy(q, v1, 8); + p += 8; + q += 8; + } while (p + 8 < e); + } + x = *p++ & 0xffff; + if (!IsUcs2(x)) { + if (p < e) { + y = *p++ & 0xffff; + x = MergeUtf16(x, y); + } else { + x = 0xFFFD; + } + } + if (x < 0200) { + *q++ = x; + } else { + w = tpenc(x); + WRITE64LE(q, w); + q += bsr(w) >> 3; + q += 1; + } + } + if (z) *z = q - r; + *q++ = '\0'; + if ((q = realloc(r, (q - r) * 1))) r = q; + } + return r; +} diff --git a/libc/x/utf8toutf16.c b/libc/x/utf8toutf16.c new file mode 100644 index 000000000..6ae24d652 --- /dev/null +++ b/libc/x/utf8toutf16.c @@ -0,0 +1,86 @@ +/*-*- 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/intrin/pcmpgtb.h" +#include "libc/intrin/pmovmskb.h" +#include "libc/intrin/punpckhbw.h" +#include "libc/intrin/punpcklbw.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/str/thompike.h" +#include "libc/str/utf16.h" +#include "libc/x/x.h" + +/** + * Transcodes UTF-8 to UTF-16. + * + * @param p is input value + * @param n if -1 implies strlen + * @param z if non-NULL receives output length + */ +char16_t *utf8toutf16(const char *p, size_t n, size_t *z) { + size_t i; + wint_t x, a, b; + char16_t *r, *q; + unsigned m, j, w; + uint8_t v1[16], v2[16], vz[16]; + if (z) *z = 0; + if (n == -1) n = p ? strlen(p) : 0; + if ((q = r = malloc(n * sizeof(char16_t) * 2 + sizeof(char16_t)))) { + for (i = 0; i < n;) { + if (i + 16 < n) { /* 34x ascii */ + memset(vz, 0, 16); + do { + memcpy(v1, p + i, 16); + pcmpgtb((int8_t *)v2, (int8_t *)v1, (int8_t *)vz); + if (pmovmskb(v2) != 0xFFFF) break; + punpcklbw(v2, v1, vz); + punpckhbw(v1, v1, vz); + memcpy(q + 0, v2, 16); + memcpy(q + 8, v1, 16); + i += 16; + q += 16; + } while (i + 16 < n); + } + x = p[i++] & 0xff; + if (x >= 0300) { + a = ThomPikeByte(x); + m = ThomPikeLen(x) - 1; + if (i + m <= n) { + for (j = 0;;) { + b = p[i + j] & 0xff; + if (!ThomPikeCont(b)) break; + a = ThomPikeMerge(a, b); + if (++j == m) { + x = a; + i += j; + break; + } + } + } + } + w = EncodeUtf16(x); + *q++ = w; + if ((w >>= 16)) *q++ = w; + } + if (z) *z = q - r; + *q++ = '\0'; + if ((q = realloc(r, (q - r) * sizeof(char16_t)))) r = q; + } + return r; +} diff --git a/libc/x/x.h b/libc/x/x.h index dd359878f..5bab222f5 100644 --- a/libc/x/x.h +++ b/libc/x/x.h @@ -51,6 +51,8 @@ char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL; char *xinet_ntop(int, const void *) _XPNN _XMAL; void *xunbinga(size_t, const char16_t *) attributeallocalign((1)) _XMAL _XRET; void *xunbing(const char16_t *) _XMAL _XRET; +char16_t *utf8toutf16(const char *, size_t, size_t *) nodiscard; +char *utf16toutf8(const char16_t *, size_t, size_t *) nodiscard; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § eXtended apis » files ─╬─│┼ diff --git a/libc/x/xgetline.c b/libc/x/xgetline.c index 6bf4f2ba8..56123ffef 100644 --- a/libc/x/xgetline.c +++ b/libc/x/xgetline.c @@ -25,16 +25,18 @@ * * @return allocated line that needs free() and usually chomp() too, * or NULL on ferror() or feof() - * @see getline() for a more difficult api + * @see getdelim() for a more difficult api + * @see chomp() */ char *xgetline(FILE *f) { - char *res; - size_t n, got; + char *p; + size_t n; + ssize_t m; n = 0; - res = NULL; - if ((got = getdelim(&res, &n, '\n', f)) <= 0) { - free(res); - res = NULL; + p = 0; + if ((m = getdelim(&p, &n, '\n', f)) <= 0) { + free(p); + p = 0; } - return res; + return p; } diff --git a/net/http/encodelatin1.c b/net/http/encodelatin1.c index 17d7f35ac..924188c4e 100644 --- a/net/http/encodelatin1.c +++ b/net/http/encodelatin1.c @@ -30,7 +30,7 @@ * @param p is input value * @param n if -1 implies strlen * @param z if non-NULL receives output length - * @param f can kControlC0, kControlC1 to forbid + * @param f can kControlC0, kControlC1, kControlWs to forbid * @return allocated NUL-terminated buffer, or NULL w/ errno * @error EILSEQ means UTF-8 found we can't or won't re-encode * @error ENOMEM means malloc() failed @@ -38,7 +38,12 @@ char *EncodeLatin1(const char *p, size_t n, size_t *z, int f) { int c; size_t i; + char t[256]; char *r, *q; + memset(t, 0, sizeof(t)); + if (f & kControlC0) memset(t + 0x00, 1, 0x20 - 0x00), t[0x7F] = 1; + if (f & kControlC1) memset(t + 0x80, 1, 0xA0 - 0x80); + t['\t'] = t['\r'] = t['\n'] = t['\v'] = !!(f & kControlWs); if (z) *z = 0; if (n == -1) n = p ? strlen(p) : 0; if ((q = r = malloc(n + 1))) { @@ -51,11 +56,7 @@ char *EncodeLatin1(const char *p, size_t n, size_t *z, int f) { goto Invalid; } } - if (((f & kControlC1) && 0x80 <= c && c < 0xA0) || - ((f & kControlC0) && (c < 32 || c == 0x7F) && - !(c == '\t' || c == '\r' || c == '\n' || c == '\v')) || - ((f & kControlWs) && - (c == '\t' || c == '\r' || c == '\n' || c == '\v'))) { + if (t[c]) { goto Invalid; } *q++ = c; diff --git a/net/http/escape.h b/net/http/escape.h index 734082ad6..665ebb0de 100644 --- a/net/http/escape.h +++ b/net/http/escape.h @@ -28,7 +28,7 @@ char *EscapeFragment(const char *, size_t, size_t *); char *EscapeSegment(const char *, size_t, size_t *); char *EscapeJsStringLiteral(const char *, size_t, size_t *); -bool HasControlCodes(const char *, size_t, int); +ssize_t HasControlCodes(const char *, size_t, int); char *Underlong(const char *, size_t, size_t *); char *DecodeLatin1(const char *, size_t, size_t *); char *EncodeLatin1(const char *, size_t, size_t *, int); diff --git a/net/http/gethttpreason.c b/net/http/gethttpreason.c index ce10aaf50..c05f46f7e 100644 --- a/net/http/gethttpreason.c +++ b/net/http/gethttpreason.c @@ -20,8 +20,8 @@ #include "libc/macros.internal.h" #include "net/http/http.h" -static const struct HttpReason { - int code; +static const struct thatispacked HttpReason { + short code; const char *name; } kHttpReason[] = { {100, "Continue"}, diff --git a/net/http/hascontrolcodes.c b/net/http/hascontrolcodes.c index 7748bb48e..c7fba8f22 100644 --- a/net/http/hascontrolcodes.c +++ b/net/http/hascontrolcodes.c @@ -16,11 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/intrin/pcmpgtb.h" -#include "libc/intrin/pmovmskb.h" -#include "libc/mem/mem.h" -#include "libc/stdio/stdio.h" +#include "libc/bits/likely.h" #include "libc/str/str.h" #include "libc/str/thompike.h" #include "net/http/escape.h" @@ -31,17 +27,22 @@ * @param p is input value * @param n if -1 implies strlen * @param f can have kControlWs, kControlC0, kControlC1 to forbid - * @return true if forbidden characters were found + * @return index of first forbidden character or -1 * @see VisualizeControlCodes() */ -bool HasControlCodes(const char *p, size_t n, int f) { - int c; +ssize_t HasControlCodes(const char *p, size_t n, int f) { + char t[256]; wint_t x, a, b; - size_t i, j, m; + size_t i, j, m, g; + memset(t, 0, sizeof(t)); + if (f & kControlC0) memset(t + 0x00, 1, 0x20 - 0x00), t[0x7F] = 1; + if (f & kControlC1) memset(t + 0x80, 1, 0xA0 - 0x80); + t['\t'] = t['\r'] = t['\n'] = t['\v'] = !!(f & kControlWs); if (n == -1) n = p ? strlen(p) : 0; for (i = 0; i < n;) { + g = i; x = p[i++] & 0xff; - if (x >= 0300) { + if (UNLIKELY(x >= 0300)) { a = ThomPikeByte(x); m = ThomPikeLen(x) - 1; if (i + m <= n) { @@ -57,13 +58,9 @@ bool HasControlCodes(const char *p, size_t n, int f) { } } } - if (((f & kControlC1) && 0x80 <= x && x < 0xA0) || - ((f & kControlC0) && (x < 32 || x == 0x7F) && - !(x == '\t' || x == '\r' || x == '\n' || x == '\v')) || - ((f & kControlWs) && - (x == '\t' || x == '\r' || x == '\n' || x == '\v'))) { - return true; + if (x < 256 && t[x]) { + return g; } } - return false; + return -1; } diff --git a/net/http/isacceptablepath.c b/net/http/isacceptablepath.c index 274074e0a..fa155ce20 100644 --- a/net/http/isacceptablepath.c +++ b/net/http/isacceptablepath.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/likely.h" #include "libc/str/str.h" #include "libc/str/thompike.h" #include "net/http/http.h" @@ -45,7 +46,7 @@ bool IsAcceptablePath(const char *data, size_t size) { e = p + size; while (p < e) { x = *p++ & 0xff; - if (x >= 0300) { + if (UNLIKELY(x >= 0300)) { a = ThomPikeByte(x); n = ThomPikeLen(x) - 1; if (p + n <= e) { diff --git a/test/libc/stdio/getline_test.c b/test/libc/stdio/getdelim_test.c similarity index 100% rename from test/libc/stdio/getline_test.c rename to test/libc/stdio/getdelim_test.c diff --git a/test/libc/str/strrchr_test.c b/test/libc/str/strrchr_test.c index cf99fc6c9..a068bcbf2 100644 --- a/test/libc/str/strrchr_test.c +++ b/test/libc/str/strrchr_test.c @@ -19,34 +19,101 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -#define T(NAME) NAME -#define S(S) S -#define C(C) C -#include "test/libc/str/strrchr_test.inc" -#undef C -#undef S -#undef T +TEST(strrchr, test) { + EXPECT_EQ(NULL, strrchr("hello", 'z')); + EXPECT_STREQ("lo", strrchr("hello", 'l')); + EXPECT_STREQ("llo", strchr("hello", 'l')); + EXPECT_STREQ("hello", strrchr("hello", 'h')); + EXPECT_STREQ("ello", strrchr("hello", 'e')); + EXPECT_STREQ("o", strrchr("hello", 'o')); +} -#define T(NAME) NAME##16 -#define S(S) u##S -#define C(C) u##C -#define strrchr(x, y) strrchr16(x, y) -#define strchr(x, y) strchr16(x, y) -#include "test/libc/str/strrchr_test.inc" -#undef strchr -#undef strrchr -#undef C -#undef S -#undef T +TEST(strrchr, simdVectorStuffIsntBroken) { + EXPECT_EQ(NULL, strrchr("--------------------------------", 'x')); + EXPECT_STREQ("x", strrchr("-------------------------------x", 'x')); + EXPECT_STREQ("x-------------------------------", + strrchr("x-------------------------------", 'x')); + EXPECT_STREQ("x" + "z-------------------------------", + strrchr("x" + "z-------------------------------", + 'x')); + EXPECT_STREQ("x-------------------------------" + "y-------------------------------", + strrchr("x-------------------------------" + "y-------------------------------", + 'x')); + EXPECT_STREQ("x" + "z-------------------------------" + "y-------------------------------", + strrchr("x" + "z-------------------------------" + "y-------------------------------", + 'x')); +} -#define T(NAME) NAME##32 -#define S(S) L##S -#define C(C) L##C -#define strchr(x, y) wcschr(x, y) -#define strrchr(x, y) wcsrchr(x, y) -#include "test/libc/str/strrchr_test.inc" -#undef strchr -#undef strrchr -#undef C -#undef S -#undef T +TEST(strrchr16, test) { + EXPECT_EQ(NULL, strrchr16(u"hello", 'z')); + EXPECT_STREQ(u"lo", strrchr16(u"hello", 'l')); + EXPECT_STREQ(u"llo", strchr16(u"hello", 'l')); + EXPECT_STREQ(u"hello", strrchr16(u"hello", 'h')); + EXPECT_STREQ(u"ello", strrchr16(u"hello", 'e')); + EXPECT_STREQ(u"o", strrchr16(u"hello", 'o')); +} + +TEST(strrchr16, simdVectorStuffIsntBroken) { + EXPECT_EQ(NULL, strrchr16(u"--------------------------------", 'x')); + EXPECT_STREQ(u"x", strrchr16(u"-------------------------------x", 'x')); + EXPECT_STREQ(u"x-------------------------------", + strrchr16(u"x-------------------------------", 'x')); + EXPECT_STREQ(u"x" + u"z-------------------------------", + strrchr16(u"x" + u"z-------------------------------", + 'x')); + EXPECT_STREQ(u"x-------------------------------" + u"y-------------------------------", + strrchr16(u"x-------------------------------" + u"y-------------------------------", + 'x')); + EXPECT_STREQ(u"x" + u"z-------------------------------" + u"y-------------------------------", + strrchr16(u"x" + u"z-------------------------------" + u"y-------------------------------", + 'x')); +} + +TEST(wcsrchr, test) { + EXPECT_EQ(NULL, wcsrchr(L"hello", 'z')); + EXPECT_STREQ(L"lo", wcsrchr(L"hello", 'l')); + EXPECT_STREQ(L"llo", wcschr(L"hello", 'l')); + EXPECT_STREQ(L"hello", wcsrchr(L"hello", 'h')); + EXPECT_STREQ(L"ello", wcsrchr(L"hello", 'e')); + EXPECT_STREQ(L"o", wcsrchr(L"hello", 'o')); +} + +TEST(wcsrchr, simdVectorStuffIsntBroken) { + EXPECT_EQ(NULL, wcsrchr(L"--------------------------------", 'x')); + EXPECT_STREQ(L"x", wcsrchr(L"-------------------------------x", 'x')); + EXPECT_STREQ(L"x-------------------------------", + wcsrchr(L"x-------------------------------", 'x')); + EXPECT_STREQ(L"x" + L"z-------------------------------", + wcsrchr(L"x" + L"z-------------------------------", + 'x')); + EXPECT_STREQ(L"x-------------------------------" + L"y-------------------------------", + wcsrchr(L"x-------------------------------" + L"y-------------------------------", + 'x')); + EXPECT_STREQ(L"x" + L"z-------------------------------" + L"y-------------------------------", + wcsrchr(L"x" + L"z-------------------------------" + L"y-------------------------------", + 'x')); +} diff --git a/test/libc/str/strstr_test.c b/test/libc/str/strstr_test.c index c80872c78..273e54e93 100644 --- a/test/libc/str/strstr_test.c +++ b/test/libc/str/strstr_test.c @@ -23,6 +23,8 @@ #include "libc/nexgen32e/x86feature.h" #include "libc/runtime/gc.internal.h" #include "libc/str/internal.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" #define MAKESTRING(NAME, VALUE) \ @@ -75,3 +77,11 @@ TEST(strstr, test) { ASSERT_EQ(NULL, strstr("-Wl,--gc-sections", "sanitize")); ASSERT_STREQ("x", strstr("x", "x")); } + +BENCH(strstr, bench) { + EZBENCH2("strstr", donothing, EXPROPRIATE(strstr(kHyperion, "THE END"))); + EZBENCH2("strstr", donothing, + EXPROPRIATE(strstr( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", + "aaaaaab"))); +} diff --git a/test/libc/str/strrchr_test.inc b/test/libc/x/utf16toutf8_test.c similarity index 55% rename from test/libc/str/strrchr_test.inc rename to test/libc/x/utf16toutf8_test.c index 8f5773a24..476105b82 100644 --- a/test/libc/str/strrchr_test.inc +++ b/test/libc/x/utf16toutf8_test.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,31 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/mem/mem.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" -TEST(T(strrchr), test) { - EXPECT_EQ(NULL, strrchr(S("hello"), C('z'))); - EXPECT_STREQ(S("lo"), strrchr(S("hello"), C('l'))); - EXPECT_STREQ(S("llo"), strchr(S("hello"), C('l'))); - EXPECT_STREQ(S("hello"), strrchr(S("hello"), C('h'))); - EXPECT_STREQ(S("ello"), strrchr(S("hello"), C('e'))); - EXPECT_STREQ(S("o"), strrchr(S("hello"), C('o'))); +TEST(utf16toutf8, test) { + EXPECT_STREQ("hello☻♥", gc(utf16toutf8(u"hello☻♥", -1, 0))); + EXPECT_STREQ("hello☻♥hello☻♥h", gc(utf16toutf8(u"hello☻♥hello☻♥h", -1, 0))); + EXPECT_STREQ("hello☻♥hello☻♥hi", gc(utf16toutf8(u"hello☻♥hello☻♥hi", -1, 0))); + EXPECT_STREQ("hello☻♥hello☻♥hello☻♥hello☻♥hello☻♥", + gc(utf16toutf8(u"hello☻♥hello☻♥hello☻♥hello☻♥hello☻♥", -1, 0))); + EXPECT_STREQ("hello--hello--h", gc(utf16toutf8(u"hello--hello--h", -1, 0))); + EXPECT_STREQ("hello--hello--hi", gc(utf16toutf8(u"hello--hello--hi", -1, 0))); + EXPECT_STREQ("hello--hello--hello--hello--hello--", + gc(utf16toutf8(u"hello--hello--hello--hello--hello--", -1, 0))); } -TEST(T(strrchr), simdVectorStuffIsntBroken) { - EXPECT_EQ(NULL, strrchr(S("--------------------------------"), C('x'))); - EXPECT_STREQ(S("x"), strrchr(S("-------------------------------x"), C('x'))); - EXPECT_STREQ(S("x-------------------------------"), - strrchr(S("x-------------------------------"), C('x'))); - EXPECT_STREQ(S("x") S("z-------------------------------"), - strrchr(S("x") S("z-------------------------------"), C('x'))); - EXPECT_STREQ(S("x-------------------------------") - S("y-------------------------------"), - strrchr(S("x-------------------------------") - S("y-------------------------------"), - C('x'))); - EXPECT_STREQ(S("x") S("z-------------------------------") - S("y-------------------------------"), - strrchr(S("x") S("z-------------------------------") - S("y-------------------------------"), - C('x'))); +BENCH(utf16toutf8, bench) { + size_t n; + char16_t *h; + h = utf8toutf16(kHyperion, kHyperionSize, &n); + EZBENCH2("utf16toutf8", donothing, free(utf16toutf8(h, n, 0))); } diff --git a/test/libc/x/utf8toutf16_test.c b/test/libc/x/utf8toutf16_test.c new file mode 100644 index 000000000..d78391416 --- /dev/null +++ b/test/libc/x/utf8toutf16_test.c @@ -0,0 +1,40 @@ +/*-*- 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/mem/mem.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(utf8toutf16, test) { + EXPECT_STREQ(u"hello☻♥", gc(utf8toutf16("hello☻♥", -1, 0))); + EXPECT_STREQ(u"hello☻♥hello☻♥h", gc(utf8toutf16("hello☻♥hello☻♥h", -1, 0))); + EXPECT_STREQ(u"hello☻♥hello☻♥hi", gc(utf8toutf16("hello☻♥hello☻♥hi", -1, 0))); + EXPECT_STREQ(u"hello☻♥hello☻♥hello☻♥hello☻♥hello☻♥", + gc(utf8toutf16("hello☻♥hello☻♥hello☻♥hello☻♥hello☻♥", -1, 0))); + EXPECT_STREQ(u"hello--hello--h", gc(utf8toutf16("hello--hello--h", -1, 0))); + EXPECT_STREQ(u"hello--hello--hi", gc(utf8toutf16("hello--hello--hi", -1, 0))); + EXPECT_STREQ(u"hello--hello--hello--hello--hello--", + gc(utf8toutf16("hello--hello--hello--hello--hello--", -1, 0))); +} + +BENCH(utf8toutf16, bench) { + EZBENCH2("utf8toutf16", donothing, + free(utf8toutf16(kHyperion, kHyperionSize, 0))); +} diff --git a/test/net/http/hascontrolcodes_test.c b/test/net/http/hascontrolcodes_test.c index 26afa3a48..264bf0bd6 100644 --- a/test/net/http/hascontrolcodes_test.c +++ b/test/net/http/hascontrolcodes_test.c @@ -22,30 +22,30 @@ #include "net/http/escape.h" TEST(HasControlCodes, test) { - EXPECT_FALSE( - HasControlCodes(kHyperion, kHyperionSize, kControlC0 | kControlC1)); - EXPECT_TRUE(HasControlCodes("hi\1", -1, kControlC0)); - EXPECT_FALSE(HasControlCodes("hi\1", -1, kControlC1)); - EXPECT_FALSE(HasControlCodes("hi there", -1, 0)); - EXPECT_TRUE(HasControlCodes("hi\tthere", -1, kControlWs)); + EXPECT_EQ(-1, HasControlCodes(kHyperion, kHyperionSize, kControlC0)); + EXPECT_EQ(+2, HasControlCodes("hi\1", -1, kControlC0)); + EXPECT_EQ(-1, HasControlCodes("hi\1", -1, kControlC1)); + EXPECT_EQ(-1, HasControlCodes("hi there", -1, 0)); + EXPECT_NE(-1, HasControlCodes("hi\tthere", -1, kControlWs)); } TEST(HasControlCodes, testDoesUtf8) { - EXPECT_FALSE(HasControlCodes(u8"→", -1, kControlC0 | kControlC1)); - EXPECT_FALSE(HasControlCodes("\304\200", -1, kControlC0 | kControlC1)); - EXPECT_TRUE(HasControlCodes("\300\200", -1, kControlC0 | kControlC1)); - EXPECT_FALSE(HasControlCodes("\300\200", -1, kControlC1)); - EXPECT_TRUE(HasControlCodes("\302\202", -1, kControlC0 | kControlC1)); - EXPECT_TRUE(HasControlCodes("\302\202", -1, kControlC1)); - EXPECT_FALSE(HasControlCodes("\302\202", -1, kControlC0)); + EXPECT_EQ(-1, HasControlCodes(u8"→", -1, kControlC0 | kControlC1)); + EXPECT_EQ(-1, HasControlCodes("\304\200", -1, kControlC0 | kControlC1)); + EXPECT_NE(-1, HasControlCodes("\300\200", -1, kControlC0 | kControlC1)); + EXPECT_EQ(-1, HasControlCodes("\300\200", -1, kControlC1)); + EXPECT_NE(-1, HasControlCodes("\302\202", -1, kControlC0 | kControlC1)); + EXPECT_NE(-1, HasControlCodes("\302\202", -1, kControlC1)); + EXPECT_EQ(-1, HasControlCodes("\302\202", -1, kControlC0)); } TEST(HasControlCodes, testHasLatin1FallbackBehavior) { - EXPECT_TRUE(HasControlCodes("\202", -1, kControlWs | kControlC1)); - EXPECT_FALSE(HasControlCodes("\202", -1, kControlC0)); + EXPECT_NE(-1, HasControlCodes("\202", -1, kControlWs | kControlC1)); + EXPECT_EQ(-1, HasControlCodes("\202", -1, kControlC0)); } BENCH(HasControlCodes, bench) { - EZBENCH2("HasControlCodes", donothing, - HasControlCodes(kHyperion, kHyperionSize, kControlWs)); + EZBENCH2("HasControlCodes small", donothing, HasControlCodes("hello", -1, 0)); + EZBENCH2("HasControlCodes big", donothing, + HasControlCodes(kHyperion, kHyperionSize, kControlC1)); } diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index 37b309939..b722e7889 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -19,25 +19,32 @@ #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/sock.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/inaddr.h" +#include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/shut.h" #include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/sock.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" - -/* TODO(jart): Finish this */ +#include "third_party/regex/regex.h" STATIC_YOINK("zip_uri_support"); STATIC_YOINK("o/" MODE "/tool/net/redbean.com"); -char testlib_enable_tmp_setup_teardown; +char testlib_enable_tmp_setup_teardown_once; +int port; -void SetUp(void) { - return; +void SetUpOnce(void) { + if (IsWindows()) return; ssize_t n; - char buf[512]; + char buf[1024]; int fdin, fdout; ASSERT_NE(-1, mkdir("bin", 0755)); ASSERT_NE(-1, (fdin = open("zip:o/" MODE "/tool/net/redbean.com", O_RDONLY))); @@ -45,32 +52,72 @@ void SetUp(void) { for (;;) { ASSERT_NE(-1, (n = read(fdin, buf, sizeof(buf)))); if (!n) break; - ASSERT_EQ(n, write(fdout, buf, sizeof(buf))); + ASSERT_EQ(n, write(fdout, buf, n)); } close(fdout); close(fdin); } -TEST(redbean, test) { - return; +char *SendHttpRequest(const char *s) { + int fd; + char *p; + size_t n; + ssize_t rc; + struct sockaddr_in addr = {AF_INET, htons(port), {htonl(INADDR_LOOPBACK)}}; + EXPECT_NE(-1, (fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + EXPECT_NE(-1, connect(fd, &addr, sizeof(addr))); + n = strlen(s); + EXPECT_EQ(n, write(fd, s, n)); + shutdown(fd, SHUT_WR); + for (p = 0, n = 0;; n += rc) { + p = xrealloc(p, n + 512); + EXPECT_NE(-1, (rc = read(fd, p + n, 512))); + if (rc <= 0) break; + } + p = xrealloc(p, n + 1); + p[n] = 0; + close(fd); + return p; +} + +bool Matches(const char *regex, const char *str) { + bool r; + regex_t re; + CHECK_EQ(REG_OK, regcomp(&re, regex, 0)); + r = regexec(&re, str, 0, 0, 0) == REG_OK; + regfree(&re); + return r; +} + +TEST(redbean, testOptions) { + if (IsWindows()) return; char portbuf[16]; - int pid, port, pipefds[2]; + int pid, pipefds[2]; sigset_t chldmask, savemask; sigaddset(&chldmask, SIGCHLD); sigprocmask(SIG_BLOCK, &chldmask, &savemask); - ASSERT_NE(-1, pipe2(pipefds, O_CLOEXEC)); + ASSERT_NE(-1, pipe(pipefds)); ASSERT_NE(-1, (pid = vfork())); if (!pid) { + close(pipefds[0]); dup2(pipefds[1], 1); sigprocmask(SIG_SETMASK, &savemask, NULL); execv("bin/redbean.com", - (char *const[]){"bin/redbean.com", "-zp0", "-l127.0.0.1", 0}); + (char *const[]){"bin/redbean.com", "-szp0", "-l127.0.0.1", 0}); _exit(127); } EXPECT_NE(-1, close(pipefds[1])); EXPECT_NE(-1, read(pipefds[0], portbuf, sizeof(portbuf))); port = atoi(portbuf); - printf("port %d\n", port); + EXPECT_TRUE(Matches("HTTP/1\\.1 200 OK\r\n" + "Accept: \\*/\\*\r\n" + "Accept-Charset: utf-8,ISO-8859-1;q=0\\.7,\\*;q=0\\.5\r\n" + "Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS\r\n" + "Date: .*\r\n" + "Server: redbean/0\\.4\r\n" + "Content-Length: 0\r\n" + "\r\n", + gc(SendHttpRequest("OPTIONS * HTTP/1.1\n\n")))); EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); } diff --git a/test/tool/net/test.mk b/test/tool/net/test.mk index 5dae443a7..d1341da9b 100644 --- a/test/tool/net/test.mk +++ b/test/tool/net/test.mk @@ -29,17 +29,21 @@ TEST_TOOL_NET_CHECKS = \ TEST_TOOL_NET_DIRECTDEPS = \ LIBC_CALLS \ - LIBC_STUBS \ LIBC_FMT \ - LIBC_STDIO \ LIBC_INTRIN \ - LIBC_NEXGEN32E \ - LIBC_SYSV \ + LIBC_LOG \ LIBC_MEM \ + LIBC_NEXGEN32E \ LIBC_RUNTIME \ - LIBC_X \ + LIBC_SOCK \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ LIBC_TESTLIB \ - LIBC_ZIPOS + LIBC_X \ + LIBC_ZIPOS \ + THIRD_PARTY_REGEX TEST_TOOL_NET_DEPS := \ $(call uniq,$(foreach x,$(TEST_TOOL_NET_DIRECTDEPS),$($(x)))) diff --git a/third_party/chibicc/as.c b/third_party/chibicc/as.c index 88c430b49..a8b8d272c 100644 --- a/third_party/chibicc/as.c +++ b/third_party/chibicc/as.c @@ -136,7 +136,12 @@ #define ISRIP 0x00080000 #define ISREG 0x00100000 -#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p)) +#define APPEND(L) \ + if (++L.n > L.c) { \ + L.c = L.n + 2 + (L.c >> 1); \ + L.p = realloc(L.p, L.c * sizeof(*L.p)); \ + } + #define IS(P, N, S) (N == sizeof(S) - 1 && !strncasecmp(P, S, sizeof(S) - 1)) #define MAX(X, Y) ((Y) < (X) ? (X) : (Y)) #define READ128BE(S) ((unsigned __int128)READ64BE(S) << 64 | READ64BE((S) + 8)) @@ -152,29 +157,29 @@ struct As { bool inhibiterr; bool inhibitwarn; struct Ints { - unsigned long n; + unsigned long n, c; long *p; } ints; struct Floats { - unsigned long n; + unsigned long n, c; long double *p; } floats; struct Slices { - unsigned long n; + unsigned long n, c; struct Slice { - unsigned long n; + unsigned long n, c; char *p; } * p; } slices; struct Sauces { - unsigned long n; + unsigned long n, c; struct Sauce { unsigned path; // strings unsigned line; // 1-indexed } * p; } sauces; struct Things { - unsigned long n; + unsigned long n, c; struct Thing { enum ThingType { TT_INT, @@ -189,7 +194,7 @@ struct As { } * p; } things; struct Sections { - unsigned long n; + unsigned long n, c; struct Section { unsigned name; // strings int flags; @@ -199,7 +204,7 @@ struct As { } * p; } sections; struct Symbols { - unsigned long n; + unsigned long n, c; struct Symbol { bool isused; unsigned char stb; // STB_* @@ -220,7 +225,7 @@ struct As { } * p; } symbolindex; struct Labels { - unsigned long n; + unsigned long n, c; struct Label { unsigned id; unsigned tok; // things @@ -228,7 +233,7 @@ struct As { } * p; } labels; struct Relas { - unsigned long n; + unsigned long n, c; struct Rela { bool isdead; int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...} @@ -239,7 +244,7 @@ struct As { } * p; } relas; struct Exprs { - unsigned long n; + unsigned long n, c; struct Expr { enum ExprKind { EX_INT, // integer @@ -277,11 +282,11 @@ struct As { } * p; } exprs; struct Strings { - unsigned long n; + unsigned long n, c; char **p; } strings, incpaths; struct SectionStack { - unsigned long n; + unsigned long n, c; int *p; } sectionstack; }; @@ -805,8 +810,7 @@ static void Tokenize(struct As *a, int path) { continue; } if (c == '"') { - buf.n = 0; - buf.p = NULL; + memset(&buf, 0, sizeof(buf)); for (i = 1; (c = p[i++]);) { if (c == '"') break; c = ReadCharLiteral(&buf, c, p, &i); diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index 733c9e660..b91da3889 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -239,7 +239,11 @@ void LoadRelationships(int argc, char *argv[]) { buf += PAGESIZE; buf[-1] = '\n'; for (i = optind; i < argc; ++i) { - CHECK_NOTNULL((finpaths = fopen(argv[i], "r"))); + if (!(finpaths = fopen(argv[i], "r"))) { + fprintf(stderr, "\n\e[1mERROR: %s FAILED BECAUSE %s CAUSED %m\e[0m\n\n", + argv[0], argv[i]); + exit(1); + } while (getline(&line, &linecap, finpaths) != -1) { src = chomp(line); if (ShouldSkipSource(src)) continue; diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 510bbca7c..cffbdf1c5 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -44,6 +44,7 @@ #include "libc/sysv/consts/inaddr.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/itimer.h" +#include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" @@ -241,6 +242,7 @@ static struct Assets { static struct Shared { int workers; long double nowish; + long double lastreindex; long double lastmeltdown; char currentdate[32]; struct rusage server; @@ -318,6 +320,7 @@ static struct Shared { long readresets; long readtimeouts; long redirects; + long reindexes; long reloads; long rewrites; long serveroptions; @@ -379,13 +382,14 @@ static uint32_t clientaddrsize; static lua_State *L; static size_t zsize; static char *content; -static uint8_t *cdir; static uint8_t *zmap; +static uint8_t *zcdir; static size_t hdrsize; static size_t msgsize; static size_t amtread; static char *extrahdrs; static char *luaheaderp; +static const char *zpath; static const char *brand; static const char *pidpath; static const char *logpath; @@ -408,6 +412,7 @@ static struct Url url; static struct HttpRequest msg; static char slashpath[PATH_MAX]; +static struct stat zst; static long double startread; static long double lastrefresh; static long double startserver; @@ -1125,7 +1130,7 @@ static void Daemonize(void) { if ((pid = fork()) > 0) _exit(0); umask(0); if (pidpath) { - fd = open(pidpath, O_CREAT | O_EXCL | O_WRONLY, 0644); + fd = open(pidpath, O_CREAT | O_WRONLY, 0644); write(fd, ibuf, uint64toarray_radix10(getpid(), ibuf)); close(fd); } @@ -1242,7 +1247,8 @@ static void ReportWorkerResources(int pid, struct rusage *ru) { AppendResourceReport(ru, "\n"); if (outbuf.n) { if ((s = IndentLines(outbuf.p, outbuf.n - 1, 0, 1))) { - LOGF("resource report for pid %d\n%s", pid, s); + flogf(kLogInfo, __FILE__, __LINE__, NULL, + "resource report for pid %d\n%s", pid, s); free(s); } ClearOutput(); @@ -1411,21 +1417,20 @@ static bool IsCompressionMethodSupported(int method) { static void IndexAssets(void) { int64_t lm; - uint64_t cf, lf; + uint64_t cf; struct Asset *p; uint32_t i, n, m, step, hash; CHECK_GE(HASH_LOAD_FACTOR, 2); - CHECK(READ32LE(cdir) == kZipCdir64HdrMagic || - READ32LE(cdir) == kZipCdirHdrMagic); - n = GetZipCdirRecords(cdir); + CHECK(READ32LE(zcdir) == kZipCdir64HdrMagic || + READ32LE(zcdir) == kZipCdirHdrMagic); + n = GetZipCdirRecords(zcdir); m = roundup2pow(MAX(1, n) * HASH_LOAD_FACTOR); p = xcalloc(m, sizeof(struct Asset)); - for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) { + for (cf = GetZipCdirOffset(zcdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) { CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf)); - lf = GetZipCfileOffset(zmap + cf); - if (!IsCompressionMethodSupported(ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf))) { + if (!IsCompressionMethodSupported(ZIP_CFILE_COMPRESSIONMETHOD(zmap + cf))) { LOGF("don't understand zip compression method %d used by %`'.*s", - ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf), + ZIP_CFILE_COMPRESSIONMETHOD(zmap + cf), ZIP_CFILE_NAMESIZE(zmap + cf), ZIP_CFILE_NAME(zmap + cf)); continue; } @@ -1437,8 +1442,8 @@ static void IndexAssets(void) { } while (p[i].hash); lm = GetZipCfileLastModified(zmap + cf); p[i].hash = hash; - p[i].lf = lf; p[i].cf = cf; + p[i].lf = GetZipCfileOffset(zmap + cf); p[i].istext = !!(ZIP_CFILE_INTERNALATTRIBUTES(zmap + cf) & kZipIattrText); p[i].lastmodified = lm; p[i].lastmodifiedstr = FormatUnixHttpDateTime(xmalloc(30), lm); @@ -1447,17 +1452,17 @@ static void IndexAssets(void) { assets.n = m; } -static void OpenZip(const char *path) { +static void OpenZip(void) { int fd; uint8_t *p; - struct stat st; - CHECK_NE(-1, (fd = open(path, O_RDONLY))); - CHECK_NE(-1, fstat(fd, &st)); - CHECK((zsize = st.st_size)); + if (zmap) munmap(zmap, zsize); + CHECK_NE(-1, (fd = open(zpath, O_RDONLY))); + CHECK_NE(-1, fstat(fd, &zst)); + CHECK((zsize = zst.st_size)); CHECK_NE(MAP_FAILED, (zmap = mmap(NULL, zsize, PROT_READ, MAP_SHARED, fd, 0))); - CHECK_NOTNULL((cdir = GetZipCdir(zmap, zsize))); - if (endswith(path, ".com.dbg") && (p = memmem(zmap, zsize, "MZqFpD", 6))) { + CHECK_NOTNULL((zcdir = GetZipCdir(zmap, zsize))); + if (endswith(zpath, ".com.dbg") && (p = memmem(zmap, zsize, "MZqFpD", 6))) { zsize -= p - zmap; zmap = p; } @@ -1472,8 +1477,8 @@ static struct Asset *GetAssetZip(const char *path, size_t pathlen) { i = (hash + (step * (step + 1)) >> 1) & (assets.n - 1); if (!assets.p[i].hash) return NULL; if (hash == assets.p[i].hash && - pathlen == ZIP_LFILE_NAMESIZE(zmap + assets.p[i].lf) && - memcmp(path, ZIP_LFILE_NAME(zmap + assets.p[i].lf), pathlen) == 0) { + pathlen == ZIP_CFILE_NAMESIZE(zmap + assets.p[i].cf) && + memcmp(path, ZIP_CFILE_NAME(zmap + assets.p[i].cf), pathlen) == 0) { return &assets.p[i]; } } @@ -1695,7 +1700,7 @@ static void AppendLogo(void) { struct Asset *a; if ((a = GetAsset("/redbean.png", 12)) && (p = LoadAsset(a, &n))) { q = EncodeBase64(p, n, &n); - Append("\"[logo]\"\r\n"); free(q); @@ -2030,13 +2035,13 @@ static int GetOctalWidth(int x) { return !x ? 1 : x < 8 ? 2 : 1 + bsr(x) / 3; } -static const char *DescribeCompressionRatio(char rb[8], uint64_t lf) { +static const char *DescribeCompressionRatio(char rb[8], uint64_t cf) { long percent; - if (ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf) == kZipCompressionNone) { + if (ZIP_CFILE_COMPRESSIONMETHOD(zmap + cf) == kZipCompressionNone) { return "n/a"; } else { - percent = lround(100 - (double)GetZipLfileCompressedSize(zmap + lf) / - GetZipLfileUncompressedSize(zmap + lf) * 100); + percent = lround(100 - (double)GetZipCfileCompressedSize(zmap + cf) / + GetZipCfileUncompressedSize(zmap + cf) * 100); sprintf(rb, "%ld%%", MIN(999, MAX(-999, percent))); return rb; } @@ -2080,23 +2085,23 @@ td { padding-right: 3em; }\r\n\ "
\r\n" "\r\n" "
\r\n",
-         strnlen(GetZipCdirComment(cdir), GetZipCdirCommentSize(cdir)),
-         GetZipCdirComment(cdir));
+         strnlen(GetZipCdirComment(zcdir), GetZipCdirCommentSize(zcdir)),
+         GetZipCdirComment(zcdir));
   memset(w, 0, sizeof(w));
-  n = GetZipCdirRecords(cdir);
-  for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
+  n = GetZipCdirRecords(zcdir);
+  for (cf = GetZipCdirOffset(zcdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
     CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
     lf = GetZipCfileOffset(zmap + cf);
     path = GetAssetPath(cf, &pathlen);
     if (!IsHiddenPath(path)) {
       w[0] = min(80, max(w[0], strwidth(path, 0) + 2));
       w[1] = max(w[1], GetOctalWidth(GetZipCfileMode(zmap + cf)));
-      w[2] = max(w[2], GetDecimalWidth(GetZipLfileUncompressedSize(zmap + lf)));
+      w[2] = max(w[2], GetDecimalWidth(GetZipCfileUncompressedSize(zmap + cf)));
     }
     free(path);
   }
-  n = GetZipCdirRecords(cdir);
-  for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
+  n = GetZipCdirRecords(zcdir);
+  for (cf = GetZipCdirOffset(zcdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
     CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
     lf = GetZipCfileOffset(zmap + cf);
     path = GetAssetPath(cf, &pathlen);
@@ -2113,17 +2118,17 @@ td { padding-right: 3em; }\r\n\
       localtime_r(&lastmod, &tm);
       strftime(tb, sizeof(tb), "%Y-%m-%d %H:%M:%S %Z", &tm);
       if (IsCompressionMethodSupported(
-              ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf)) &&
+              ZIP_CFILE_COMPRESSIONMETHOD(zmap + cf)) &&
           IsAcceptablePath(path, pathlen)) {
         Append("%-*.*s %s  %0*o %4s  %,*ld  %'s\r\n",
                rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1],
-               GetZipCfileMode(zmap + cf), DescribeCompressionRatio(rb, lf),
-               w[2], GetZipLfileUncompressedSize(zmap + lf), rp[3]);
+               GetZipCfileMode(zmap + cf), DescribeCompressionRatio(rb, cf),
+               w[2], GetZipCfileUncompressedSize(zmap + cf), rp[3]);
       } else {
         Append("%-*.*s %s  %0*o %4s  %,*ld  %'s\r\n", w[0], rn[4], rp[4], tb,
                w[1], GetZipCfileMode(zmap + cf),
-               DescribeCompressionRatio(rb, lf), w[2],
-               GetZipLfileUncompressedSize(zmap + lf), rp[3]);
+               DescribeCompressionRatio(rb, cf), w[2],
+               GetZipCfileUncompressedSize(zmap + cf), rp[3]);
       }
       free(rp[4]);
       free(rp[3]);
@@ -2299,6 +2304,7 @@ static char *ServeStatusz(void) {
   AppendLong1("readresets", shared->readresets);
   AppendLong1("readtimeouts", shared->readtimeouts);
   AppendLong1("redirects", shared->redirects);
+  AppendLong1("reindexes", shared->reindexes);
   AppendLong1("reloads", shared->reloads);
   AppendLong1("rewrites", shared->rewrites);
   AppendLong1("serveroptions", shared->serveroptions);
@@ -2507,6 +2513,13 @@ static char *Route(const char *host, size_t hostlen, const char *path,
   }
 }
 
+static void Reindex(void) {
+  LockInc(&shared->reindexes);
+  LOGF("reindexing");
+  OpenZip();
+  IndexAssets();
+}
+
 static const char *LuaCheckPath(lua_State *L, int idx, size_t *pathlen) {
   const char *path;
   if (lua_isnoneornil(L, idx)) {
@@ -2951,7 +2964,7 @@ static int LuaSetHeader(lua_State *L) {
   }
   switch (h) {
     case kHttpConnection:
-      if (evallen != 5 || memcmp(eval, "close", 5)) {
+      if (evallen != 5 || memcasecmp(eval, "close", 5)) {
         luaL_argerror(L, 2, "unsupported");
         unreachable;
       }
@@ -3162,7 +3175,7 @@ static int LuaHasControlCodes(lua_State *L) {
   const char *p;
   p = luaL_checklstring(L, 1, &n);
   f = LuaCheckControlFlags(L, 2);
-  lua_pushboolean(L, HasControlCodes(p, n, f));
+  lua_pushboolean(L, HasControlCodes(p, n, f) != -1);
   return 1;
 }
 
@@ -3446,8 +3459,8 @@ static int LuaGetZipPaths(lua_State *L) {
   size_t i, n, pathlen;
   lua_newtable(L);
   i = 0;
-  n = GetZipCdirRecords(cdir);
-  for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
+  n = GetZipCdirRecords(zcdir);
+  for (cf = GetZipCdirOffset(zcdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
     CHECK_EQ(kZipCfileHdrMagic, ZIP_CFILE_MAGIC(zmap + cf));
     path = GetAssetPath(cf, &pathlen);
     lua_pushlstring(L, path, pathlen);
@@ -3847,13 +3860,31 @@ static void LuaReload(void) {
 }
 
 static void HandleReload(void) {
+  LockInc(&shared->reloads);
   LOGF("reloading");
+  Reindex();
   LuaReload();
 }
 
+static bool ZipCdirChanged(void) {
+  struct stat st;
+  if (!IsZipCdir32(zmap, zsize, zcdir - zmap) &&
+      !IsZipCdir64(zmap, zsize, zcdir - zmap)) {
+    return true;
+  }
+  if (stat(zpath, &st) != -1 && st.st_ino != zst.st_ino) {
+    return true;
+  }
+  return false;
+}
+
 static void HandleHeartbeat(void) {
   if (nowl() - lastrefresh > 60 * 60) RefreshTime();
   UpdateCurrentDate(nowl());
+  if (ZipCdirChanged()) {
+    shared->lastreindex = nowl();
+    kill(0, SIGUSR1);
+  }
   getrusage(RUSAGE_SELF, &shared->server);
 #ifndef STATIC
   LuaRun("/.heartbeat.lua");
@@ -4344,6 +4375,10 @@ static void HandleMessages(void) {
         LogClose(DescribeClose());
         return;
       }
+      if (invalidated) {
+        HandleReload();
+        invalidated = false;
+      }
     }
     if (msgsize == amtread) {
       amtread = 0;
@@ -4363,6 +4398,10 @@ static void HandleMessages(void) {
       }
     }
     CollectGarbage();
+    if (invalidated) {
+      HandleReload();
+      invalidated = false;
+    }
   }
 }
 
@@ -4485,7 +4524,7 @@ static void TuneSockets(void) {
   setsockopt(server, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
 }
 
-static void RestoreApe(const char *prog) {
+static void RestoreApe(void) {
   char *p;
   size_t n;
   struct Asset *a;
@@ -4493,7 +4532,7 @@ static void RestoreApe(const char *prog) {
   if (IsWindows()) return; /* TODO */
   if (IsOpenbsd()) return; /* TODO */
   if (IsNetbsd()) return;  /* TODO */
-  if (endswith(prog, ".com.dbg")) return;
+  if (endswith(zpath, ".com.dbg")) return;
   close(OpenExecutable());
   if ((a = GetAssetZip("/.ape", 5)) && (p = LoadAsset(a, &n))) {
     mprotect(ape_rom_vaddr, PAGESIZE, PROT_READ | PROT_WRITE);
@@ -4506,7 +4545,7 @@ static void RestoreApe(const char *prog) {
   }
 }
 
-void RedBean(int argc, char *argv[], const char *prog) {
+void RedBean(int argc, char *argv[]) {
   uint32_t addrsize;
   gmtoff = GetGmtOffset((lastrefresh = startserver = nowl()));
   CHECK_GT(CLK_TCK, 0);
@@ -4514,9 +4553,10 @@ void RedBean(int argc, char *argv[], const char *prog) {
            (shared = mmap(NULL, ROUNDUP(sizeof(struct Shared), FRAMESIZE),
                           PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
                           -1, 0)));
-  OpenZip(prog);
+  zpath = (const char *)getauxval(AT_EXECFN);
+  OpenZip();
   IndexAssets();
-  RestoreApe(prog);
+  RestoreApe();
   SetDefaults();
   GetOpts(argc, argv);
   LuaInit();
@@ -4530,9 +4570,6 @@ void RedBean(int argc, char *argv[], const char *prog) {
   xsigaction(SIGALRM, OnAlrm, 0, 0, 0);
   xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0);
   /* TODO(jart): SIGXCPU and SIGXFSZ */
-  if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) {
-    heartless = true;
-  }
   server = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
   CHECK_NE(-1, server);
   TuneSockets();
@@ -4556,16 +4593,23 @@ void RedBean(int argc, char *argv[], const char *prog) {
     printf("%d\n", ntohs(serveraddr.sin_port));
     fflush(stdout);
   }
-  if (daemonize) Daemonize();
+  if (daemonize) {
+    Daemonize();
+  } else {
+    setpgid(getpid(), getpid());
+  }
+  if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) {
+    heartless = true;
+  }
   UpdateCurrentDate(nowl());
   freelist.c = 8;
   freelist.p = xcalloc(freelist.c, sizeof(*freelist.p));
   unmaplist.c = 1;
   unmaplist.p = xcalloc(unmaplist.c, sizeof(*unmaplist.p));
   hdrbuf.n = 4 * 1024;
-  hdrbuf.p = xvalloc(hdrbuf.n);
+  hdrbuf.p = xmalloc(hdrbuf.n);
   inbuf.n = maxpayloadsize;
-  inbuf.p = xvalloc(inbuf.n);
+  inbuf.p = xmalloc(inbuf.n);
   while (!terminated) {
     if (zombied) {
       ReapZombies();
@@ -4603,6 +4647,6 @@ int main(int argc, char *argv[]) {
     setenv("GDB", "", true);
     showcrashreports();
   }
-  RedBean(argc, argv, (const char *)getauxval(AT_EXECFN));
+  RedBean(argc, argv);
   return 0;
 }
diff --git a/tool/viz/life.c b/tool/viz/life.c
index 672516b48..2965a67c0 100644
--- a/tool/viz/life.c
+++ b/tool/viz/life.c
@@ -428,6 +428,7 @@ static void OnUnzoom(long y, long x) {
 }
 
 static void OnMouseLeftDrag(long y, long x) {
+  int i;
   if (y == save_y && x == save_x) return;
   save_y = y;
   save_x = x;
@@ -440,7 +441,10 @@ static void OnMouseLeftDrag(long y, long x) {
   if (erase) {
     Unset(y, x);
   } else {
-    Set(y, x);
+    for (i = 0; i < (2 << zoom); ++i) {
+      Set(y + (rand() % (zoom + 1)) - (rand() % (zoom + 1)),
+          x + (rand() % (zoom + 1)) - (rand() % (zoom + 1)));
+    }
   }
 }