diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com
index 48e4822ec..8ca98c4da 100755
Binary files a/build/bootstrap/compile.com and b/build/bootstrap/compile.com differ
diff --git a/build/definitions.mk b/build/definitions.mk
index 7d94b887f..82fdb1d10 100644
--- a/build/definitions.mk
+++ b/build/definitions.mk
@@ -77,7 +77,8 @@ PWD := $(shell pwd)
IMAGE_BASE_VIRTUAL ?= 0x400000
HELLO := $(shell build/hello)
TMPDIR := $(shell build/findtmp)
-COMPILE := $(shell build/getcompile) -V$(shell build/getccversion $(CC))
+SPAWNER := $(shell build/getcompile) -V$(shell build/getccversion $(CC))
+COMPILE = $(SPAWNER) $(QUOTA)
export ADDR2LINE
export LC_ALL
diff --git a/libc/calls/setrlimit.c b/libc/calls/setrlimit.c
index fd219839b..4c4a67c24 100644
--- a/libc/calls/setrlimit.c
+++ b/libc/calls/setrlimit.c
@@ -18,7 +18,6 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
-#include "libc/dce.h"
#include "libc/sysv/errfuns.h"
/**
@@ -28,6 +27,7 @@
* @param rlim specifies new resource limit
* @return 0 on success or -1 w/ errno
* @see libc/sysv/consts.sh
+ * @vforksafe
*/
int setrlimit(int resource, const struct rlimit *rlim) {
if (resource == 127) return einval();
diff --git a/libc/fmt/atoi.c b/libc/fmt/atoi.c
index caf1a2ca9..d7392c132 100644
--- a/libc/fmt/atoi.c
+++ b/libc/fmt/atoi.c
@@ -16,21 +16,34 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
+#include "libc/str/str.h"
/**
- * Decodes decimal number from ASCII string.
+ * Decodes decimal integer from ASCII string.
*
- * @param s is a non-null NUL-terminated string
- * @return the decoded signed saturated number
- * @note calling strtoimax() directly with base 0 permits greater
- * flexibility in terms of inputs
+ * @param s is a non-null nul-terminated string
+ * @return the decoded signed saturated integer
*/
int atoi(const char *s) {
- int res;
- res = strtoimax(s, NULL, 10);
- if (res < INT_MIN) return INT_MIN;
- if (res > INT_MAX) return INT_MAX;
- return res;
+ int x, c, d;
+ do {
+ c = *s++;
+ } while (c == ' ' || c == '\t');
+ d = c == '-' ? -1 : 1;
+ if (c == '-' || c == '+') c = *s++;
+ for (x = 0; isdigit(c); c = *s++) {
+ if (__builtin_mul_overflow(x, 10, &x) ||
+ __builtin_add_overflow(x, (c - '0') * d, &x)) {
+ errno = ERANGE;
+ if (d > 0) {
+ return INT_MAX;
+ } else {
+ return INT_MIN;
+ }
+ }
+ }
+ return x;
}
diff --git a/libc/fmt/atol.c b/libc/fmt/atol.c
index 2d07474ed..da13f1484 100644
--- a/libc/fmt/atol.c
+++ b/libc/fmt/atol.c
@@ -16,13 +16,35 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
+#include "libc/str/str.h"
+/**
+ * Decodes decimal integer from ASCII string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @return the decoded signed saturated integer
+ */
long atol(const char *s) {
- long res;
- res = strtoimax(s, NULL, 10);
- if (res < LONG_MIN) return LONG_MIN;
- if (res > LONG_MAX) return LONG_MAX;
- return res;
+ long x;
+ int c, d;
+ do {
+ c = *s++;
+ } while (c == ' ' || c == '\t');
+ d = c == '-' ? -1 : 1;
+ if (c == '-' || c == '+') c = *s++;
+ for (x = 0; isdigit(c); c = *s++) {
+ if (__builtin_mul_overflow(x, 10, &x) ||
+ __builtin_add_overflow(x, (c - '0') * d, &x)) {
+ errno = ERANGE;
+ if (d > 0) {
+ return LONG_MAX;
+ } else {
+ return LONG_MIN;
+ }
+ }
+ }
+ return x;
}
diff --git a/libc/fmt/atoll.c b/libc/fmt/atoll.c
index 33b9363f8..11b25e768 100644
--- a/libc/fmt/atoll.c
+++ b/libc/fmt/atoll.c
@@ -19,10 +19,13 @@
#include "libc/fmt/conv.h"
#include "libc/limits.h"
+/**
+ * Decodes decimal number from ASCII string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @return the decoded signed saturated integer
+ */
long long atoll(const char *s) {
- long long res;
- res = strtoimax(s, NULL, 10);
- if (res < LONG_LONG_MIN) return LONG_LONG_MIN;
- if (res > LONG_LONG_MAX) return LONG_LONG_MAX;
- return res;
+ _Static_assert(LONG_MAX == LONG_LONG_MAX, "need atoll impl");
+ return atol(s);
}
diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h
index a4e3894b0..7b4f47d8f 100644
--- a/libc/fmt/conv.h
+++ b/libc/fmt/conv.h
@@ -29,8 +29,11 @@ intmax_t div10(intmax_t, unsigned *) hidden;
intmax_t strtoimax(const char *, char **, int) paramsnonnull((1));
uintmax_t strtoumax(const char *, char **, int) paramsnonnull((1));
intmax_t wcstoimax(const wchar_t *, wchar_t **, int);
+uintmax_t wcstoumax(const wchar_t *, wchar_t **, int);
long wcstol(const wchar_t *, wchar_t **, int);
+unsigned long wcstoul(const wchar_t *, wchar_t **, int);
long strtol(const char *, char **, int) paramsnonnull((1)) libcesque;
+long sizetol(const char *, long) paramsnonnull() libcesque;
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § conversion » time ─╬─│┼
diff --git a/libc/fmt/fmt.mk b/libc/fmt/fmt.mk
index e73cc9299..221f6367c 100644
--- a/libc/fmt/fmt.mk
+++ b/libc/fmt/fmt.mk
@@ -68,6 +68,18 @@ o/$(MODE)/libc/fmt/filetimetotimeval.o: \
OVERRIDE_CFLAGS += \
-O3
+o/$(MODE)/libc/fmt/atoi.o \
+o/$(MODE)/libc/fmt/strtol.o \
+o/$(MODE)/libc/fmt/strtoul.o \
+o/$(MODE)/libc/fmt/wcstol.o \
+o/$(MODE)/libc/fmt/wcstoul.o \
+o/$(MODE)/libc/fmt/strtoimax.o \
+o/$(MODE)/libc/fmt/strtoumax.o \
+o/$(MODE)/libc/fmt/wcstoimax.o \
+o/$(MODE)/libc/fmt/wcstoumax.o: \
+ OVERRIDE_CFLAGS += \
+ -Os
+
LIBC_FMT_LIBS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)))
LIBC_FMT_SRCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_SRCS))
LIBC_FMT_HDRS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_HDRS))
diff --git a/libc/fmt/sizetol.c b/libc/fmt/sizetol.c
new file mode 100644
index 000000000..10ef06b8e
--- /dev/null
+++ b/libc/fmt/sizetol.c
@@ -0,0 +1,90 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/fmt/conv.h"
+#include "libc/fmt/fmt.h"
+
+static int GetExponent(int c) {
+ switch (c) {
+ case '\0':
+ case ' ':
+ case '\t':
+ return 0;
+ case 'k':
+ case 'K':
+ return 1;
+ case 'm':
+ case 'M':
+ return 2;
+ case 'g':
+ case 'G':
+ return 3;
+ case 't':
+ case 'T':
+ return 4;
+ case 'p':
+ case 'P':
+ return 5;
+ case 'e':
+ case 'E':
+ return 6;
+ default:
+ return -1;
+ }
+}
+
+/**
+ * Converts size string to long.
+ *
+ * The following unit suffixes may be used
+ *
+ * - `k` or `K` for kilo (multiply by 𝑏¹)
+ * - `m` or `M` for mega (multiply by 𝑏²)
+ * - `g` or `G` for giga (multiply by 𝑏³)
+ * - `t` or `T` for tera (multiply by 𝑏⁴)
+ * - `p` or `P` for peta (multiply by 𝑏⁵)
+ * - `e` or `E` for exa (multiply by 𝑏⁶)
+ *
+ * If a permitted alpha character is supplied, then any additional
+ * characters after it (e.g. kbit, Mibit, TiB) are ignored. Spaces
+ * before the integer are ignored, and overflows will be detected.
+ *
+ * @param s is non-null nul-terminated input string
+ * @param b is multiplier which should be 1000 or 1024
+ * @return size greater than or equal 0 or -1 on error
+ */
+long sizetol(const char *s, long b) {
+ long x;
+ int c, e;
+ do {
+ c = *s++;
+ } while (c == ' ' || c == '\t');
+ if (!isdigit(c)) return -1;
+ x = 0;
+ do {
+ if (__builtin_mul_overflow(x, 10, &x) ||
+ __builtin_add_overflow(x, c - '0', &x)) {
+ return -1;
+ }
+ } while (isdigit((c = *s++)));
+ if ((e = GetExponent(c)) == -1) return -1;
+ while (e--) {
+ if (__builtin_mul_overflow(x, b, &x)) return -1;
+ }
+ return x;
+}
diff --git a/libc/fmt/strtoimax.c b/libc/fmt/strtoimax.c
index 11a81ba2d..25dbfffb3 100644
--- a/libc/fmt/strtoimax.c
+++ b/libc/fmt/strtoimax.c
@@ -16,103 +16,42 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/fmt/conv.h"
#include "libc/errno.h"
+#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
#include "libc/limits.h"
-#include "libc/nexgen32e/bsr.h"
#include "libc/str/str.h"
/**
* Decodes 128-bit signed integer from ASCII string.
*
- * @param s is a non-NULL NUL-terminated string
- * @param endptr if non-NULL will always receive a pointer to the char
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
* following the last one this function processed, which is usually
* the NUL byte, or in the case of invalid strings, would point to
* the first invalid character
* @param base can be anywhere between [2,36] or 0 to auto-detect based
* on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
- * decimal (base 10) by default.
- * @return the decoded saturated number
- * @see strtoumax
+ * decimal (base 10) by default
+ * @return decoded saturated integer
+ * @see strtoumax()
*/
intmax_t strtoimax(const char *s, char **endptr, int base) {
- bool neg;
- uintmax_t x;
- intmax_t res;
- unsigned diglet, bits;
-
- x = 0;
- bits = 0;
- neg = false;
-
- while (isspace(*s)) {
- s++;
- }
-
- switch (*s) {
- case '-':
- neg = true;
- /* 𝑠𝑙𝑖𝑑𝑒 */
- case '+':
- s++;
- break;
- default:
- break;
- }
-
- if (!(2 <= base && base <= 36)) {
- if (*s == '0') {
- s++;
- if (*s == 'x' || *s == 'X') {
- s++;
- base = 16;
- } else if (*s == 'b' || *s == 'B') {
- s++;
- base = 2;
- } else {
- base = 8;
+ int d, c = *s;
+ intmax_t x = 0;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ if (__builtin_mul_overflow(x, base, &x) ||
+ __builtin_add_overflow(x, c * d, &x)) {
+ x = d > 0 ? INTMAX_MAX : INTMAX_MIN;
+ errno = ERANGE;
+ break;
}
- } else {
- base = 10;
- }
- } else if (*s == '0') {
- ++s;
- if (base == 2 && (*s == 'b' || *s == 'B')) ++s;
- if (base == 16 && (*s == 'x' || *s == 'X')) ++s;
+ } while ((c = kBase36[*++s & 255]) && --c < base);
}
-
- for (;;) {
- diglet = kBase36[*s & 0xff];
- if (!diglet || diglet > base) break;
- diglet -= 1;
- if (!x || (bits = (diglet ? bsr(diglet) : 0) + bsrmax(x * base)) < 127) {
- s++;
- x *= base;
- x += diglet;
- } else if (neg) {
- if (bits == 127) {
- x *= base;
- x += diglet;
- if (x == INTMAX_MIN) s++;
- }
- x = INTMAX_MIN;
- errno = ERANGE;
- break;
- } else {
- x = INTMAX_MAX;
- errno = ERANGE;
- break;
- }
- }
-
if (endptr) *endptr = s;
-
- if (neg) {
- res = -x;
- } else {
- res = x;
- }
-
- return res;
+ return x;
}
diff --git a/libc/fmt/strtol.c b/libc/fmt/strtol.c
index f0892fbfa..9e28c99ef 100644
--- a/libc/fmt/strtol.c
+++ b/libc/fmt/strtol.c
@@ -16,25 +16,41 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/fmt/conv.h"
#include "libc/errno.h"
+#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
#include "libc/limits.h"
+#include "libc/str/str.h"
/**
- * Converts string to number.
+ * Decodes signed integer from ASCII string.
*
- * @param optional_base is recommended as 0 for flexidecimal
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return the decoded signed saturated number
*/
-long strtol(const char *s, char **opt_out_end, int optional_base) {
- intmax_t res;
- res = strtoimax(s, opt_out_end, optional_base);
- if (res < LONG_MIN) {
- errno = ERANGE;
- return LONG_MIN;
+long strtol(const char *s, char **endptr, int base) {
+ long x = 0;
+ int d, c = *s;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ if (__builtin_mul_overflow(x, base, &x) ||
+ __builtin_add_overflow(x, c * d, &x)) {
+ x = d > 0 ? LONG_MAX : LONG_MIN;
+ errno = ERANGE;
+ break;
+ }
+ } while ((c = kBase36[*++s & 255]) && --c < base);
}
- if (res > LONG_MAX) {
- errno = ERANGE;
- return LONG_MAX;
- }
- return res;
+ if (endptr) *endptr = s;
+ return x;
}
diff --git a/libc/fmt/strtol.internal.h b/libc/fmt/strtol.internal.h
new file mode 100644
index 000000000..0ffae2c93
--- /dev/null
+++ b/libc/fmt/strtol.internal.h
@@ -0,0 +1,35 @@
+#ifndef COSMOPOLITAN_LIBC_FMT_STRTOL_H_
+#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_
+
+#define CONSUME_SPACES(s, c) \
+ while (c == ' ' || c == '\t') c = *++s
+
+#define GET_SIGN(s, c, d) \
+ d = c == '-' ? -1 : 1; \
+ if (c == '-' || c == '+') c = *++s
+
+#define GET_RADIX(s, c, r) \
+ if (!(2 <= r && r <= 36)) { \
+ if (c == '0') { \
+ c = *++s; \
+ if (c == 'x' || c == 'X') { \
+ c = *++s; \
+ r = 16; \
+ } else if (c == 'b' || c == 'B') { \
+ c = *++s; \
+ r = 2; \
+ } else { \
+ r = 8; \
+ } \
+ } else { \
+ r = 10; \
+ } \
+ } else if (c == '0') { \
+ c = *++s; \
+ if ((r == 2 && (c == 'b' || c == 'B')) || \
+ (r == 16 && (c == 'x' || c == 'X'))) { \
+ c = *++s; \
+ } \
+ }
+
+#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */
diff --git a/libc/fmt/strtoll.c b/libc/fmt/strtoll.c
index 2ed524c15..31d311b2c 100644
--- a/libc/fmt/strtoll.c
+++ b/libc/fmt/strtoll.c
@@ -17,19 +17,22 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/conv.h"
-#include "libc/errno.h"
#include "libc/limits.h"
-long long strtoll(const char *s, char **endptr, int optional_base) {
- intmax_t res;
- res = strtoimax(s, endptr, optional_base);
- if (res < LONG_LONG_MIN) {
- errno = ERANGE;
- return LONG_LONG_MIN;
- }
- if (res > LONG_LONG_MAX) {
- errno = ERANGE;
- return LONG_LONG_MAX;
- }
- return res;
+/**
+ * Decodes signed integer from ASCII string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded signed saturated integer
+ */
+long long strtoll(const char *s, char **endptr, int base) {
+ _Static_assert(LONG_MAX == LONG_LONG_MAX, "need strtoll impl");
+ return strtoll(s, endptr, base);
}
diff --git a/libc/fmt/strtoul.c b/libc/fmt/strtoul.c
index 1e9ef3a41..3adb34ad0 100644
--- a/libc/fmt/strtoul.c
+++ b/libc/fmt/strtoul.c
@@ -16,20 +16,36 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/fmt/conv.h"
#include "libc/errno.h"
-#include "libc/limits.h"
+#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
+#include "libc/str/str.h"
-unsigned long strtoul(const char *s, char **endptr, int optional_base) {
- intmax_t res;
- res = strtoimax(s, endptr, optional_base);
- if (res < ULONG_MIN) {
- errno = ERANGE;
- return ULONG_MIN;
+/**
+ * Decodes unsigned integer from ASCII string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded integer mod 2⁶⁴ negated if leading `-`
+ */
+unsigned long strtoul(const char *s, char **endptr, int base) {
+ int d, c = *s;
+ unsigned long x = 0;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ x *= base;
+ x += c;
+ } while ((c = kBase36[*++s & 255]) && --c < base);
}
- if (res > ULONG_MAX) {
- errno = ERANGE;
- return ULONG_MAX;
- }
- return res;
+ if (endptr) *endptr = s;
+ return d > 0 ? x : -x;
}
diff --git a/libc/fmt/strtoull.c b/libc/fmt/strtoull.c
index 94d4c3071..4d2721e89 100644
--- a/libc/fmt/strtoull.c
+++ b/libc/fmt/strtoull.c
@@ -17,19 +17,22 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/conv.h"
-#include "libc/errno.h"
#include "libc/limits.h"
-unsigned long long strtoull(const char *s, char **endptr, int optional_base) {
- intmax_t res;
- res = strtoimax(s, endptr, optional_base);
- if (res < ULONG_LONG_MIN) {
- errno = ERANGE;
- return ULONG_LONG_MIN;
- }
- if (res > ULONG_LONG_MAX) {
- errno = ERANGE;
- return ULONG_LONG_MAX;
- }
- return res;
+/**
+ * Decodes unsigned integer from ASCII string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded integer mod 2⁶⁴ negated if leading `-`
+ */
+unsigned long long strtoull(const char *s, char **endptr, int base) {
+ _Static_assert(LONG_MAX == LONG_LONG_MAX, "need strtoull impl");
+ return strtoul(s, endptr, base);
}
diff --git a/libc/fmt/strtoumax.c b/libc/fmt/strtoumax.c
index e93ef95c4..e2b937515 100644
--- a/libc/fmt/strtoumax.c
+++ b/libc/fmt/strtoumax.c
@@ -17,54 +17,35 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
#include "libc/str/str.h"
/**
* Decodes 128-bit unsigned integer from ASCII string.
*
- * This is a more restricted form of strtoimax() that's useful for folks
- * needing to decode numbers in the range [1^127, 1^128).
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded integer mod 2¹²⁸ negated if leading `-`
+ * @see strtoimax()
*/
uintmax_t strtoumax(const char *s, char **endptr, int base) {
- const unsigned char *p = (const unsigned char *)s;
- unsigned diglet;
- uintmax_t res;
-
- res = 0;
-
- while (isspace(*p)) {
- p++;
+ int d, c = *s;
+ uintmax_t x = 0;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ x *= base;
+ x += c;
+ } while ((c = kBase36[*++s & 255]) && --c < base);
}
-
- if (!base) {
- if (*p == '0') {
- p++;
- if (*p == 'x' || *p == 'X') {
- p++;
- base = 16;
- } else if (*p == 'b' || *p == 'B') {
- p++;
- base = 2;
- } else {
- base = 8;
- }
- } else {
- base = 10;
- }
- } else if (*p == '0') {
- ++p;
- if (base == 2 && (*p == 'b' || *p == 'B')) ++p;
- if (base == 16 && (*p == 'x' || *p == 'X')) ++p;
- }
-
- for (;;) {
- diglet = kBase36[*p];
- if (!diglet || diglet > base) break;
- p++;
- res *= base;
- res += diglet - 1;
- }
-
- if (endptr) *endptr = (char *)p;
- return res;
+ if (endptr) *endptr = s;
+ return d > 0 ? x : -x;
}
diff --git a/libc/fmt/wcstoimax.c b/libc/fmt/wcstoimax.c
index 3c2e6a082..5fe859c44 100644
--- a/libc/fmt/wcstoimax.c
+++ b/libc/fmt/wcstoimax.c
@@ -16,53 +16,46 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
+#include "libc/limits.h"
#include "libc/str/str.h"
+/**
+ * Decodes 128-bit signed integer from wide string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded saturated integer
+ * @see strtoumax()
+ */
intmax_t wcstoimax(const wchar_t *s, wchar_t **endptr, int base) {
- intmax_t res = 0;
- int neg = 0;
-
- while (iswspace(*s)) {
- s++;
- }
-
- switch (*s) {
- case '-':
- neg = 1;
- /* fallthrough */
- case '+':
- s++;
- break;
- default:
- break;
- }
-
- if (!base) {
- if (*s == '0') {
- s++;
- if (*s == 'x' || *s == 'X') {
- s++;
- base = 16;
- } else if (*s == 'b' || *s == 'B') {
- s++;
- base = 2;
- } else {
- base = 8;
+ int c, d;
+ intmax_t x;
+ c = *s;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ x = 0;
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ if (__builtin_mul_overflow(x, base, &x) ||
+ __builtin_add_overflow(x, c * d, &x)) {
+ x = d > 0 ? INTMAX_MAX : INTMAX_MIN;
+ errno = ERANGE;
+ break;
}
- } else {
- base = 10;
- }
+ } while ((c = kBase36[*++s & 255]) && --c < base);
}
-
- for (;;) {
- unsigned diglet = kBase36[*s];
- if (!diglet || diglet > base) break;
- s++;
- res *= base; /* needs __muloti4() w/ clang */
- res -= diglet - 1;
+ if (endptr) {
+ *endptr = s;
}
-
- if (endptr) *endptr = (wchar_t *)s;
- return neg ? res : -res;
+ return x;
}
diff --git a/libc/fmt/wcstol.c b/libc/fmt/wcstol.c
index 0530dfe34..6b2abe007 100644
--- a/libc/fmt/wcstol.c
+++ b/libc/fmt/wcstol.c
@@ -16,9 +16,41 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
+#include "libc/limits.h"
#include "libc/str/str.h"
-long wcstol(const wchar_t *s, wchar_t **end, int opt_base) {
- return wcstoimax(s, end, opt_base);
+/**
+ * Decodes signed integer from wide string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return the decoded signed saturated number
+ */
+long wcstol(const wchar_t *s, wchar_t **endptr, int base) {
+ long x = 0;
+ int d, c = *s;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ if (__builtin_mul_overflow(x, base, &x) ||
+ __builtin_add_overflow(x, c * d, &x)) {
+ x = d > 0 ? LONG_MAX : LONG_MIN;
+ errno = ERANGE;
+ break;
+ }
+ } while ((c = kBase36[*++s & 255]) && --c < base);
+ }
+ if (endptr) *endptr = s;
+ return x;
}
diff --git a/test/libc/fmt/strtoumax_test.c b/libc/fmt/wcstoul.c
similarity index 63%
rename from test/libc/fmt/strtoumax_test.c
rename to libc/fmt/wcstoul.c
index 010510ddd..25bf684aa 100644
--- a/test/libc/fmt/strtoumax_test.c
+++ b/libc/fmt/wcstoul.c
@@ -16,38 +16,36 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/bits/bits.h"
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
-#include "libc/limits.h"
-#include "libc/testlib/testlib.h"
+#include "libc/fmt/strtol.internal.h"
+#include "libc/str/str.h"
-TEST(strtoumax, testZero) {
- EXPECT_EQ(UINTMAX_MIN, strtoumax("0", NULL, 0));
-}
-TEST(strtoumax, testDecimal) {
- EXPECT_EQ(123, strtoumax("123", NULL, 0));
-}
-TEST(strtoumax, testHex) {
- EXPECT_EQ(255, strtoumax("0xff", NULL, 0));
- EXPECT_EQ(255, strtoumax("0xff", NULL, 16));
-}
-TEST(strtoumax, testOctal) {
- EXPECT_EQ(123, strtoumax("0173", NULL, 0));
- EXPECT_EQ(123, strtoumax("0173", NULL, 8));
-}
-TEST(strtoumax, testBinary) {
- EXPECT_EQ(42, strtoumax("0b101010", NULL, 0));
- EXPECT_EQ(42, strtoumax("0b101010", NULL, 2));
-}
-
-TEST(strtoumax, testMaximum) {
- EXPECT_EQ(UINTMAX_MAX,
- strtoumax("340282366920938463463374607431768211455", NULL, 0));
- EXPECT_EQ(UINTMAX_MAX,
- strtoumax("0xffffffffffffffffffffffffffffffff", NULL, 0));
-}
-
-TEST(strtoumax, testTwosBane) {
- EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000,
- strtoumax("0x80000000000000000000000000000000", NULL, 0));
+/**
+ * Decodes unsigned integer from wide string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded integer mod 2⁶⁴ negated if leading `-`
+ */
+unsigned long wcstoul(const wchar_t *s, wchar_t **endptr, int base) {
+ int d, c = *s;
+ unsigned long x = 0;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ x *= base;
+ x += c;
+ } while ((c = kBase36[*++s & 255]) && --c < base);
+ }
+ if (endptr) *endptr = s;
+ return d > 0 ? x : -x;
}
diff --git a/libc/fmt/wcstoumax.c b/libc/fmt/wcstoumax.c
new file mode 100644
index 000000000..38646d9a7
--- /dev/null
+++ b/libc/fmt/wcstoumax.c
@@ -0,0 +1,55 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2020 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/fmt/conv.h"
+#include "libc/fmt/strtol.internal.h"
+#include "libc/str/str.h"
+
+/**
+ * Decodes 128-bit unsigned integer from wide string.
+ *
+ * @param s is a non-null nul-terminated string
+ * @param endptr if non-null will always receive a pointer to the char
+ * following the last one this function processed, which is usually
+ * the NUL byte, or in the case of invalid strings, would point to
+ * the first invalid character
+ * @param base can be anywhere between [2,36] or 0 to auto-detect based
+ * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
+ * decimal (base 10) by default
+ * @return decoded integer mod 2¹²⁸ negated if leading `-`
+ * @see strtoimax()
+ */
+uintmax_t wcstoumax(const wchar_t *s, wchar_t **endptr, int base) {
+ int c, d;
+ uintmax_t x;
+ c = *s;
+ CONSUME_SPACES(s, c);
+ GET_SIGN(s, c, d);
+ GET_RADIX(s, c, base);
+ x = 0;
+ if ((c = kBase36[c & 255]) && --c < base) {
+ do {
+ x *= base;
+ x += c;
+ } while ((c = kBase36[*++s & 255]) && --c < base);
+ }
+ if (endptr) {
+ *endptr = s;
+ }
+ return d > 0 ? x : -x;
+}
diff --git a/libc/log/appendresourcereport.c b/libc/log/appendresourcereport.c
new file mode 100644
index 000000000..cade76a54
--- /dev/null
+++ b/libc/log/appendresourcereport.c
@@ -0,0 +1,81 @@
+/*-*- 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/calls/struct/rusage.h"
+#include "libc/log/log.h"
+#include "libc/math.h"
+#include "libc/runtime/clktck.h"
+#include "libc/stdio/append.internal.h"
+
+/**
+ * Generates process resource usage report.
+ */
+void AppendResourceReport(char **b, struct rusage *ru, const char *nl) {
+ long utime, stime;
+ long double ticks;
+ if (ru->ru_maxrss) {
+ (appendf)(b, "ballooned to %,ldkb in size%s", ru->ru_maxrss, nl);
+ }
+ if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) |
+ (stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) {
+ ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK));
+ (appendf)(b, "needed %,ldµs cpu (%d%% kernel)%s", utime + stime,
+ (int)((long double)stime / (utime + stime) * 100), nl);
+ if (ru->ru_idrss) {
+ (appendf)(b, "needed %,ldkb memory on average%s",
+ lroundl(ru->ru_idrss / ticks), nl);
+ }
+ if (ru->ru_isrss) {
+ (appendf)(b, "needed %,ldkb stack on average%s",
+ lroundl(ru->ru_isrss / ticks), nl);
+ }
+ if (ru->ru_ixrss) {
+ (appendf)(b, "mapped %,ldkb shared on average%s",
+ lroundl(ru->ru_ixrss / ticks), nl);
+ }
+ }
+ if (ru->ru_minflt || ru->ru_majflt) {
+ (appendf)(b, "caused %,ld page faults (%d%% memcpy)%s",
+ ru->ru_minflt + ru->ru_majflt,
+ (int)((long double)ru->ru_minflt /
+ (ru->ru_minflt + ru->ru_majflt) * 100),
+ nl);
+ }
+ if (ru->ru_nvcsw + ru->ru_nivcsw > 1) {
+ (appendf)(
+ b, "%,ld context switches (%d%% consensual)%s",
+ ru->ru_nvcsw + ru->ru_nivcsw,
+ (int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100),
+ nl);
+ }
+ if (ru->ru_msgrcv || ru->ru_msgsnd) {
+ (appendf)(b, "received %,ld message%s and sent %,ld%s", ru->ru_msgrcv,
+ ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl);
+ }
+ if (ru->ru_inblock || ru->ru_oublock) {
+ (appendf)(b, "performed %,ld read%s and %,ld write i/o operations%s",
+ ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock,
+ nl);
+ }
+ if (ru->ru_nsignals) {
+ (appendf)(b, "received %,ld signals%s", ru->ru_nsignals, nl);
+ }
+ if (ru->ru_nswap) {
+ (appendf)(b, "got swapped %,ld times%s", ru->ru_nswap, nl);
+ }
+}
diff --git a/libc/log/cancolor.c b/libc/log/cancolor.c
index 6d920897a..51ebd0ef2 100644
--- a/libc/log/cancolor.c
+++ b/libc/log/cancolor.c
@@ -47,7 +47,8 @@ bool cancolor(void) {
static bool once;
static bool result;
if (!once) {
- result = !!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1");
+ result = !!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1") &&
+ !!strcmp(nulltoempty(getenv("TERM")), "dumb");
once = true;
}
return result;
diff --git a/libc/log/checkaligned.c b/libc/log/checkaligned.c
index fbd5e355d..66fea2f58 100644
--- a/libc/log/checkaligned.c
+++ b/libc/log/checkaligned.c
@@ -25,7 +25,7 @@
void __check_fail_aligned(unsigned bytes, uint64_t ptr) {
fflush(stderr);
if (!IsTiny()) memsummary(fileno(stderr));
- (dprintf)(fileno(stderr), "%s%d%s%#p\r\n", "error: pointer not ", bytes,
+ (dprintf)(fileno(stderr), "%s%d%s%#p\n", "error: pointer not ", bytes,
"-byte aligned: ", ptr);
__die();
}
diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c
index 23aad9a9f..619eb607f 100644
--- a/libc/log/checkfail.c
+++ b/libc/log/checkfail.c
@@ -53,10 +53,10 @@ relegated void __check_fail(const char *suffix, const char *opstr,
gethostname(hostname, sizeof(hostname));
(dprintf)(STDERR_FILENO,
- "check failed on %s pid %d\r\n"
- "\tCHECK_%s(%s, %s);\r\n"
- "\t\t → %#lx (%s)\r\n"
- "\t\t%s %#lx (%s)\r\n",
+ "check failed on %s pid %d\n"
+ "\tCHECK_%s(%s, %s);\n"
+ "\t\t → %#lx (%s)\n"
+ "\t\t%s %#lx (%s)\n",
hostname, getpid(), sufbuf, wantstr, gotstr, want, wantstr, opstr,
got, gotstr);
@@ -65,19 +65,19 @@ relegated void __check_fail(const char *suffix, const char *opstr,
va_start(va, fmt);
(vdprintf)(STDERR_FILENO, fmt, va);
va_end(va);
- (dprintf)(STDERR_FILENO, "\r\n");
+ (dprintf)(STDERR_FILENO, "\n");
}
- (dprintf)(STDERR_FILENO, "\t%s\r\n\t%s%s%s%s\r\n", strerror(lasterr), SUBTLE,
+ (dprintf)(STDERR_FILENO, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
getauxval(AT_EXECFN), __argc > 1 ? " \\" : "", RESET);
for (i = 1; i < __argc; ++i) {
- (dprintf)(STDERR_FILENO, "\t\t%s%s\r\n", __argv[i],
+ (dprintf)(STDERR_FILENO, "\t\t%s%s\n", __argv[i],
i < __argc - 1 ? " \\" : "");
}
if (!IsTiny() && lasterr == ENOMEM) {
- (dprintf)(STDERR_FILENO, "\r\n");
+ (dprintf)(STDERR_FILENO, "\n");
PrintMemoryIntervals(STDERR_FILENO, &_mmi);
}
diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c
index b232a73e0..a3a1a18c6 100644
--- a/libc/log/checkfail_ndebug.c
+++ b/libc/log/checkfail_ndebug.c
@@ -46,5 +46,5 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got,
__print(bx, uint64toarray_radix16(got, bx));
__print_string(" (");
__print(bx, int64toarray_radix10(lasterr, bx));
- __print_string(")\r\n");
+ __print_string(")\n");
}
diff --git a/libc/log/getsicodename.c b/libc/log/getsicodename.c
new file mode 100644
index 000000000..af4c6a58b
--- /dev/null
+++ b/libc/log/getsicodename.c
@@ -0,0 +1,154 @@
+/*-*- 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/log/log.h"
+#include "libc/str/str.h"
+#include "libc/sysv/consts/sicode.h"
+#include "libc/sysv/consts/sig.h"
+
+static bool IsSiUser(int si_code) {
+ if (!IsOpenbsd()) {
+ return si_code == SI_USER;
+ } else {
+ return si_code <= 0;
+ }
+}
+
+/**
+ * Returns symbolic name for siginfo::si_code value.
+ */
+const char *GetSiCodeName(int sig, int si_code) {
+ static char b[16];
+ memset(b, 0, sizeof(b));
+ strcpy(b, "SI_???");
+ if (si_code == SI_QUEUE) {
+ strcpy(b + 3, "QUEUE"); /* sent by sigqueue(2) */
+ } else if (si_code == SI_TIMER) {
+ strcpy(b + 3, "TIMER"); /* sent by setitimer(2) or clock_settime(2) */
+ } else if (si_code == SI_TKILL) {
+ strcpy(b + 3, "TKILL"); /* sent by tkill(2), etc. */
+ } else if (si_code == SI_MESGQ) {
+ strcpy(b + 3, "MESGQ"); /* sent by mq_notify(2) */
+ } else if (si_code == SI_MESGQ) {
+ strcpy(b + 3, "MESGQ"); /* aio completion */
+ } else if (si_code == SI_ASYNCNL) {
+ strcpy(b + 3, "ASYNCNL"); /* aio completion for dns; the horror */
+ } else if (si_code == SI_NOINFO) {
+ strcpy(b + 3, "NOINFO"); /* no signal specific info available */
+ } else if (si_code == SI_KERNEL) {
+ strcpy(b + 3, "KERNEL"); /* sent by kernel */
+ } else if (IsSiUser(si_code)) {
+ strcpy(b + 3, "USER"); /* sent by kill(2) i.e. from userspace */
+ } else if (sig == SIGCHLD) {
+ strcpy(b, "CLD_???");
+ if (si_code == CLD_EXITED) {
+ strcpy(b + 4, "EXITED"); /* child exited */
+ } else if (si_code == CLD_KILLED) {
+ strcpy(b + 4, "KILLED"); /* child terminated w/o core */
+ } else if (si_code == CLD_DUMPED) {
+ strcpy(b + 4, "DUMPED"); /* child terminated w/ core */
+ } else if (si_code == CLD_TRAPPED) {
+ strcpy(b + 4, "TRAPPED"); /* traced child trapped */
+ } else if (si_code == CLD_STOPPED) {
+ strcpy(b + 4, "STOPPED"); /* child stopped */
+ } else if (si_code == CLD_CONTINUED) {
+ strcpy(b + 4, "CONTINUED"); /* stopped child continued */
+ }
+ } else if (sig == SIGTRAP) {
+ strcpy(b, "TRAP_???");
+ if (si_code == TRAP_BRKPT) {
+ strcpy(b + 5, "BRKPT"); /* process breakpoint */
+ } else if (si_code == TRAP_TRACE) {
+ strcpy(b + 5, "TRACE"); /* process trace trap */
+ }
+ } else if (sig == SIGSEGV) {
+ strcpy(b, "SEGV_???");
+ if (si_code == SEGV_MAPERR) {
+ strcpy(b + 5, "MAPERR"); /* address not mapped to object */
+ } else if (si_code == SEGV_ACCERR) {
+ strcpy(b + 5, "ACCERR"); /* invalid permissions for mapped object */
+ }
+ } else if (sig == SIGFPE) {
+ strcpy(b, "FPE_???");
+ if (si_code == FPE_INTDIV) {
+ strcpy(b + 4, "INTDIV"); /* integer divide by zero */
+ } else if (si_code == FPE_INTOVF) {
+ strcpy(b + 4, "INTOVF"); /* integer overflow */
+ } else if (si_code == FPE_FLTDIV) {
+ strcpy(b + 4, "FLTDIV"); /* floating point divide by zero */
+ } else if (si_code == FPE_FLTOVF) {
+ strcpy(b + 4, "FLTOVF"); /* floating point overflow */
+ } else if (si_code == FPE_FLTUND) {
+ strcpy(b + 4, "FLTUND"); /* floating point underflow */
+ } else if (si_code == FPE_FLTRES) {
+ strcpy(b + 4, "FLTRES"); /* floating point inexact */
+ } else if (si_code == FPE_FLTINV) {
+ strcpy(b + 4, "FLTINV"); /* invalid floating point operation */
+ } else if (si_code == FPE_FLTSUB) {
+ strcpy(b + 4, "FLTSUB"); /* subscript out of range */
+ }
+ } else if (sig == SIGILL) {
+ strcpy(b, "ILL_???");
+ if (si_code == ILL_ILLOPC) {
+ strcpy(b + 4, "ILLOPC"); /* illegal opcode */
+ } else if (si_code == ILL_ILLOPN) {
+ strcpy(b + 4, "ILLOPN"); /* illegal operand */
+ } else if (si_code == ILL_ILLADR) {
+ strcpy(b + 4, "ILLADR"); /* illegal addressing mode */
+ } else if (si_code == ILL_ILLTRP) {
+ strcpy(b + 4, "ILLTRP"); /* illegal trap */
+ } else if (si_code == ILL_PRVOPC) {
+ strcpy(b + 4, "PRVOPC"); /* privileged opcode */
+ } else if (si_code == ILL_PRVREG) {
+ strcpy(b + 4, "PRVREG"); /* privileged register */
+ } else if (si_code == ILL_COPROC) {
+ strcpy(b + 4, "COPROC"); /* coprocessor error */
+ } else if (si_code == ILL_BADSTK) {
+ strcpy(b + 4, "BADSTK"); /* internal stack error */
+ }
+ } else if (sig == SIGBUS) {
+ strcpy(b, "BUS_???");
+ if (si_code == BUS_ADRALN) {
+ strcpy(b + 4, "ADRALN"); /* invalid address alignment */
+ } else if (si_code == BUS_ADRERR) {
+ strcpy(b + 4, "ADRERR"); /* non-existent physical address */
+ } else if (si_code == BUS_OBJERR) {
+ strcpy(b + 4, "OBJERR"); /* object specific hardware error */
+ } else if (si_code == BUS_MCEERR_AR) {
+ strcpy(b + 4, "BUS_AR"); /* Linux 2.6.32+ */
+ } else if (si_code == BUS_MCEERR_AO) {
+ strcpy(b + 4, "BUS_AO"); /* Linux 2.6.32+ */
+ }
+ } else if (sig == SIGIO) {
+ strcpy(b, "POLL_???");
+ if (si_code == POLL_IN) {
+ strcpy(b + 5, "IN"); /* data input available */
+ } else if (si_code == POLL_OUT) {
+ strcpy(b + 5, "OUT"); /* output buffer available */
+ } else if (si_code == POLL_MSG) {
+ strcpy(b + 5, "MSG"); /* input message available */
+ } else if (si_code == POLL_ERR) {
+ strcpy(b + 5, "ERR"); /* i/o error */
+ } else if (si_code == POLL_PRI) {
+ strcpy(b + 5, "PRI"); /* high priority input available */
+ } else if (si_code == POLL_HUP) {
+ strcpy(b + 5, "HUP"); /* device disconnected */
+ }
+ }
+ return b;
+}
diff --git a/libc/log/internal.h b/libc/log/internal.h
index 49adbc2f3..eba7db65e 100644
--- a/libc/log/internal.h
+++ b/libc/log/internal.h
@@ -6,8 +6,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
-extern int kCrashSigs[8];
-extern struct sigaction g_oldcrashacts[8];
+extern int kCrashSigs[8] hidden;
+extern struct sigaction g_oldcrashacts[8] hidden;
void __start_fatal(const char *, int) hidden;
void __start_fatal_ndebug(void) hidden;
diff --git a/libc/log/log.h b/libc/log/log.h
index a28abc83a..ff1180c3e 100644
--- a/libc/log/log.h
+++ b/libc/log/log.h
@@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_LOG_LOG_H_
#define COSMOPOLITAN_LIBC_LOG_LOG_H_
#include "libc/bits/likely.h"
+#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/winsize.h"
#include "libc/nexgen32e/stackframe.h"
@@ -52,6 +53,8 @@ void showcrashreports(void);
void callexitontermination(struct sigset *);
bool32 IsDebuggerPresent(bool);
bool IsRunningUnderMake(void);
+const char *GetSiCodeName(int, int);
+void AppendResourceReport(char **, struct rusage *, const char *);
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § liblog » logging ─╬─│┼
diff --git a/libc/log/malloc_stats.c b/libc/log/malloc_stats.c
index 9369fd1ac..f57f049df 100644
--- a/libc/log/malloc_stats.c
+++ b/libc/log/malloc_stats.c
@@ -22,7 +22,7 @@
void malloc_stats(void) {
struct MallocStats res = dlmalloc_stats(g_dlmalloc);
- (fprintf)(stderr, "max system bytes = %'10zu\r\n", res.maxfp);
- (fprintf)(stderr, "system bytes = %'10zu\r\n", res.fp);
- (fprintf)(stderr, "in use bytes = %'10zu\r\n", res.used);
+ (fprintf)(stderr, "max system bytes = %'10zu\n", res.maxfp);
+ (fprintf)(stderr, "system bytes = %'10zu\n", res.fp);
+ (fprintf)(stderr, "in use bytes = %'10zu\n", res.used);
}
diff --git a/libc/log/meminfo.c b/libc/log/meminfo.c
index 95f2bf823..5fbe6bee9 100644
--- a/libc/log/meminfo.c
+++ b/libc/log/meminfo.c
@@ -22,7 +22,7 @@
#include "libc/mem/mem.h"
static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) {
- (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\r\n", start, end, used_bytes,
+ (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\n", start, end, used_bytes,
(intptr_t)end - (intptr_t)start);
}
@@ -31,7 +31,7 @@ static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) {
*/
void meminfo(int fd) {
memsummary(fd);
- (dprintf)(fd, "%*s %*s %*s %*s\r\n", POINTER_XDIGITS, "start",
+ (dprintf)(fd, "%*s %*s %*s %*s\n", POINTER_XDIGITS, "start",
POINTER_XDIGITS, "end", 8, "used", 8, "size");
malloc_inspect_all(onmemchunk, &fd);
}
diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c
index cdb3446b1..75f8c3798 100644
--- a/libc/log/oncrash.c
+++ b/libc/log/oncrash.c
@@ -18,6 +18,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
+#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/utsname.h"
#include "libc/calls/ucontext.h"
@@ -57,11 +58,21 @@ static const char kGregNames[17][4] forcealign(1) = {
static const char kCpuFlags[12] forcealign(1) = "CVPRAKZSTIDO";
static const char kFpuExceptions[6] forcealign(1) = "IDZOUP";
-static const char kCrashSigNames[8][5] forcealign(1) = {
- "QUIT", "FPE", "ILL", "SEGV", "TRAP", "ABRT", "BUS", "PIPE"};
-hidden int kCrashSigs[8];
-hidden struct sigaction g_oldcrashacts[8];
+/* : showcrashreports.c, oncrashthunks.S, oncrash.c */
+int kCrashSigs[8];
+struct sigaction g_oldcrashacts[8];
+static const char kCrashSigNames[8][5] forcealign(1) = {
+ "QUIT", //
+ "FPE", //
+ "ILL", //
+ "SEGV", //
+ "TRAP", //
+ "ABRT", //
+ "BUS", //
+ "PIPE", //
+};
+/* : showcrashreports.c, oncrashthunks.S, oncrash.c */
relegated static const char *TinyStrSignal(int sig) {
size_t i;
@@ -76,7 +87,7 @@ relegated static const char *TinyStrSignal(int sig) {
relegated static void ShowFunctionCalls(int fd, ucontext_t *ctx) {
struct StackFrame *bp;
struct StackFrame goodframe;
- write(fd, "\r\n", 2);
+ write(fd, "\n", 1);
if (ctx && ctx->uc_mcontext.rip && ctx->uc_mcontext.rbp) {
goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp;
goodframe.addr = ctx->uc_mcontext.rip;
@@ -121,7 +132,7 @@ relegated static void DescribeCpuFlags(int fd, int flags, int x87sw,
relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) {
size_t i, j, k;
long double st;
- write(fd, "\r\n", 2);
+ write(fd, "\n", 1);
for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) {
if (j > 0) write(fd, " ", 1);
dprintf(fd, "%-3s %016lx", kGregNames[(unsigned)kGregOrder[i]],
@@ -135,7 +146,7 @@ relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) {
}
dprintf(fd, " %s(%zu) %Lg", "ST", k, st);
++k;
- write(fd, "\r\n", 2);
+ write(fd, "\n", 1);
}
}
DescribeCpuFlags(
@@ -147,9 +158,9 @@ relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) {
relegated static void ShowSseRegisters(int fd, ucontext_t *ctx) {
size_t i;
if (ctx->uc_mcontext.fpregs) {
- write(fd, "\r\n\r\n", 4);
+ write(fd, "\n\n", 2);
for (i = 0; i < 8; ++i) {
- dprintf(fd, "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\r\n", "XMM", i + 0,
+ dprintf(fd, "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\n", "XMM", i + 0,
ctx->uc_mcontext.fpregs->xmm[i + 0].u64[1],
ctx->uc_mcontext.fpregs->xmm[i + 0].u64[0], "XMM", i + 8,
ctx->uc_mcontext.fpregs->xmm[i + 8].u64[1],
@@ -174,32 +185,37 @@ relegated static void ShowMemoryMappings(int outfd) {
}
relegated static void ShowCrashReport(int err, int fd, int sig,
- ucontext_t *ctx) {
+ struct siginfo *si, ucontext_t *ctx) {
int i;
char hostname[64];
struct utsname names;
strcpy(hostname, "unknown");
gethostname(hostname, sizeof(hostname));
- dprintf(fd, "\r\n%serror%s: Uncaught SIG%s on %s\r\n %s\r\n %s\r\n", RED2,
- RESET, TinyStrSignal(sig), hostname, getauxval(AT_EXECFN),
- strerror(err));
+ dprintf(fd,
+ "\n"
+ "%serror%s: Uncaught SIG%s (%s) on %s\n"
+ " %s\n"
+ " %s\n",
+ RED2, RESET, TinyStrSignal(sig),
+ si ? GetSiCodeName(sig, si->si_code) : "???", hostname,
+ getauxval(AT_EXECFN), strerror(err));
if (uname(&names) != -1) {
- dprintf(fd, " %s %s %s %s\r\n", names.sysname, names.nodename,
- names.release, names.version);
+ dprintf(fd, " %s %s %s %s\n", names.sysname, names.nodename, names.release,
+ names.version);
}
ShowFunctionCalls(fd, ctx);
if (ctx) {
ShowGeneralRegisters(fd, ctx);
ShowSseRegisters(fd, ctx);
}
- write(fd, "\r\n", 2);
+ write(fd, "\n", 1);
ShowMemoryMappings(fd);
- write(fd, "\r\n", 2);
+ write(fd, "\n", 1);
for (i = 0; i < __argc; ++i) {
write(fd, __argv[i], strlen(__argv[i]));
write(fd, " ", 1);
}
- write(fd, "\r\n", 2);
+ write(fd, "\n", 1);
}
relegated static void RestoreDefaultCrashSignalHandlers(void) {
@@ -246,7 +262,7 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
: 0);
}
if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return;
- ShowCrashReport(err, STDERR_FILENO, sig, ctx);
+ ShowCrashReport(err, STDERR_FILENO, sig, si, ctx);
exit(128 + sig);
unreachable;
}
diff --git a/libc/log/oncrashthunks.S b/libc/log/oncrashthunks.S
index 5d295cab0..ef70cb9e8 100644
--- a/libc/log/oncrashthunks.S
+++ b/libc/log/oncrashthunks.S
@@ -25,6 +25,8 @@
__oncrash_thunks:
+// : showcrashreports.c, oncrashthunks.S, oncrash.c
+
.org 11*0
__oncrash_sigquit:
push %rbp
@@ -97,4 +99,6 @@ __oncrash_sigpipe:
ret
.endfn __oncrash_sigpipe,globl
+// : showcrashreports.c, oncrashthunks.S, oncrash.c
+
.endobj __oncrash_thunks,globl
diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c
index ae6a94ab5..d9eae94bb 100644
--- a/libc/log/showcrashreports.c
+++ b/libc/log/showcrashreports.c
@@ -34,7 +34,7 @@
STATIC_YOINK("__die");
-extern const unsigned char __oncrash_thunks[7][11];
+extern const unsigned char __oncrash_thunks[8][11];
/**
* Installs crash signal handlers.
@@ -56,7 +56,7 @@ extern const unsigned char __oncrash_thunks[7][11];
void showcrashreports(void) {
size_t i;
struct sigaction sa;
- /* : oncrashthunks.S */
+ /* : showcrashreports.c, oncrashthunks.S, oncrash.c */
kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */
kCrashSigs[1] = SIGFPE; /* 1 / 0 */
kCrashSigs[2] = SIGILL; /* illegal instruction */
@@ -65,9 +65,9 @@ void showcrashreports(void) {
kCrashSigs[5] = SIGABRT; /* abort() called */
kCrashSigs[6] = SIGBUS; /* misaligned, noncanonical ptr, etc. */
kCrashSigs[7] = SIGPIPE; /* write to closed thing */
- /* : oncrashthunks.S */
+ /* : showcrashreports.c, oncrashthunks.S, oncrash.c */
memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_RESETHAND;
+ sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
sigfillset(&sa.sa_mask);
for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) {
sigdelset(&sa.sa_mask, kCrashSigs[i]);
diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh
index c23df5455..e2af91c5e 100755
--- a/libc/sysv/consts.sh
+++ b/libc/sysv/consts.sh
@@ -534,10 +534,10 @@ syscon sicode CLD_DUMPED 3 3 3 3 3 3 # SIGCHLD; child terminated
syscon sicode CLD_TRAPPED 4 4 4 4 4 4 # SIGCHLD; traced child trapped; unix consensus
syscon sicode CLD_STOPPED 5 5 5 5 5 5 # SIGCHLD; child stopped; unix consensus
syscon sicode CLD_CONTINUED 6 6 6 6 6 6 # SIGCHLD; stopped child continued; unix consensus
-syscon sicode TRAP_BRKPT 1 1 1 1 1 1 # SIGTRAP; unix consensus
-syscon sicode TRAP_TRACE 2 2 2 2 2 2 # SIGTRAP; unix consensus
-syscon sicode SEGV_MAPERR 1 1 1 1 1 1 # SIGSEGV; unix consensus
-syscon sicode SEGV_ACCERR 2 2 2 2 2 2 # SIGSEGV; unix consensus
+syscon sicode TRAP_BRKPT 1 1 1 1 1 1 # SIGTRAP; process breakpoint; unix consensus
+syscon sicode TRAP_TRACE 2 2 2 2 2 2 # SIGTRAP; process trace trap; unix consensus
+syscon sicode SEGV_MAPERR 1 1 1 1 1 1 # SIGSEGV; address not mapped to object; unix consensus
+syscon sicode SEGV_ACCERR 2 2 2 2 2 2 # SIGSEGV; invalid permissions for mapped object; unix consensus
syscon sicode FPE_INTDIV 1 7 2 1 1 1 # SIGFPE; integer divide by zero
syscon sicode FPE_INTOVF 2 8 1 2 2 2 # SIGFPE; integer overflow
syscon sicode FPE_FLTDIV 3 1 3 3 3 3 # SIGFPE; floating point divide by zero
diff --git a/libc/x/xsigaction.c b/libc/x/xsigaction.c
index 844844cd0..782a420ef 100644
--- a/libc/x/xsigaction.c
+++ b/libc/x/xsigaction.c
@@ -37,6 +37,7 @@
* @return 0 on success, or -1 w/ errno
* @see libc/sysv/consts.sh
* @asyncsignalsafe
+ * @vforksafe
*/
int xsigaction(int sig, void *handler, uint64_t flags, uint64_t mask,
struct sigaction *old) {
diff --git a/test/libc/calls/setrlimit_test.c b/test/libc/calls/setrlimit_test.c
new file mode 100644
index 000000000..656db5791
--- /dev/null
+++ b/test/libc/calls/setrlimit_test.c
@@ -0,0 +1,291 @@
+/*-*- 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 "dsp/core/core.h"
+#include "libc/bits/safemacros.internal.h"
+#include "libc/calls/calls.h"
+#include "libc/calls/sigbits.h"
+#include "libc/calls/struct/rlimit.h"
+#include "libc/calls/struct/sigaction.h"
+#include "libc/errno.h"
+#include "libc/fmt/fmt.h"
+#include "libc/log/check.h"
+#include "libc/math.h"
+#include "libc/mem/mem.h"
+#include "libc/rand/rand.h"
+#include "libc/runtime/directmap.internal.h"
+#include "libc/runtime/runtime.h"
+#include "libc/str/str.h"
+#include "libc/sysv/consts/map.h"
+#include "libc/sysv/consts/o.h"
+#include "libc/sysv/consts/prot.h"
+#include "libc/sysv/consts/rlimit.h"
+#include "libc/sysv/consts/sig.h"
+#include "libc/testlib/testlib.h"
+#include "libc/time/time.h"
+#include "libc/x/x.h"
+
+#define MEM (64 * 1024 * 1024)
+
+char tmpfile[PATH_MAX];
+
+void OnSigxcpu(int sig) {
+ ASSERT_EQ(SIGXCPU, sig);
+ _exit(0);
+}
+
+void OnSigxfsz(int sig) {
+ unlink(tmpfile);
+ ASSERT_EQ(SIGXFSZ, sig);
+ _exit(0);
+}
+
+int EzSpawn(void) {
+ int pid, wstatus;
+ sigset_t chldmask, savemask;
+ struct sigaction ignore, saveint, savequit;
+ ignore.sa_flags = 0;
+ ignore.sa_handler = SIG_IGN;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGINT, &ignore, &saveint);
+ sigaction(SIGQUIT, &ignore, &savequit);
+ sigemptyset(&chldmask);
+ sigaddset(&chldmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &chldmask, &savemask);
+ ASSERT_NE(-1, (pid = fork()));
+ if (!pid) {
+ sigaction(SIGINT, &saveint, 0);
+ sigaction(SIGQUIT, &savequit, 0);
+ sigprocmask(SIG_SETMASK, &savemask, 0);
+ return -2;
+ }
+ while (wait4(pid, &wstatus, 0, 0) == -1) {
+ ASSERT_EQ(EINTR, errno);
+ }
+ sigaction(SIGINT, &saveint, 0);
+ sigaction(SIGQUIT, &savequit, 0);
+ sigprocmask(SIG_SETMASK, &savemask, 0);
+ return wstatus;
+}
+
+int EzSpawnFast(void fun(void *), void *ctx) {
+ int pid, wstatus;
+ sigset_t chldmask, savemask;
+ struct sigaction saveint, savequit;
+ xsigaction(SIGINT, SIG_IGN, 0, 0, &saveint);
+ xsigaction(SIGQUIT, SIG_IGN, 0, 0, &savequit);
+ sigemptyset(&chldmask);
+ sigaddset(&chldmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &chldmask, &savemask);
+ ASSERT_NE(-1, (pid = vfork()));
+ if (!pid) {
+ xsigaction(SIGINT, SIG_DFL, 0, 0, 0);
+ xsigaction(SIGQUIT, SIG_DFL, 0, 0, 0);
+ sigprocmask(SIG_SETMASK, &savemask, 0);
+ fun(ctx);
+ _exit(127);
+ }
+ while (wait4(pid, &wstatus, 0, 0) == -1) {
+ ASSERT_EQ(EINTR, errno);
+ }
+ sigaction(SIGINT, &saveint, 0);
+ sigaction(SIGQUIT, &savequit, 0);
+ sigprocmask(SIG_SETMASK, &savemask, 0);
+ return wstatus;
+}
+
+TEST(setrlimit, testCpuLimit) {
+ char *p;
+ int i, wstatus;
+ long double start;
+ struct rlimit rlim;
+ double matrices[3][3][3];
+ if (IsWindows()) return; /* of course it doesn't work on windows */
+ ASSERT_NE(-1, (wstatus = EzSpawn()));
+ if (wstatus == -2) {
+ CHECK_EQ(0, xsigaction(SIGXCPU, OnSigxcpu, 0, 0, 0));
+ ASSERT_EQ(0, getrlimit(RLIMIT_CPU, &rlim));
+ rlim.rlim_cur = 1; /* set soft limit to one second */
+ ASSERT_EQ(0, setrlimit(RLIMIT_CPU, &rlim));
+ start = nowl();
+ do {
+ matmul3(matrices[0], matrices[1], matrices[2]);
+ matmul3(matrices[0], matrices[1], matrices[2]);
+ matmul3(matrices[0], matrices[1], matrices[2]);
+ matmul3(matrices[0], matrices[1], matrices[2]);
+ } while ((nowl() - start) < 5);
+ _exit(1);
+ }
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ EXPECT_FALSE(WIFSIGNALED(wstatus));
+ EXPECT_EQ(0, WEXITSTATUS(wstatus));
+ EXPECT_EQ(0, WTERMSIG(wstatus));
+}
+
+TEST(setrlimit, testFileSizeLimit) {
+ char *p;
+ char junkdata[512];
+ int i, fd, wstatus;
+ struct rlimit rlim;
+ if (IsWindows()) return; /* of course it doesn't work on windows */
+ ASSERT_NE(-1, (wstatus = EzSpawn()));
+ if (wstatus == -2) {
+ CHECK_EQ(0, xsigaction(SIGXFSZ, OnSigxfsz, 0, 0, 0));
+ ASSERT_EQ(0, getrlimit(RLIMIT_FSIZE, &rlim));
+ rlim.rlim_cur = 1024 * 1024; /* set soft limit to one megabyte */
+ ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rlim));
+ snprintf(tmpfile, sizeof(tmpfile), "%s/%s.%d",
+ firstnonnull(getenv("TMPDIR"), "/tmp"),
+ program_invocation_short_name, getpid());
+ ASSERT_NE(-1, (fd = open(tmpfile, O_RDWR | O_CREAT | O_TRUNC)));
+ rngset(junkdata, 512, rand64, -1);
+ for (i = 0; i < 5 * 1024 * 1024 / 512; ++i) {
+ ASSERT_EQ(512, write(fd, junkdata, 512));
+ }
+ close(fd);
+ unlink(tmpfile);
+ _exit(1);
+ }
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ EXPECT_FALSE(WIFSIGNALED(wstatus));
+ EXPECT_EQ(0, WEXITSTATUS(wstatus));
+ EXPECT_EQ(0, WTERMSIG(wstatus));
+}
+
+int SetKernelEnforcedMemoryLimit(size_t n) {
+ struct rlimit rlim = {n, n};
+ if (IsWindows() || IsXnu()) return -1;
+ return setrlimit(!IsOpenbsd() ? RLIMIT_AS : RLIMIT_DATA, &rlim);
+}
+
+TEST(setrlimit, testMemoryLimit) {
+ char *p;
+ int i, wstatus;
+ if (IsAsan()) return; /* b/c we use sys_mmap */
+ if (IsXnu()) return; /* doesn't work on darwin */
+ if (IsWindows()) return; /* of course it doesn't work on windows */
+ ASSERT_NE(-1, (wstatus = EzSpawn()));
+ if (wstatus == -2) {
+ ASSERT_EQ(0, SetKernelEnforcedMemoryLimit(MEM));
+ for (i = 0; i < (MEM * 2) / PAGESIZE; ++i) {
+ p = sys_mmap(0, PAGESIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
+ .addr;
+ if (p == MAP_FAILED) {
+ ASSERT_EQ(ENOMEM, errno);
+ _exit(0);
+ }
+ rngset(p, PAGESIZE, rand64, -1);
+ }
+ _exit(1);
+ }
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ EXPECT_FALSE(WIFSIGNALED(wstatus));
+ EXPECT_EQ(0, WEXITSTATUS(wstatus));
+ EXPECT_EQ(0, WTERMSIG(wstatus));
+}
+
+TEST(setrlimit, testVirtualMemoryLimit) {
+ char *p;
+ int i, wstatus;
+ if (IsAsan()) return;
+ if (IsXnu()) return; /* doesn't work on darwin */
+ if (IsOpenbsd()) return; /* unavailable on openbsd */
+ if (IsWindows()) return; /* of course it doesn't work on windows */
+ ASSERT_NE(-1, (wstatus = EzSpawn()));
+ if (wstatus == -2) {
+ ASSERT_EQ(0, setrlimit(RLIMIT_AS, &(struct rlimit){MEM, MEM}));
+ for (i = 0; i < (MEM * 2) / PAGESIZE; ++i) {
+ p = sys_mmap(0, PAGESIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
+ .addr;
+ if (p == MAP_FAILED) {
+ ASSERT_EQ(ENOMEM, errno);
+ _exit(0);
+ }
+ rngset(p, PAGESIZE, rand64, -1);
+ }
+ _exit(1);
+ }
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ EXPECT_FALSE(WIFSIGNALED(wstatus));
+ EXPECT_EQ(0, WEXITSTATUS(wstatus));
+ EXPECT_EQ(0, WTERMSIG(wstatus));
+}
+
+TEST(setrlimit, testDataMemoryLimit) {
+ char *p;
+ int i, wstatus;
+ if (IsAsan()) return;
+ if (IsXnu()) return; /* doesn't work on darwin */
+ if (IsNetbsd()) return; /* doesn't work on netbsd */
+ if (IsFreebsd()) return; /* doesn't work on freebsd */
+ if (IsLinux()) return; /* doesn't work on gnu/systemd */
+ if (IsWindows()) return; /* of course it doesn't work on windows */
+ ASSERT_NE(-1, (wstatus = EzSpawn()));
+ if (wstatus == -2) {
+ ASSERT_EQ(0, setrlimit(RLIMIT_DATA, &(struct rlimit){MEM, MEM}));
+ for (i = 0; i < (MEM * 2) / PAGESIZE; ++i) {
+ p = sys_mmap(0, PAGESIZE, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
+ .addr;
+ if (p == MAP_FAILED) {
+ ASSERT_EQ(ENOMEM, errno);
+ _exit(0);
+ }
+ rngset(p, PAGESIZE, rand64, -1);
+ }
+ _exit(1);
+ }
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ EXPECT_FALSE(WIFSIGNALED(wstatus));
+ EXPECT_EQ(0, WEXITSTATUS(wstatus));
+ EXPECT_EQ(0, WTERMSIG(wstatus));
+}
+
+TEST(setrlimit, testPhysicalMemoryLimit) {
+ /* RLIMIT_RSS doesn't work on gnu/systemd */
+ /* RLIMIT_RSS doesn't work on darwin */
+ /* RLIMIT_RSS doesn't work on freebsd */
+ /* RLIMIT_RSS doesn't work on netbsd */
+ /* RLIMIT_RSS doesn't work on openbsd */
+ /* of course it doesn't work on windows */
+}
+
+wontreturn void OnVfork(void *ctx) {
+ struct rlimit *rlim;
+ rlim = ctx;
+ rlim->rlim_cur -= 1;
+ ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim));
+ _exit(0);
+}
+
+TEST(setrlimit, isVforkSafe) {
+ int pid, ws;
+ struct rlimit rlim[2];
+ if (IsWindows()) return; /* of course it doesn't work on windows */
+ ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim));
+ ws = EzSpawnFast(OnVfork, rlim);
+ EXPECT_TRUE(WIFEXITED(ws));
+ EXPECT_FALSE(WIFSIGNALED(ws));
+ EXPECT_EQ(0, WEXITSTATUS(ws));
+ EXPECT_EQ(0, WTERMSIG(ws));
+ ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim + 1));
+ EXPECT_EQ(rlim[0].rlim_cur, rlim[1].rlim_cur);
+ EXPECT_EQ(rlim[0].rlim_max, rlim[1].rlim_max);
+}
diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk
index 0ac4572fe..ebda8edcf 100644
--- a/test/libc/calls/test.mk
+++ b/test/libc/calls/test.mk
@@ -24,7 +24,9 @@ TEST_LIBC_CALLS_CHECKS = \
$(TEST_LIBC_CALLS_SRCS_TEST:%.c=o/$(MODE)/%.com.runs)
TEST_LIBC_CALLS_DIRECTDEPS = \
+ DSP_CORE \
LIBC_CALLS \
+ LIBC_TINYMATH \
LIBC_SOCK \
LIBC_FMT \
LIBC_INTRIN \
diff --git a/test/libc/fmt/atoi_test.c b/test/libc/fmt/atoi_test.c
new file mode 100644
index 000000000..0092ae22a
--- /dev/null
+++ b/test/libc/fmt/atoi_test.c
@@ -0,0 +1,463 @@
+/*-*- 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/errno.h"
+#include "libc/fmt/conv.h"
+#include "libc/limits.h"
+#include "libc/stdio/stdio.h"
+#include "libc/testlib/ezbench.h"
+#include "libc/testlib/testlib.h"
+
+TEST(atoi, test) {
+ EXPECT_EQ(0, atoi(""));
+ EXPECT_EQ(0, atoi("0"));
+ EXPECT_EQ(1, atoi("1"));
+ EXPECT_EQ(9, atoi("9"));
+ EXPECT_EQ(12, atoi("12"));
+ EXPECT_EQ(-12, atoi("-12"));
+ EXPECT_EQ(31337, atoi("31337"));
+ EXPECT_EQ(+0, atoi("+0"));
+ EXPECT_EQ(+1, atoi("+1"));
+ EXPECT_EQ(+9, atoi("+9"));
+ EXPECT_EQ(-0, atoi("-0"));
+ EXPECT_EQ(-1, atoi("-1"));
+ EXPECT_EQ(-9, atoi("-9"));
+ EXPECT_EQ(-31337, atoi("-31337"));
+ EXPECT_EQ(INT_MIN, atoi("-2147483648"));
+ EXPECT_EQ(INT_MAX, atoi("2147483647"));
+ EXPECT_EQ(INT_MIN, atoi("-2147483649"));
+ EXPECT_EQ(INT_MAX, atoi("2147483648"));
+ EXPECT_EQ(INT_MIN, atoi("-2147483658"));
+ EXPECT_EQ(INT_MAX, atoi("2147483657"));
+ EXPECT_EQ(123, atoi(" 123"));
+ EXPECT_EQ(123, atoi(" \t123"));
+ EXPECT_EQ(+123, atoi(" +123"));
+ EXPECT_EQ(+123, atoi(" \t+123"));
+ EXPECT_EQ(-123, atoi(" -123"));
+ EXPECT_EQ(-123, atoi(" \t-123"));
+ EXPECT_EQ(0, atoi(" a123"));
+ EXPECT_EQ(0, atoi(" \ta123"));
+}
+
+TEST(atoi, testWithinLimit_doesntChangeErrno) {
+ errno = 7;
+ EXPECT_EQ(INT_MAX, atoi("2147483647"));
+ EXPECT_EQ(7, errno);
+ errno = 7;
+ EXPECT_EQ(INT_MIN, atoi("-2147483648"));
+ EXPECT_EQ(7, errno);
+}
+
+TEST(atoi, testOutsideLimit_saturatesAndSetsErangeErrno) {
+ errno = 0;
+ EXPECT_EQ(INT_MAX, atoi("2147483648"));
+ EXPECT_EQ(ERANGE, errno);
+ errno = 0;
+ EXPECT_EQ(INT_MIN, atoi("-2147483649"));
+ EXPECT_EQ(ERANGE, errno);
+}
+
+TEST(atol, test) {
+ EXPECT_EQ(0, atol(""));
+ EXPECT_EQ(0, atol("0"));
+ EXPECT_EQ(1, atol("1"));
+ EXPECT_EQ(9, atol("9"));
+ EXPECT_EQ(12, atol("12"));
+ EXPECT_EQ(-12, atol("-12"));
+ EXPECT_EQ(31337, atol("31337"));
+ EXPECT_EQ(+0, atol("+0"));
+ EXPECT_EQ(+1, atol("+1"));
+ EXPECT_EQ(+9, atol("+9"));
+ EXPECT_EQ(-0, atol("-0"));
+ EXPECT_EQ(-1, atol("-1"));
+ EXPECT_EQ(-9, atol("-9"));
+ EXPECT_EQ(-31337, atol("-31337"));
+ EXPECT_EQ(LONG_MIN, atol("-9223372036854775808"));
+ EXPECT_EQ(LONG_MAX, atol("9223372036854775807"));
+ EXPECT_EQ(LONG_MIN, atol("-9223372036854775809"));
+ EXPECT_EQ(LONG_MAX, atol("9223372036854775808"));
+ EXPECT_EQ(LONG_MIN, atol("-9223372036854775818"));
+ EXPECT_EQ(LONG_MAX, atol("9223372036854775817"));
+ EXPECT_EQ(123, atol(" 123"));
+ EXPECT_EQ(123, atol(" \t123"));
+ EXPECT_EQ(-123, atol(" -123"));
+ EXPECT_EQ(-123, atol(" \t-123"));
+ EXPECT_EQ(0, atol(" a123"));
+ EXPECT_EQ(0, atol(" \ta123"));
+}
+
+TEST(strtol, test) {
+ EXPECT_EQ(0, strtol("", 0, 10));
+ EXPECT_EQ(0, strtol("0", 0, 10));
+ EXPECT_EQ(1, strtol("1", 0, 10));
+ EXPECT_EQ(9, strtol("9", 0, 10));
+ EXPECT_EQ(12, strtol("12", 0, 10));
+ EXPECT_EQ(-12, strtol("-12", 0, 10));
+ EXPECT_EQ(31337, strtol("31337", 0, 10));
+ EXPECT_EQ(0, strtol("-0", 0, 10));
+ EXPECT_EQ(-1, strtol("-1", 0, 10));
+ EXPECT_EQ(-9, strtol("-9", 0, 10));
+ EXPECT_EQ(-31337, strtol("-31337", 0, 10));
+ EXPECT_EQ(LONG_MIN, strtol("-9223372036854775808", 0, 10));
+ EXPECT_EQ(LONG_MAX, strtol("9223372036854775807", 0, 10));
+ EXPECT_EQ(LONG_MIN, strtol("-9223372036854775809", 0, 10));
+ EXPECT_EQ(LONG_MAX, strtol("9223372036854775808", 0, 10));
+ EXPECT_EQ(LONG_MIN, strtol("-9223372036854775818", 0, 10));
+ EXPECT_EQ(LONG_MAX, strtol("9223372036854775817", 0, 10));
+ EXPECT_EQ(123, strtol(" 123", 0, 10));
+ EXPECT_EQ(123, strtol(" \t123", 0, 10));
+ EXPECT_EQ(-123, strtol(" -123", 0, 10));
+ EXPECT_EQ(-123, strtol(" \t-123", 0, 10));
+ EXPECT_EQ(0, strtol(" a123", 0, 10));
+ EXPECT_EQ(0, strtol(" \ta123", 0, 10));
+}
+
+TEST(strtol, testBinary) {
+ char *e;
+ ASSERT_EQ(85, strtol("0b1010101", 0, 2));
+ ASSERT_EQ(85, strtol("0b1010101", 0, 0));
+ ASSERT_EQ(85, strtol("0b1010101", &e, 0));
+ ASSERT_EQ(0, *e);
+}
+
+TEST(strtol, testOutsideLimit) {
+ errno = 0;
+ EXPECT_EQ(0x7fffffffffffffff, strtol("0x8000000000000000", NULL, 0));
+ EXPECT_EQ(ERANGE, errno);
+ errno = 0;
+ EXPECT_EQ(0x8000000000000000, strtol("-0x8000000000000001", NULL, 0));
+ EXPECT_EQ(ERANGE, errno);
+ errno = 0;
+ EXPECT_EQ(0x8000000000000001, strtol("-9223372036854775807", NULL, 0));
+ EXPECT_EQ(0, errno);
+ errno = 0;
+ EXPECT_EQ(0x8000000000000000, strtol("-9223372036854775808", NULL, 0));
+ EXPECT_EQ(0, errno);
+}
+
+TEST(strtoul, ipAddress) {
+ char *e;
+ const char *p = "1.2.3.4";
+ EXPECT_EQ(1, strtoul(p, &e, 0));
+ EXPECT_EQ(1, e - p);
+}
+
+TEST(strtoul, weirdComma) {
+ char *e;
+ const char *p = ",2";
+ EXPECT_EQ(0, strtoul(p, &e, 0));
+ EXPECT_EQ(0, e - p);
+}
+
+TEST(strtoul, outsideLimit_doesModulus) {
+ EXPECT_EQ(/* python -c 'print(((2**123-1)/123)%(2**64))' */
+ 9298358801382050408ull,
+ strtoul(/* python -c 'print((2**123-1)/123)' */
+ "86453853384384772221385825058884200", 0, 10));
+}
+
+TEST(strtol, testHex) {
+ EXPECT_EQ(0, strtol("", 0, 16));
+ EXPECT_EQ(0, strtol("0", 0, 16));
+ EXPECT_EQ(1, strtol("1", 0, 16));
+ EXPECT_EQ(9, strtol("9", 0, 16));
+ EXPECT_EQ(18, strtol("12", 0, 16));
+ EXPECT_EQ(-18, strtol("-12", 0, 16));
+ EXPECT_EQ(201527, strtol("31337", 0, 16));
+ EXPECT_EQ(15, strtol("f", 0, 16));
+ EXPECT_EQ(3735928559, strtol("deadbeef", 0, 16));
+ EXPECT_EQ(0, strtol("-0", 0, 16));
+ EXPECT_EQ(-1, strtol("-1", 0, 16));
+ EXPECT_EQ(-9, strtol("-9", 0, 16));
+ EXPECT_EQ(-201527, strtol("-31337", 0, 16));
+ EXPECT_EQ(-3735928559, strtol("-DEADBEEF", 0, 16));
+ EXPECT_EQ(LONG_MIN, strtol("-8000000000000000", 0, 16));
+ EXPECT_EQ(LONG_MAX, strtol("7fffffffffffffff", 0, 16));
+ EXPECT_EQ(LONG_MIN, strtol("-8000000000000001", 0, 16));
+ EXPECT_EQ(LONG_MAX, strtol("8000000000000000", 0, 16));
+ EXPECT_EQ(LONG_MIN, strtol("-0x8000000000000000", 0, 16));
+ EXPECT_EQ(LONG_MAX, strtol("0x7fffffffffffffff", 0, 16));
+ EXPECT_EQ(LONG_MIN, strtol("-0x800000000000000a", 0, 16));
+ EXPECT_EQ(LONG_MAX, strtol("0x8000000000000009", 0, 16));
+ EXPECT_EQ(291, strtol(" 123", 0, 16));
+ EXPECT_EQ(291, strtol(" \t123", 0, 16));
+ EXPECT_EQ(-291, strtol(" -123", 0, 16));
+ EXPECT_EQ(-291, strtol(" \t-123", 0, 16));
+ EXPECT_EQ(0, strtol(" z123", 0, 16));
+ EXPECT_EQ(0, strtol(" \tz123", 0, 16));
+}
+
+TEST(strtol, testOctal) {
+ EXPECT_EQ(0, strtol("", 0, 8));
+ EXPECT_EQ(0, strtol("0", 0, 8));
+ EXPECT_EQ(1, strtol("1", 0, 8));
+ EXPECT_EQ(7, strtol("7", 0, 8));
+ EXPECT_EQ(10, strtol("12", 0, 8));
+ EXPECT_EQ(-10, strtol("-12", 0, 8));
+ EXPECT_EQ(13023, strtol("31337", 0, 8));
+ EXPECT_EQ(0, strtol("-0", 0, 8));
+ EXPECT_EQ(-1, strtol("-1", 0, 8));
+ EXPECT_EQ(-7, strtol("-7", 0, 8));
+ EXPECT_EQ(-13023, strtol("-31337", 0, 8));
+ EXPECT_EQ(LONG_MIN, strtol("-1000000000000000000000", 0, 8));
+ EXPECT_EQ(LONG_MAX, strtol("777777777777777777777", 0, 8));
+ EXPECT_EQ(LONG_MIN, strtol("-01000000000000000000000", 0, 0));
+ EXPECT_EQ(LONG_MAX, strtol("0777777777777777777777", 0, 0));
+ EXPECT_EQ(1152921504606846975, strtol("077777777777777777777", 0, 0));
+ EXPECT_EQ(-144115188075855872, strtol("-010000000000000000000", 0, 0));
+}
+
+TEST(strtol, testBase36) {
+ EXPECT_EQ(35, strtol("z", 0, 36));
+ EXPECT_EQ(5089507, strtol("31337", 0, 36));
+ EXPECT_EQ(29234652, strtol("hello", 0, 36));
+ EXPECT_EQ(29234652, strtol("HELLO", 0, 36));
+}
+
+TEST(strtol, testEndPtr) {
+ char *e;
+ const char *p;
+ p = "1";
+ ASSERT_EQ(1, strtol(p, &e, 2));
+ ASSERT_EQ(1, e - p);
+ p = "";
+ ASSERT_EQ(0, strtol(p, &e, 2));
+ ASSERT_EQ(0, e - p);
+}
+
+TEST(wcstol, testEndPtr) {
+ wchar_t *e;
+ const wchar_t *p;
+ p = L"1";
+ ASSERT_EQ(1, wcstol(p, &e, 2));
+ ASSERT_EQ(1, e - p);
+ p = L"";
+ ASSERT_EQ(0, wcstol(p, &e, 2));
+ ASSERT_EQ(0, e - p);
+}
+
+TEST(strtoul, testEndPtr) {
+ char *e;
+ const char *p;
+ p = "1";
+ ASSERT_EQ(1, strtoul(p, &e, 2));
+ ASSERT_EQ(1, e - p);
+ p = "";
+ ASSERT_EQ(0, strtoul(p, &e, 2));
+ ASSERT_EQ(0, e - p);
+}
+
+TEST(wcstoul, testEndPtr) {
+ wchar_t *e;
+ const wchar_t *p;
+ p = L"1";
+ ASSERT_EQ(1, wcstoul(p, &e, 2));
+ ASSERT_EQ(1, e - p);
+ p = L"";
+ ASSERT_EQ(0, wcstoul(p, &e, 2));
+ ASSERT_EQ(0, e - p);
+}
+
+TEST(strtoimax, testZero) {
+ EXPECT_EQ(0, strtoimax("0", NULL, 0));
+}
+
+TEST(strtoimax, testDecimal) {
+ EXPECT_EQ(-123, strtoimax("-123", NULL, 0));
+}
+
+TEST(strtoimax, testHex) {
+ EXPECT_EQ(-255, strtoimax("-0xff", NULL, 0));
+ EXPECT_EQ(255, strtoimax("0xff", NULL, 16));
+}
+
+TEST(strtoimax, testOctal) {
+ EXPECT_EQ(-123, strtoimax("-0173", NULL, 0));
+ EXPECT_EQ(123, strtoimax("0173", NULL, 8));
+}
+
+TEST(strtoimax, testBinary) {
+ EXPECT_EQ(-42, strtoimax("-0b101010", NULL, 0));
+ EXPECT_EQ(42, strtoimax("0b101010", NULL, 2));
+ ASSERT_EQ(85, strtoimax("0b1010101", 0, 2));
+ ASSERT_EQ(85, strtoimax("0b1010101", 0, 0));
+}
+
+TEST(strtoimax, testEndPtr) {
+ char *e;
+ const char *p = "1";
+ ASSERT_EQ(1, strtoimax(p, &e, 2));
+ ASSERT_EQ(1, e - p);
+}
+
+TEST(strtoimax, testLimits) {
+ EXPECT_EQ(
+ ((uintmax_t)0xffffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff,
+ strtoimax("-1", NULL, 0));
+ EXPECT_EQ(
+ ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff,
+ strtoimax("0x7fffffffffffffffffffffffffffffff", NULL, 0));
+}
+
+TEST(strtoimax, testOutsideLimit) {
+ errno = 0;
+ EXPECT_EQ(
+ ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff,
+ strtoimax("0x80000000000000000000000000000000", NULL, 0));
+ EXPECT_EQ(ERANGE, errno);
+ errno = 0;
+ EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000,
+ strtoimax("-0x80000000000000000000000000000001", NULL, 0));
+ EXPECT_EQ(ERANGE, errno);
+}
+
+TEST(strtoul, neghex) {
+ errno = 0;
+ ASSERT_EQ(-16, (long)strtoul("0xfffffffffffffff0", NULL, 0));
+ EXPECT_EQ(0, errno);
+}
+
+TEST(strtoumax, testZero) {
+ EXPECT_EQ(UINTMAX_MIN, strtoumax("0", NULL, 0));
+}
+TEST(strtoumax, testDecimal) {
+ EXPECT_EQ(123, strtoumax("123", NULL, 0));
+}
+TEST(strtoumax, testHex) {
+ EXPECT_EQ(255, strtoumax("0xff", NULL, 0));
+ EXPECT_EQ(255, strtoumax("0xff", NULL, 16));
+}
+TEST(strtoumax, testOctal) {
+ EXPECT_EQ(123, strtoumax("0173", NULL, 0));
+ EXPECT_EQ(123, strtoumax("0173", NULL, 8));
+}
+TEST(strtoumax, testBinary) {
+ EXPECT_EQ(42, strtoumax("0b101010", NULL, 0));
+ EXPECT_EQ(42, strtoumax("0b101010", NULL, 2));
+}
+
+TEST(strtoumax, testMaximum) {
+ EXPECT_EQ(UINTMAX_MAX,
+ strtoumax("340282366920938463463374607431768211455", NULL, 0));
+ EXPECT_EQ(UINTMAX_MAX,
+ strtoumax("0xffffffffffffffffffffffffffffffff", NULL, 0));
+}
+
+TEST(strtoumax, testTwosBane) {
+ EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000,
+ strtoumax("0x80000000000000000000000000000000", NULL, 0));
+}
+
+TEST(wcstol, test) {
+ EXPECT_EQ(0, wcstol(L"", 0, 10));
+ EXPECT_EQ(0, wcstol(L"0", 0, 10));
+ EXPECT_EQ(1, wcstol(L"1", 0, 10));
+ EXPECT_EQ(9, wcstol(L"9", 0, 10));
+ EXPECT_EQ(12, wcstol(L"12", 0, 10));
+ EXPECT_EQ(-12, wcstol(L"-12", 0, 10));
+ EXPECT_EQ(31337, wcstol(L"31337", 0, 10));
+ EXPECT_EQ(0, wcstol(L"-0", 0, 10));
+ EXPECT_EQ(-1, wcstol(L"-1", 0, 10));
+ EXPECT_EQ(-9, wcstol(L"-9", 0, 10));
+ EXPECT_EQ(-31337, wcstol(L"-31337", 0, 10));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-9223372036854775808", 0, 10));
+ EXPECT_EQ(LONG_MAX, wcstol(L"9223372036854775807", 0, 10));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-9223372036854775809", 0, 10));
+ EXPECT_EQ(LONG_MAX, wcstol(L"9223372036854775808", 0, 10));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-9223372036854775818", 0, 10));
+ EXPECT_EQ(LONG_MAX, wcstol(L"9223372036854775817", 0, 10));
+ EXPECT_EQ(123, wcstol(L" 123", 0, 10));
+ EXPECT_EQ(123, wcstol(L" \t123", 0, 10));
+ EXPECT_EQ(-123, wcstol(L" -123", 0, 10));
+ EXPECT_EQ(-123, wcstol(L" \t-123", 0, 10));
+ EXPECT_EQ(0, wcstol(L" a123", 0, 10));
+ EXPECT_EQ(0, wcstol(L" \ta123", 0, 10));
+}
+
+TEST(wcstol, testOutsideLimit) {
+ errno = 0;
+ EXPECT_EQ(0x7fffffffffffffff, wcstol(L"0x8000000000000000", NULL, 0));
+ EXPECT_EQ(ERANGE, errno);
+ errno = 0;
+ EXPECT_EQ(0x8000000000000000, wcstol(L"-0x8000000000000001", NULL, 0));
+ EXPECT_EQ(ERANGE, errno);
+ errno = 0;
+ EXPECT_EQ(0x8000000000000001, wcstol(L"-9223372036854775807", NULL, 0));
+ EXPECT_EQ(0, errno);
+ errno = 0;
+ EXPECT_EQ(0x8000000000000000, wcstol(L"-9223372036854775808", NULL, 0));
+ EXPECT_EQ(0, errno);
+}
+
+TEST(wcstol, testHex) {
+ EXPECT_EQ(0, wcstol(L"", 0, 16));
+ EXPECT_EQ(0, wcstol(L"0", 0, 16));
+ EXPECT_EQ(1, wcstol(L"1", 0, 16));
+ EXPECT_EQ(9, wcstol(L"9", 0, 16));
+ EXPECT_EQ(18, wcstol(L"12", 0, 16));
+ EXPECT_EQ(-18, wcstol(L"-12", 0, 16));
+ EXPECT_EQ(201527, wcstol(L"31337", 0, 16));
+ EXPECT_EQ(15, wcstol(L"f", 0, 16));
+ EXPECT_EQ(3735928559, wcstol(L"deadbeef", 0, 16));
+ EXPECT_EQ(0, wcstol(L"-0", 0, 16));
+ EXPECT_EQ(-1, wcstol(L"-1", 0, 16));
+ EXPECT_EQ(-9, wcstol(L"-9", 0, 16));
+ EXPECT_EQ(-201527, wcstol(L"-31337", 0, 16));
+ EXPECT_EQ(-3735928559, wcstol(L"-DEADBEEF", 0, 16));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-8000000000000000", 0, 16));
+ EXPECT_EQ(LONG_MAX, wcstol(L"7fffffffffffffff", 0, 16));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-8000000000000001", 0, 16));
+ EXPECT_EQ(LONG_MAX, wcstol(L"8000000000000000", 0, 16));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-0x8000000000000000", 0, 16));
+ EXPECT_EQ(LONG_MAX, wcstol(L"0x7fffffffffffffff", 0, 16));
+ EXPECT_EQ(LONG_MIN, wcstol(L"-0x800000000000000a", 0, 16));
+ EXPECT_EQ(LONG_MAX, wcstol(L"0x8000000000000009", 0, 16));
+ EXPECT_EQ(291, wcstol(L" 123", 0, 16));
+ EXPECT_EQ(291, wcstol(L" \t123", 0, 16));
+ EXPECT_EQ(-291, wcstol(L" -123", 0, 16));
+ EXPECT_EQ(-291, wcstol(L" \t-123", 0, 16));
+ EXPECT_EQ(0, wcstol(L" z123", 0, 16));
+ EXPECT_EQ(0, wcstol(L" \tz123", 0, 16));
+}
+
+TEST(wcstol, testBase36) {
+ EXPECT_EQ(35, wcstol(L"z", 0, 36));
+ EXPECT_EQ(5089507, wcstol(L"31337", 0, 36));
+ EXPECT_EQ(29234652, wcstol(L"hello", 0, 36));
+ EXPECT_EQ(29234652, wcstol(L"HELLO", 0, 36));
+}
+
+BENCH(atoi, bench) {
+ EZBENCH2("atoi", donothing, EXPROPRIATE(atoi(VEIL("r", "123456789"))));
+ EZBENCH2("strtol", donothing,
+ EXPROPRIATE(strtol(VEIL("r", "123456789"), 0, 10)));
+ EZBENCH2("strtoul", donothing,
+ EXPROPRIATE(strtol(VEIL("r", "123456789"), 0, 10)));
+ EZBENCH2("wcstol", donothing,
+ EXPROPRIATE(wcstol(VEIL("r", L"123456789"), 0, 10)));
+ EZBENCH2("wcstoul", donothing,
+ EXPROPRIATE(wcstol(VEIL("r", L"123456789"), 0, 10)));
+ EZBENCH2("strtoimax", donothing,
+ EXPROPRIATE(strtoimax(VEIL("r", "123456789"), 0, 10)));
+ EZBENCH2("strtoumax", donothing,
+ EXPROPRIATE(strtoimax(VEIL("r", "123456789"), 0, 10)));
+ EZBENCH2("wcstoimax", donothing,
+ EXPROPRIATE(wcstoimax(VEIL("r", L"123456789"), 0, 10)));
+ EZBENCH2("wcstoumax", donothing,
+ EXPROPRIATE(wcstoimax(VEIL("r", L"123456789"), 0, 10)));
+}
diff --git a/test/libc/fmt/sizetol_test.c b/test/libc/fmt/sizetol_test.c
new file mode 100644
index 000000000..ec1030cb0
--- /dev/null
+++ b/test/libc/fmt/sizetol_test.c
@@ -0,0 +1,53 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 2021 Justine Alexandra Roberts Tunney │
+│ │
+│ Permission to use, copy, modify, and/or distribute this software for │
+│ any purpose with or without fee is hereby granted, provided that the │
+│ above copyright notice and this permission notice appear in all copies. │
+│ │
+│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
+│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
+│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
+│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
+│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
+│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
+│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
+│ PERFORMANCE OF THIS SOFTWARE. │
+╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/fmt/conv.h"
+#include "libc/testlib/testlib.h"
+
+TEST(sizetol, test) {
+ EXPECT_EQ(-1, sizetol("", 1000));
+ EXPECT_EQ(0, sizetol("0", 1000));
+ EXPECT_EQ(0, sizetol("0kb", 1000));
+ EXPECT_EQ(31337, sizetol("31337", 1000));
+ EXPECT_EQ(1000, sizetol("1kb", 1000));
+ EXPECT_EQ(2000, sizetol("2k", 1000));
+ EXPECT_EQ(1024, sizetol("1kb", 1024));
+ EXPECT_EQ(2048, sizetol("2k", 1024));
+ EXPECT_EQ(100000000, sizetol("100mb", 1000));
+ EXPECT_EQ(100000000000000, sizetol("100tb", 1000));
+ EXPECT_EQ(104857600, sizetol("100mb", 1024));
+ EXPECT_EQ(100000000, sizetol("100MB", 1000));
+ EXPECT_EQ(104857600, sizetol("100MB", 1024));
+ EXPECT_EQ(100000000, sizetol("100M", 1000));
+ EXPECT_EQ(104857600, sizetol("100M", 1024));
+ EXPECT_EQ(100000000, sizetol(" 100M", 1000));
+ EXPECT_EQ(104857600, sizetol("\t100M", 1024));
+ EXPECT_EQ(100, sizetol(" 100", 1000));
+ EXPECT_EQ(100, sizetol("\t100", 1024));
+ EXPECT_EQ(100, sizetol(" 100 ", 1000));
+ EXPECT_EQ(100, sizetol("\t100\t", 1024));
+}
+
+TEST(sizetol, testNegative_notAllowed) {
+ EXPECT_EQ(-1, sizetol("-23", 1000));
+}
+
+TEST(sizetol, testOverflow_isDetected) {
+ EXPECT_EQ(9000000000000000000, sizetol("9eb", 1000));
+ EXPECT_EQ(-1, sizetol("10eb", 1000));
+}
diff --git a/test/libc/fmt/strtoimax_test.c b/test/libc/fmt/strtoimax_test.c
deleted file mode 100644
index 53d9d2a8c..000000000
--- a/test/libc/fmt/strtoimax_test.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
-│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
-╞══════════════════════════════════════════════════════════════════════════════╡
-│ Copyright 2020 Justine Alexandra Roberts Tunney │
-│ │
-│ Permission to use, copy, modify, and/or distribute this software for │
-│ any purpose with or without fee is hereby granted, provided that the │
-│ above copyright notice and this permission notice appear in all copies. │
-│ │
-│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
-│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
-│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
-│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
-│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
-│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
-│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
-│ PERFORMANCE OF THIS SOFTWARE. │
-╚─────────────────────────────────────────────────────────────────────────────*/
-#include "libc/bits/bits.h"
-#include "libc/errno.h"
-#include "libc/fmt/conv.h"
-#include "libc/testlib/testlib.h"
-
-TEST(strtoimax, testZero) {
- EXPECT_EQ(0, strtoimax("0", NULL, 0));
-}
-
-TEST(strtoimax, testDecimal) {
- EXPECT_EQ(-123, strtoimax("-123", NULL, 0));
-}
-
-TEST(strtoimax, testHex) {
- EXPECT_EQ(-255, strtoimax("-0xff", NULL, 0));
- EXPECT_EQ(255, strtoimax("0xff", NULL, 16));
-}
-
-TEST(strtoimax, testOctal) {
- EXPECT_EQ(-123, strtoimax("-0173", NULL, 0));
- EXPECT_EQ(123, strtoimax("0173", NULL, 8));
-}
-
-TEST(strtoimax, testBinary) {
- EXPECT_EQ(-42, strtoimax("-0b101010", NULL, 0));
- EXPECT_EQ(42, strtoimax("0b101010", NULL, 2));
-}
-
-TEST(strtoimax, testLimits) {
- EXPECT_EQ(
- ((uintmax_t)0xffffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff,
- strtoimax("-1", NULL, 0));
- EXPECT_EQ(
- ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff,
- strtoimax("0x7fffffffffffffffffffffffffffffff", NULL, 0));
-}
-
-TEST(strtoimax, testOutsideLimit) {
- errno = 0;
- EXPECT_EQ(
- ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff,
- strtoimax("0x80000000000000000000000000000000", NULL, 0));
- EXPECT_EQ(ERANGE, errno);
- errno = 0;
- EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000,
- strtoimax("-0x80000000000000000000000000000001", NULL, 0));
- EXPECT_EQ(ERANGE, errno);
-}
-
-TEST(strtoul, neghex) {
- errno = 0;
- ASSERT_EQ(-16, (long)strtoul("0xfffffffffffffff0", NULL, 0));
- EXPECT_EQ(0, errno);
-}
-
-TEST(strtol, testOutsideLimit) {
- errno = 0;
- EXPECT_EQ(0x7fffffffffffffff, strtol("0x8000000000000000", NULL, 0));
- EXPECT_EQ(ERANGE, errno);
- errno = 0;
- EXPECT_EQ(0x8000000000000000, strtol("-0x8000000000000001", NULL, 0));
- EXPECT_EQ(ERANGE, errno);
- errno = 0;
- EXPECT_EQ(0x8000000000000001, strtol("-9223372036854775807", NULL, 0));
- EXPECT_EQ(0, errno);
- errno = 0;
- EXPECT_EQ(0x8000000000000000, strtol("-9223372036854775808", NULL, 0));
- EXPECT_EQ(0, errno);
-}
diff --git a/test/libc/nexgen32e/kbase36_test.c b/test/libc/nexgen32e/kbase36_test.c
new file mode 100644
index 000000000..2b74de29c
--- /dev/null
+++ b/test/libc/nexgen32e/kbase36_test.c
@@ -0,0 +1,37 @@
+/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
+│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
+╞══════════════════════════════════════════════════════════════════════════════╡
+│ Copyright 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/nexgen32e/nexgen32e.h"
+#include "libc/testlib/testlib.h"
+
+TEST(kBase36, test) {
+ EXPECT_EQ(0, kBase36[0]);
+ EXPECT_EQ(0, kBase36[255]);
+ EXPECT_EQ(0, kBase36['!']);
+ EXPECT_EQ(0 + 1, kBase36['0']);
+ EXPECT_EQ(1 + 1, kBase36['1']);
+ EXPECT_EQ(9 + 1, kBase36['9']);
+ EXPECT_EQ(10 + 1, kBase36['a']);
+ EXPECT_EQ(10 + 1, kBase36['A']);
+ EXPECT_EQ(35 + 1, kBase36['z']);
+ EXPECT_EQ(35 + 1, kBase36['Z']);
+ EXPECT_EQ(0, kBase36['a' - 1]);
+ EXPECT_EQ(0, kBase36['A' - 1]);
+ EXPECT_EQ(0, kBase36['z' + 1]);
+ EXPECT_EQ(0, kBase36['Z' + 1]);
+}
diff --git a/third_party/mbedtls/test/test_suite_asn1parse.c b/third_party/mbedtls/test/test_suite_asn1parse.c
index 0009f62e8..5aea897a9 100644
--- a/third_party/mbedtls/test/test_suite_asn1parse.c
+++ b/third_party/mbedtls/test/test_suite_asn1parse.c
@@ -15,6 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "libc/stdio/stdio.h"
#include "third_party/mbedtls/test/test.inc"
/*
* *** THIS FILE WAS MACHINE GENERATED ***
@@ -688,6 +689,7 @@ void test_get_sequence_of( const data_t *input, int tag,
mbedtls_test_set_step( step );
TEST_ASSERT( cur != NULL );
TEST_EQUAL( cur->buf.tag, tag );
+ printf("yo %`'s\n", rest);
n = strtoul( rest, (char **) &rest, 0 );
TEST_EQUAL( n, (size_t)( cur->buf.p - input->x ) );
++rest;
diff --git a/third_party/python/python.mk b/third_party/python/python.mk
index e6dbbed38..0bc36bbfc 100644
--- a/third_party/python/python.mk
+++ b/third_party/python/python.mk
@@ -445,11 +445,11 @@ $(THIRD_PARTY_PYTHON_A).pkg: \
$(foreach x,$(THIRD_PARTY_PYTHON_A_DIRECTDEPS),$($(x)_A).pkg)
$(THIRD_PARTY_PYTHON_A_OBJS): \
- OVERRIDE_CFLAGS += \
- -DNDEBUG \
- -DPy_BUILD_CORE \
- -DPLATFORM='"cosmo"' \
- -DMULTIARCH='"x86_64-cosmo"'
+ OVERRIDE_CFLAGS += \
+ -DNDEBUG \
+ -DPy_BUILD_CORE \
+ -DPLATFORM='"cosmo"' \
+ -DMULTIARCH='"x86_64-cosmo"'
o/$(MODE)/third_party/python/Programs/python.o \
o/$(MODE)/third_party/python/Python/marshal.o \
@@ -457,8 +457,8 @@ o/$(MODE)/third_party/python/Python/sysmodule.o \
o/$(MODE)/third_party/python/Modules/selectmodule.o \
o/$(MODE)/third_party/python/Modules/getpath.o \
o/$(MODE)/third_party/python/Objects/listobject.o: \
- OVERRIDE_CFLAGS += \
- -DSTACK_FRAME_UNLIMITED
+ OVERRIDE_CFLAGS += \
+ -DSTACK_FRAME_UNLIMITED
o/$(MODE)/third_party/python/Python/dynload_shlib.o: \
OVERRIDE_CFLAGS += \
@@ -468,6 +468,8 @@ o/$(MODE)/third_party/python/Python/sysmodule.o: \
OVERRIDE_CFLAGS += \
-DABIFLAGS='"m"'
+o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C16
+
$(THIRD_PARTY_PYTHON_A_OBJS): \
OVERRIDE_CFLAGS += \
-ffunction-sections \
diff --git a/tool/build/calculator.c b/tool/build/calculator.c
index 086c49976..adbefef2b 100644
--- a/tool/build/calculator.c
+++ b/tool/build/calculator.c
@@ -14,6 +14,7 @@
#include "libc/bits/morton.h"
#include "libc/bits/popcnt.h"
#include "libc/calls/calls.h"
+#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
@@ -439,18 +440,26 @@ bool CallFunction(const char *sym) {
return false;
}
+volatile const char *literal_;
bool ConsumeLiteral(const char *literal) {
+ bool r;
char *e;
struct Value x;
+ literal_ = literal;
+ errno = 0;
x.t = kInt;
- x.i = *literal == '-' ? strtoimax(literal, &e, 0) : strtoumax(literal, &e, 0);
- if (!e || *e) {
+ x.i = strtoimax(literal, &e, 0);
+ if (*e) {
x.t = kFloat;
x.f = strtod(literal, &e);
- if (!e || *e) return false;
+ r = !(!e || *e);
+ } else if (errno == ERANGE) {
+ r = false;
+ } else {
+ r = true;
}
Push(x);
- return true;
+ return r;
}
void ConsumeToken(void) {
@@ -465,7 +474,7 @@ void ConsumeToken(void) {
default:
if (CallFunction(token.p)) return;
if (ConsumeLiteral(token.p)) return;
- Warning("bad token: %s", token.p);
+ Warning("bad token: %`'s", token.p);
break;
case kUnderflow:
Warning("stack underflow");
diff --git a/tool/build/calculator.ctest b/tool/build/calculator.ctest
index a316238d2..99de88017 100755
--- a/tool/build/calculator.ctest
+++ b/tool/build/calculator.ctest
@@ -37,7 +37,7 @@ nan isnormal ! assert
# BIT ARITHMETIC
-1 ~ 0 = assert
-0xffffffffffffffffffffffffffffffff ~ 0 = assert
+0 0x7fffffffffffffffffffffffffffffff - -0x7fffffffffffffffffffffffffffffff = assert
0b1010101 popcnt 4 = assert
0b1010101 0b0110101 ^ 0b1100000 = assert
0b1010101 0b0110101 | 0b1110101 = assert
diff --git a/tool/build/compile.c b/tool/build/compile.c
index 2c1cae0d7..ba48ce5ee 100644
--- a/tool/build/compile.c
+++ b/tool/build/compile.c
@@ -16,22 +16,33 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/assert.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/copyfile.h"
#include "libc/calls/sigbits.h"
+#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/sigset.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
+#include "libc/macros.internal.h"
+#include "libc/math.h"
#include "libc/mem/mem.h"
+#include "libc/nexgen32e/kcpuids.h"
#include "libc/runtime/runtime.h"
+#include "libc/stdio/append.internal.h"
#include "libc/str/str.h"
+#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/sig.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
+long cpuquota = 8; /* secs */
+long fszquota = 100 * 1000 * 1000; /* bytes */
+long memquota = 256 * 1024 * 1024; /* bytes */
+
#define MANUAL \
"\
SYNOPSIS\n\
@@ -185,9 +196,9 @@ const char *const kGccOnlyFlags[] = {
char *DescribeCommand(void) {
if (iscc) {
if (isgcc) {
- return xasprintf("gcc %d", ccversion);
+ return xasprintf("%s %d", "gcc", ccversion);
} else if (isclang) {
- return xasprintf("clang %d", ccversion);
+ return xasprintf("%s %d", "clang", ccversion);
}
}
return basename(cmd);
@@ -273,35 +284,76 @@ void AddArg(char *s) {
command.n += n;
}
} else {
- command.p = realloc(command.p, command.n + 2);
- command.p[command.n++] = '\r';
+ command.p = realloc(command.p, command.n + 1);
command.p[command.n++] = '\n';
}
}
-int Launch(void) {
+int GetBaseCpuFreqMhz(void) {
+ return KCPUIDS(16H, EAX) & 0x7fff;
+}
+
+void SetCpuLimit(int secs) {
+ int mhz;
+ struct rlimit rlim;
+ if (secs < 0) return;
+ if (IsWindows()) return;
+ if (!(mhz = GetBaseCpuFreqMhz())) return;
+ if (getrlimit(RLIMIT_CPU, &rlim) == -1) return;
+ rlim.rlim_cur = ceil(3100. / mhz * secs);
+ setrlimit(RLIMIT_CPU, &rlim);
+}
+
+void SetFszLimit(long n) {
+ struct rlimit rlim;
+ if (n < 0) return;
+ if (IsWindows()) return;
+ if (getrlimit(RLIMIT_FSIZE, &rlim) == -1) return;
+ rlim.rlim_cur = n;
+ setrlimit(RLIMIT_FSIZE, &rlim);
+}
+
+void SetMemLimit(long n) {
+ struct rlimit rlim = {n, n};
+ if (n < 0) return;
+ if (IsWindows() || IsXnu()) return;
+ setrlimit(!IsOpenbsd() ? RLIMIT_AS : RLIMIT_DATA, &rlim);
+}
+
+int Launch(struct rusage *ru) {
int ws, pid;
if ((pid = vfork()) == -1) exit(errno);
if (!pid) {
+ SetCpuLimit(cpuquota);
+ SetFszLimit(fszquota);
+ SetMemLimit(memquota);
sigprocmask(SIG_SETMASK, &savemask, NULL);
execve(cmd, args.p, env.p);
_exit(127);
}
- while (waitpid(pid, &ws, 0) == -1) {
+ while (wait4(pid, &ws, 0, ru) == -1) {
if (errno != EINTR) exit(errno);
}
return ws;
}
+char *GetResourceReport(struct rusage *ru) {
+ char *report = 0;
+ appendf(&report, "\n");
+ AppendResourceReport(&report, ru, "\n");
+ return report;
+}
+
int main(int argc, char *argv[]) {
size_t n;
char *p, **envp;
+ struct rusage ru;
int i, ws, rc, opt;
/*
* parse prefix arguments
*/
- while ((opt = getopt(argc, argv, "?hntA:T:V:")) != -1) {
+ while ((opt = getopt(argc, argv, "?hntC:M:F:A:T:V:")) != -1) {
switch (opt) {
case 'n':
exit(0);
@@ -317,6 +369,15 @@ int main(int argc, char *argv[]) {
case 'V':
ccversion = atoi(optarg);
break;
+ case 'C':
+ cpuquota = atoi(optarg);
+ break;
+ case 'M':
+ memquota = sizetol(optarg, 1024);
+ break;
+ case 'F':
+ fszquota = sizetol(optarg, 1000);
+ break;
case '?':
case 'h':
write(1, MANUAL, sizeof(MANUAL) - 1);
@@ -432,7 +493,7 @@ int main(int argc, char *argv[]) {
if (isclang) AddArg(argv[i]);
} else if (isclang && startswith(argv[i], "--debug-prefix-map")) {
/* llvm doesn't provide a gas interface so simulate w/ clang */
- AddArg(xasprintf("-f%s", argv[i] + 2));
+ AddArg(xasprintf("%s%s", "-f", argv[i] + 2));
} else if (isgcc && (!strcmp(argv[i], "-Os") || !strcmp(argv[i], "-O2") ||
!strcmp(argv[i], "-O3"))) {
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97623 */
@@ -526,8 +587,8 @@ int main(int argc, char *argv[]) {
* log command being run
*/
if (!strcmp(nulltoempty(getenv("V")), "0") && !IsTerminalInarticulate()) {
- p = xasprintf("\r\e[K%-15s%s\r", firstnonnull(action, "BUILD"),
- firstnonnull(target, nulltoempty(outpath)));
+ p = (xasprintf)("\r\e[K%-15s%s\r", firstnonnull(action, "BUILD"),
+ firstnonnull(target, nulltoempty(outpath)));
n = strlen(p);
} else {
if (IsTerminalInarticulate() &&
@@ -535,10 +596,9 @@ int main(int argc, char *argv[]) {
command.n > columns + 2) {
/* emacs command window is very slow so truncate lines */
command.n = columns + 2;
- command.p[command.n - 5] = '.';
command.p[command.n - 4] = '.';
command.p[command.n - 3] = '.';
- command.p[command.n - 2] = '\r';
+ command.p[command.n - 2] = '.';
command.p[command.n - 1] = '\n';
}
p = command.p;
@@ -551,7 +611,7 @@ int main(int argc, char *argv[]) {
* and we help FindDebugBinary to find debug symbols
*/
if (!IsWindows() && endswith(cmd, ".com")) {
- comdbg = xasprintf("%s.dbg", cmd);
+ comdbg = xasprintf("%s%s", cmd, ".dbg");
cachedcmd = xasprintf("o/%s", cmd);
if (fileexists(comdbg)) {
AddEnv(xasprintf("COMDBG=%s", comdbg));
@@ -578,7 +638,7 @@ int main(int argc, char *argv[]) {
*/
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, &savemask);
- ws = Launch();
+ ws = Launch(&ru);
/*
* if execve() failed unzip gcc and try again
@@ -587,7 +647,7 @@ int main(int argc, char *argv[]) {
startswith(cmd, "o/third_party/gcc") &&
fileexists("third_party/gcc/unbundle.sh")) {
system("third_party/gcc/unbundle.sh");
- ws = Launch();
+ ws = Launch(&ru);
}
/*
@@ -613,13 +673,15 @@ int main(int argc, char *argv[]) {
}
return 0;
} else {
- p = xasprintf("%s%s EXITED WITH %d%s: %.*s\r\n", RED2, DescribeCommand(),
- WEXITSTATUS(ws), RESET, command.n, command.p);
+ p = xasprintf("%s%s %s %d%s: %.*s%s\n", RED2, DescribeCommand(),
+ "exited with", WEXITSTATUS(ws), RESET, command.n, command.p,
+ GetResourceReport(&ru));
rc = WEXITSTATUS(ws);
}
} else {
- p = xasprintf("%s%s TERMINATED BY %s%s: %.*s\r\n", RED2, DescribeCommand(),
- strsignal(WTERMSIG(ws)), RESET, command.n, command.p);
+ p = xasprintf("%s%s %s %s%s: %.*s%s\n", RED2, DescribeCommand(),
+ "terminated by", strsignal(WTERMSIG(ws)), RESET, command.n,
+ command.p, GetResourceReport(&ru));
rc = 128 + WTERMSIG(ws);
}
diff --git a/tool/build/runit.c b/tool/build/runit.c
index e8fb5620f..34ddc1a7b 100644
--- a/tool/build/runit.c
+++ b/tool/build/runit.c
@@ -325,67 +325,55 @@ void SendRequest(void) {
CHECK_NE(-1, close(fd));
}
+bool Recv(unsigned char *p, size_t n) {
+ size_t i, rc;
+ static long backoff;
+ for (i = 0; i < n; i += rc) {
+ do {
+ rc = mbedtls_ssl_read(&ezssl, p + i, n - i);
+ } while (rc == MBEDTLS_ERR_SSL_WANT_READ);
+ if (!rc || rc == MBEDTLS_ERR_NET_CONN_RESET) {
+ usleep((backoff = (backoff + 1000) * 2));
+ return false;
+ } else if (rc < 0) {
+ EzTlsDie("read response failed", rc);
+ }
+ }
+ return true;
+}
+
int ReadResponse(void) {
int res;
ssize_t rc;
size_t n, m;
uint32_t size;
- unsigned char *p;
- enum RunitCommand cmd;
- static long backoff;
- static unsigned char msg[512];
- res = -1;
- for (;;) {
- while ((rc = mbedtls_ssl_read(&ezssl, msg, sizeof(msg))) < 0) {
- if (rc == MBEDTLS_ERR_SSL_WANT_READ) {
- continue;
- } else if (rc == MBEDTLS_ERR_NET_CONN_RESET) {
- CHECK_EQ(ECONNRESET, errno);
- usleep((backoff = (backoff + 1000) * 2));
- close(g_sock);
- return -1;
- } else {
- EzTlsDie("read response failed", rc);
- }
+ unsigned char b[512];
+ for (res = -1; res == -1;) {
+ if (!Recv(b, 5)) break;
+ CHECK_EQ(RUNITD_MAGIC, READ32BE(b));
+ switch (b[4]) {
+ case kRunitExit:
+ if (!Recv(b, 1)) break;
+ if ((res = *b)) {
+ WARNF("%s on %s exited with %d", g_prog, g_hostname, res);
+ }
+ break;
+ case kRunitStderr:
+ if (!Recv(b, 4)) break;
+ size = READ32BE(b);
+ for (; size; size -= n) {
+ n = MIN(size, sizeof(b));
+ if (!Recv(b, n)) goto drop;
+ CHECK_EQ(n, write(2, b, n));
+ }
+ break;
+ default:
+ fprintf(stderr, "error: received invalid runit command\n");
+ _exit(1);
}
- p = &msg[0];
- n = (size_t)rc;
- if (!n) break;
- do {
- CHECK_GE(n, 4 + 1);
- CHECK_EQ(RUNITD_MAGIC, READ32BE(p));
- p += 4, n -= 4;
- cmd = *p++, n--;
- switch (cmd) {
- case kRunitExit:
- CHECK_GE(n, 1);
- if ((res = *p & 0xff)) {
- WARNF("%s on %s exited with %d", g_prog, g_hostname, res);
- }
- goto drop;
- case kRunitStderr:
- CHECK_GE(n, 4);
- size = READ32BE(p), p += 4, n -= 4;
- while (size) {
- if (n) {
- CHECK_NE(-1, (rc = write(STDERR_FILENO, p, MIN(n, size))));
- CHECK_NE(0, (m = (size_t)rc));
- p += m, n -= m, size -= m;
- } else {
- CHECK_NE(-1, (rc = recv(g_sock, msg, sizeof(msg), 0)));
- p = &msg[0];
- n = (size_t)rc;
- if (!n) goto drop;
- }
- }
- break;
- default:
- __die();
- }
- } while (n);
}
drop:
- CHECK_NE(-1, close(g_sock));
+ close(g_sock);
return res;
}
diff --git a/tool/net/redbean.c b/tool/net/redbean.c
index ae32f263a..a936e2113 100644
--- a/tool/net/redbean.c
+++ b/tool/net/redbean.c
@@ -1179,60 +1179,6 @@ static void ReportWorkerExit(int pid, int ws) {
}
}
-static void AppendResourceReport(char **b, struct rusage *ru, const char *nl) {
- long utime, stime;
- long double ticks;
- if (ru->ru_maxrss) {
- appendf(b, "ballooned to %,ldkb in size%s", ru->ru_maxrss, nl);
- }
- if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) |
- (stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) {
- ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK));
- appendf(b, "needed %,ldµs cpu (%d%% kernel)%s", utime + stime,
- (int)((long double)stime / (utime + stime) * 100), nl);
- if (ru->ru_idrss) {
- appendf(b, "needed %,ldkb memory on average%s",
- lroundl(ru->ru_idrss / ticks), nl);
- }
- if (ru->ru_isrss) {
- appendf(b, "needed %,ldkb stack on average%s",
- lroundl(ru->ru_isrss / ticks), nl);
- }
- if (ru->ru_ixrss) {
- appendf(b, "mapped %,ldkb shared on average%s",
- lroundl(ru->ru_ixrss / ticks), nl);
- }
- }
- if (ru->ru_minflt || ru->ru_majflt) {
- appendf(b, "caused %,ld page faults (%d%% memcpy)%s",
- ru->ru_minflt + ru->ru_majflt,
- (int)((long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) *
- 100),
- nl);
- }
- if (ru->ru_nvcsw + ru->ru_nivcsw > 1) {
- appendf(
- b, "%,ld context switches (%d%% consensual)%s",
- ru->ru_nvcsw + ru->ru_nivcsw,
- (int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100),
- nl);
- }
- if (ru->ru_msgrcv || ru->ru_msgsnd) {
- appendf(b, "received %,ld message%s and sent %,ld%s", ru->ru_msgrcv,
- ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl);
- }
- if (ru->ru_inblock || ru->ru_oublock) {
- appendf(b, "performed %,ld read%s and %,ld write i/o operations%s",
- ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock, nl);
- }
- if (ru->ru_nsignals) {
- appendf(b, "received %,ld signals%s", ru->ru_nsignals, nl);
- }
- if (ru->ru_nswap) {
- appendf(b, "got swapped %,ld times%s", ru->ru_nswap, nl);
- }
-}
-
static void AddTimeval(struct timeval *x, const struct timeval *y) {
x->tv_sec += y->tv_sec;
x->tv_usec += y->tv_usec;
diff --git a/tool/viz/cpuid.c b/tool/viz/cpuid.c
index 1a8295eff..d10c36394 100644
--- a/tool/viz/cpuid.c
+++ b/tool/viz/cpuid.c
@@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
+#include "libc/bits/bits.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
#include "libc/nexgen32e/cpuid4.internal.h"
@@ -42,6 +43,10 @@ static void show(const char *constant, long value) {
printf("%-20s%#lx\n", constant, value);
}
+static void decimal(const char *a, long b, const char *c) {
+ printf("%-20s%ld%s\n", a, b, c);
+}
+
static void showvendor(void) {
printf("%.*s%.*s%.*s", 4, &KCPUIDS(0H, EBX), 4, &KCPUIDS(0H, EDX), 4,
&KCPUIDS(0H, ECX));
@@ -55,12 +60,6 @@ static void showmodel(void) {
}
}
-static void showspeed(void) {
- if (KCPUIDS(16H, EAX)) {
- printf(" %.1f%s", KCPUIDS(16H, EAX) / 1000.0, "ghz");
- }
-}
-
static void showstrata(void) {
if (getx86processormodel(kX86ProcessorModelKey)) {
printf(" (%s %s)",
@@ -91,14 +90,21 @@ void showcachesizes(void) {
}
int main(int argc, char *argv[]) {
+ int x;
long tsc_aux;
showvendor();
showmodel();
- showspeed();
showstrata();
printf("\n");
+ if (KCPUIDS(16H, EAX)) {
+ printf("\n");
+ if ((x = KCPUIDS(16H, EAX) & 0x7fff)) decimal("frequency", x, "mhz");
+ if ((x = KCPUIDS(16H, EBX) & 0x7fff)) decimal("turbo", x, "mhz");
+ if ((x = KCPUIDS(16H, ECX) & 0x7fff)) decimal("bus", x, "mhz");
+ }
+
if (X86_HAVE(HYPERVISOR)) {
unsigned eax, ebx, ecx, edx;
asm("push\t%%rbx\n\t"