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("\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", - 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))); + } } }