Improve redbean

- Improve serialization
- Add Benchmark() API to redbean
- Refactor UNIX API to be assert() friendly
- Make the redbean Lua REPL print data structures
- Fix recent regressions in linenoise reverse search
- Add -i flag so redbean can be a language interpreter
This commit is contained in:
Justine Tunney 2022-04-25 08:30:14 -07:00
parent 2046c0d2ae
commit 451e3f73d9
74 changed files with 1781 additions and 1024 deletions

View file

@ -119,6 +119,9 @@ int fsync(int);
int ftruncate(int, int64_t); int ftruncate(int, int64_t);
int getdents(unsigned, void *, unsigned, long *); int getdents(unsigned, void *, unsigned, long *);
int getdomainname(char *, size_t); int getdomainname(char *, size_t);
int getegid(void) nosideeffect;
int geteuid(void) nosideeffect;
int getgid(void) nosideeffect;
int gethostname(char *, size_t); int gethostname(char *, size_t);
int getloadavg(double *, int); int getloadavg(double *, int);
int getpgid(int); int getpgid(int);
@ -130,6 +133,7 @@ int getrlimit(int, struct rlimit *);
int getrusage(int, struct rusage *); int getrusage(int, struct rusage *);
int getsid(int) nosideeffect; int getsid(int) nosideeffect;
int gettid(void); int gettid(void);
int getuid(void) nosideeffect;
int kill(int, int); int kill(int, int);
int killpg(int, int); int killpg(int, int);
int link(const char *, const char *) dontthrow; int link(const char *, const char *) dontthrow;
@ -196,6 +200,7 @@ int sysinfo(struct sysinfo *);
int touch(const char *, uint32_t); int touch(const char *, uint32_t);
int truncate(const char *, uint64_t); int truncate(const char *, uint64_t);
int ttyname_r(int, char *, size_t); int ttyname_r(int, char *, size_t);
int umask(int);
int uname(struct utsname *); int uname(struct utsname *);
int unlink(const char *); int unlink(const char *);
int unlink_s(const char **); int unlink_s(const char **);
@ -226,11 +231,6 @@ ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t);
ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t); ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t);
ssize_t write(int, const void *, size_t); ssize_t write(int, const void *, size_t);
struct dirent *readdir(DIR *); struct dirent *readdir(DIR *);
uint32_t getegid(void) nosideeffect;
uint32_t geteuid(void) nosideeffect;
uint32_t getgid(void) nosideeffect;
uint32_t getuid(void) nosideeffect;
uint32_t umask(uint32_t);
void rewinddir(DIR *); void rewinddir(DIR *);
void sync(void); void sync(void);

View file

@ -111,6 +111,7 @@ o/$(MODE)/libc/calls/execlp.o \
o/$(MODE)/libc/calls/execve-nt.o \ o/$(MODE)/libc/calls/execve-nt.o \
o/$(MODE)/libc/calls/execve-sysv.o \ o/$(MODE)/libc/calls/execve-sysv.o \
o/$(MODE)/libc/calls/readlinkat-nt.o \ o/$(MODE)/libc/calls/readlinkat-nt.o \
o/$(MODE)/libc/calls/describeopenflags.greg.o \
o/$(MODE)/libc/calls/mkntenvblock.o: \ o/$(MODE)/libc/calls/mkntenvblock.o: \
OVERRIDE_CPPFLAGS += \ OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED -DSTACK_FRAME_UNLIMITED

View file

@ -0,0 +1,38 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sol.h"
/**
* Describes clock_gettime() clock argument.
*/
char *DescribeClockName(int x) {
int i;
char *s;
_Alignas(char) static char buf[32];
if ((s = GetMagnumStr(kClockNames, x))) {
stpcpy(stpcpy(buf, "CLOCK_"), s);
return buf;
} else {
FormatInt32(buf, x);
return buf;
}
}

View file

@ -0,0 +1,41 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/mem/alloca.h"
#include "libc/sysv/consts/sol.h"
/**
* Describes clock_gettime() clock argument.
*/
char *DescribeOpenFlags(int x) {
char *s;
int i, n;
struct DescribeFlags *d;
_Alignas(char) static char openflags[128];
// TODO(jart): unify DescribeFlags and MagnumStr data structures
for (n = 0; kOpenFlags[n].x != MAGNUM_TERMINATOR;) ++n;
d = alloca(n * sizeof(struct DescribeFlags));
for (i = 0; i < n; ++i) {
d[i].flag = MAGNUM_NUMBER(kOpenFlags, i);
d[i].name = MAGNUM_STRING(kOpenFlags, i);
}
return DescribeFlags(openflags, sizeof(openflags), d, n, "O_", x);
}

View file

@ -28,6 +28,7 @@
*/ */
int32_t sys_fstatat(int32_t dirfd, const char *path, struct stat *st, int32_t sys_fstatat(int32_t dirfd, const char *path, struct stat *st,
int32_t flags) { int32_t flags) {
int rc;
void *p; void *p;
union metastat ms; union metastat ms;
if (IsAsan() && !__asan_is_valid(path, 1)) return efault(); if (IsAsan() && !__asan_is_valid(path, 1)) return efault();

View file

@ -25,7 +25,7 @@
* Returns effective group ID of calling process. * Returns effective group ID of calling process.
* @return group id * @return group id
*/ */
uint32_t getegid(void) { int getegid(void) {
int rc; int rc;
if (!IsWindows()) { if (!IsWindows()) {
rc = sys_getegid(); rc = sys_getegid();

View file

@ -24,7 +24,7 @@
* Returns effective user ID of calling process. * Returns effective user ID of calling process.
* @return user id * @return user id
*/ */
uint32_t geteuid(void) { int geteuid(void) {
int rc; int rc;
if (!IsWindows()) { if (!IsWindows()) {
rc = sys_geteuid(); rc = sys_geteuid();

View file

@ -51,7 +51,7 @@ static textwindows dontinline uint32_t GetUserNameHash(void) {
* @asyncsignalsafe * @asyncsignalsafe
* @vforksafe * @vforksafe
*/ */
uint32_t getuid(void) { int getuid(void) {
int rc; int rc;
if (!IsWindows()) { if (!IsWindows()) {
rc = sys_getuid(); rc = sys_getuid();
@ -71,7 +71,7 @@ uint32_t getuid(void) {
* @asyncsignalsafe * @asyncsignalsafe
* @vforksafe * @vforksafe
*/ */
uint32_t getgid(void) { int getgid(void) {
int rc; int rc;
if (!IsWindows()) { if (!IsWindows()) {
rc = sys_getgid(); rc = sys_getgid();

47
libc/calls/kclocknames.S Normal file
View file

@ -0,0 +1,47 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e s
.long \e - kClockNames
.long 1f - kClockNames
.rodata.str1.1
1: .string "\s"
.previous
.endm
.section .rodata
.align 4
.underrun
kClockNames:
.e CLOCK_REALTIME,"REALTIME"
.e CLOCK_MONOTONIC,"MONOTONIC"
.e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW"
.e CLOCK_REALTIME_COARSE,"REALTIME_COARSE"
.e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE"
.e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID"
.e CLOCK_TAI,"TAI"
.e CLOCK_PROF,"PROF"
.e CLOCK_BOOTTIME,"BOOTTIME"
.e CLOCK_REALTIME_ALARM,"REALTIME_ALARM"
.e CLOCK_BOOTTIME_ALARM,"BOOTTIME_ALARM"
.long MAGNUM_TERMINATOR
.endobj kClockNames,globl,hidden
.overrun

64
libc/calls/kopenflags.S Normal file
View file

@ -0,0 +1,64 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e s
.long \e - kOpenFlags
.long 1f - kOpenFlags
.rodata.str1.1
1: .string "\s"
.previous
.endm
.section .rodata
.align 4
.underrun
kOpenFlags:
.e O_RDWR,"RDWR" // order matters
.e O_RDONLY,"RDONLY" //
.e O_WRONLY,"WRONLY" //
.e O_ACCMODE,"ACCMODE" // mask of prev three
.e O_CREAT,"CREAT" //
.e O_EXCL,"EXCL" //
.e O_TRUNC,"TRUNC" //
.e O_CLOEXEC,"CLOEXEC" //
.e O_DIRECT,"DIRECT" // no-op on xnu/openbsd
.e O_APPEND,"APPEND" // weird on nt
.e O_TMPFILE,"TMPFILE" // linux, windows
.e O_NOFOLLOW,"NOFOLLOW" // unix
.e O_SYNC,"SYNC" // unix
.e O_ASYNC,"ASYNC" // unix
.e O_NOCTTY,"NOCTTY" // unix
.e O_NOATIME,"NOATIME" // linux
.e O_EXEC,"EXEC" // free/openbsd
.e O_SEARCH,"SEARCH" // free/netbsd
.e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd
.e O_RSYNC,"RSYNC" // linux/open/netbsd
.e O_PATH,"PATH" // linux
.e O_VERIFY,"VERIFY" // freebsd
.e O_SHLOCK,"SHLOCK" // bsd
.e O_EXLOCK,"EXLOCK" // bsd
.e O_RANDOM,"RANDOM" // windows
.e O_SEQUENTIAL,"SEQUENTIAL" // windows
.e O_COMPRESSED,"COMPRESSED" // windows
.e O_INDEXED,"INDEXED" // windows
.long MAGNUM_TERMINATOR
.endobj kOpenFlags,globl,hidden
.overrun

View file

@ -21,6 +21,7 @@
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h" #include "libc/calls/strace.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/str/str.h" #include "libc/str/str.h"
@ -74,7 +75,8 @@ int openat(int dirfd, const char *file, int flags, ...) {
} else { } else {
rc = efault(); rc = efault();
} }
STRACE("openat(%s, %#s, %#x, %#o) → %d% m", __strace_dirfd(buf, dirfd), file, STRACE("openat(%s, %#s, %s, %#o) → %d% m", __strace_dirfd(buf, dirfd), file,
flags, (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, rc); DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0,
rc);
return rc; return rc;
} }

View file

@ -27,8 +27,8 @@
* @return previous mask * @return previous mask
* @note always succeeds * @note always succeeds
*/ */
unsigned umask(unsigned newmask) { int umask(int newmask) {
unsigned oldmask; int oldmask;
if (!IsWindows()) { if (!IsWindows()) {
oldmask = sys_umask(newmask); oldmask = sys_umask(newmask);
} else { } else {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.macro .e e s .macro .e e s
@ -115,6 +116,6 @@ kErrnoDocs:
.e ENOTRECOVERABLE,"State not recoverable" .e ENOTRECOVERABLE,"State not recoverable"
.e ENONET,"Machine is not on the network" .e ENONET,"Machine is not on the network"
.e ERESTART,"Interrupted system call should be restarted" .e ERESTART,"Interrupted system call should be restarted"
.long -123 .long MAGNUM_TERMINATOR
.endobj kErrnoDocs,globl,hidden .endobj kErrnoDocs,globl,hidden
.overrun .overrun

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.macro .e e .macro .e e
@ -27,7 +28,7 @@
.endm .endm
.section .rodata .section .rodata
.align 4 .align 4
.underrun .underrun
kErrnoNames: kErrnoNames:
.e EINVAL .e EINVAL
@ -116,6 +117,6 @@ kErrnoNames:
.e ENONET .e ENONET
.e ERESTART .e ERESTART
.e ENODATA .e ENODATA
.long -123 .long MAGNUM_TERMINATOR
.endobj kErrnoNames,globl,hidden .endobj kErrnoNames,globl,hidden
.overrun .overrun

View file

@ -1,22 +1,35 @@
#ifndef COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ #ifndef COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_
#define COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_ #define COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_
#define MAGNUM_TERMINATOR -123
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#define MAGNUM_NUMBER(TABLE, INDEX) \
*(const int *)((uintptr_t)TABLE + TABLE[INDEX].x)
#define MAGNUM_STRING(TABLE, INDEX) \
(const char *)((uintptr_t)TABLE + TABLE[INDEX].s)
struct MagnumStr { struct MagnumStr {
int x, s; int x, s;
}; };
extern const struct MagnumStr kErrnoDocs[]; hidden extern const struct MagnumStr kClockNames[];
extern const struct MagnumStr kErrnoNames[]; hidden extern const struct MagnumStr kErrnoDocs[];
extern const struct MagnumStr kIpOptnames[]; hidden extern const struct MagnumStr kErrnoNames[];
extern const struct MagnumStr kSignalNames[]; hidden extern const struct MagnumStr kIpOptnames[];
extern const struct MagnumStr kSockOptnames[]; hidden extern const struct MagnumStr kOpenFlags[];
extern const struct MagnumStr kTcpOptnames[]; hidden extern const struct MagnumStr kSignalNames[];
hidden extern const struct MagnumStr kSockOptnames[];
hidden extern const struct MagnumStr kTcpOptnames[];
const char *DescribeSockLevel(int); char *DescribeClockName(int) hidden;
const char *DescribeSockOptname(int, int); char *DescribeOpenFlags(int) hidden;
const char *GetMagnumStr(const struct MagnumStr *, int); char *DescribeSockLevel(int) hidden;
char *DescribeSockOptname(int, int) hidden;
char *GetMagnumStr(const struct MagnumStr *, int) hidden;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -23,7 +23,7 @@
* Converts errno value to descriptive sentence. * Converts errno value to descriptive sentence.
* @return non-null rodata string or null if not found * @return non-null rodata string or null if not found
*/ */
const char *strerdoc(int x) { char *strerdoc(int x) {
if (x) { if (x) {
return GetMagnumStr(kErrnoDocs, x); return GetMagnumStr(kErrnoDocs, x);
} else { } else {

View file

@ -23,7 +23,7 @@
* Converts errno value to symbolic name. * Converts errno value to symbolic name.
* @return non-null rodata string or null if not found * @return non-null rodata string or null if not found
*/ */
const char *strerrno(int x) { char *strerrno(int x) {
if (x) { if (x) {
return GetMagnumStr(kErrnoNames, x); return GetMagnumStr(kErrnoNames, x);
} else { } else {

View file

@ -23,7 +23,7 @@
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
const char *DescribeMapFlags(int x) { const char *DescribeMapFlags(int x) {
static char mapflags[256]; _Alignas(char) static char mapflags[256];
const struct DescribeFlags kMapFlags[] = { const struct DescribeFlags kMapFlags[] = {
{MAP_ANONYMOUS, "ANONYMOUS"}, // {MAP_ANONYMOUS, "ANONYMOUS"}, //
{MAP_PRIVATE, "PRIVATE"}, // {MAP_PRIVATE, "PRIVATE"}, //

View file

@ -34,7 +34,7 @@ static const struct DescribeFlags kConsoleModeInputFlags[] = {
}; };
const char *DescribeNtConsoleModeInputFlags(uint32_t x) { const char *DescribeNtConsoleModeInputFlags(uint32_t x) {
static char consolemodeinputflags[256]; _Alignas(char) static char consolemodeinputflags[256];
return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags), return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags),
kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags), kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags),
"kNtEnable", x); "kNtEnable", x);

View file

@ -29,7 +29,7 @@ static const struct DescribeFlags kConsoleModeOutputFlags[] = {
}; };
const char *DescribeNtConsoleModeOutputFlags(uint32_t x) { const char *DescribeNtConsoleModeOutputFlags(uint32_t x) {
static char consolemodeoutputflags[128]; _Alignas(char) static char consolemodeoutputflags[128];
return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags), return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags),
kConsoleModeOutputFlags, kConsoleModeOutputFlags,
ARRAYLEN(kConsoleModeOutputFlags), "kNt", x); ARRAYLEN(kConsoleModeOutputFlags), "kNt", x);

View file

@ -64,7 +64,7 @@ static const struct DescribeFlags kFileAccessflags[] = {
}; };
const char *DescribeNtFileAccessFlags(uint32_t x) { const char *DescribeNtFileAccessFlags(uint32_t x) {
static char ntfileaccessflags[512]; _Alignas(char) static char ntfileaccessflags[512];
return DescribeFlags(ntfileaccessflags, sizeof(ntfileaccessflags), return DescribeFlags(ntfileaccessflags, sizeof(ntfileaccessflags),
kFileAccessflags, ARRAYLEN(kFileAccessflags), "kNt", x); kFileAccessflags, ARRAYLEN(kFileAccessflags), "kNt", x);
} }

View file

@ -52,7 +52,7 @@ static const struct DescribeFlags kFileFlags[] = {
}; };
const char *DescribeNtFileFlagsAndAttributes(uint32_t x) { const char *DescribeNtFileFlagsAndAttributes(uint32_t x) {
static char ntfileflags[256]; _Alignas(char) static char ntfileflags[256];
if (x == -1u) return "-1u"; if (x == -1u) return "-1u";
return DescribeFlags(ntfileflags, sizeof(ntfileflags), kFileFlags, return DescribeFlags(ntfileflags, sizeof(ntfileflags), kFileFlags,
ARRAYLEN(kFileFlags), "kNtFile", x); ARRAYLEN(kFileFlags), "kNtFile", x);

View file

@ -31,7 +31,7 @@ static const struct DescribeFlags kFileMapFlags[] = {
}; };
const char *DescribeNtFileMapFlags(uint32_t x) { const char *DescribeNtFileMapFlags(uint32_t x) {
static char filemapflags[64]; _Alignas(char) static char filemapflags[64];
return DescribeFlags(filemapflags, sizeof(filemapflags), kFileMapFlags, return DescribeFlags(filemapflags, sizeof(filemapflags), kFileMapFlags,
ARRAYLEN(kFileMapFlags), "kNtFileMap", x); ARRAYLEN(kFileMapFlags), "kNtFileMap", x);
} }

View file

@ -27,7 +27,7 @@ static const struct DescribeFlags kFileShareflags[] = {
}; };
const char *DescribeNtFileShareFlags(uint32_t x) { const char *DescribeNtFileShareFlags(uint32_t x) {
static char ntfileshareflags[64]; _Alignas(char) static char ntfileshareflags[64];
return DescribeFlags(ntfileshareflags, sizeof(ntfileshareflags), return DescribeFlags(ntfileshareflags, sizeof(ntfileshareflags),
kFileShareflags, ARRAYLEN(kFileShareflags), kFileShareflags, ARRAYLEN(kFileShareflags),
"kNtFileShare", x); "kNtFileShare", x);

View file

@ -29,7 +29,7 @@ static const struct DescribeFlags kFiletypeFlags[] = {
}; };
const char *DescribeNtFiletypeFlags(uint32_t x) { const char *DescribeNtFiletypeFlags(uint32_t x) {
static char filetypeflags[64]; _Alignas(char) static char filetypeflags[64];
return DescribeFlags(filetypeflags, sizeof(filetypeflags), kFiletypeFlags, return DescribeFlags(filetypeflags, sizeof(filetypeflags), kFiletypeFlags,
ARRAYLEN(kFiletypeFlags), "kNtFileType", x); ARRAYLEN(kFiletypeFlags), "kNtFileType", x);
} }

View file

@ -30,7 +30,7 @@ static const struct DescribeFlags kMoveFileInputFlags[] = {
}; };
const char *DescribeNtMoveFileInputFlags(uint32_t x) { const char *DescribeNtMoveFileInputFlags(uint32_t x) {
static char movefileflags[256]; _Alignas(char) static char movefileflags[256];
return DescribeFlags(movefileflags, sizeof(movefileflags), return DescribeFlags(movefileflags, sizeof(movefileflags),
kMoveFileInputFlags, ARRAYLEN(kMoveFileInputFlags), kMoveFileInputFlags, ARRAYLEN(kMoveFileInputFlags),
"kNtMovefile", x); "kNtMovefile", x);

View file

@ -42,7 +42,7 @@ static const struct DescribeFlags kPageFlags[] = {
}; };
const char *DescribeNtPageFlags(uint32_t x) { const char *DescribeNtPageFlags(uint32_t x) {
static char pageflags[64]; _Alignas(char) static char pageflags[64];
return DescribeFlags(pageflags, sizeof(pageflags), kPageFlags, return DescribeFlags(pageflags, sizeof(pageflags), kPageFlags,
ARRAYLEN(kPageFlags), "kNt", x); ARRAYLEN(kPageFlags), "kNt", x);
} }

View file

@ -33,7 +33,7 @@ static const struct DescribeFlags kPipeModeFlags[] = {
}; };
const char *DescribeNtPipeModeFlags(uint32_t x) { const char *DescribeNtPipeModeFlags(uint32_t x) {
static char pipemodeflags[64]; _Alignas(char) static char pipemodeflags[64];
return DescribeFlags(pipemodeflags, sizeof(pipemodeflags), kPipeModeFlags, return DescribeFlags(pipemodeflags, sizeof(pipemodeflags), kPipeModeFlags,
ARRAYLEN(kPipeModeFlags), "kNtPipe", x); ARRAYLEN(kPipeModeFlags), "kNtPipe", x);
} }

View file

@ -28,7 +28,7 @@ static const struct DescribeFlags kPipeOpenFlags[] = {
}; };
const char *DescribeNtPipeOpenFlags(uint32_t x) { const char *DescribeNtPipeOpenFlags(uint32_t x) {
static char pipeopenflags[64]; _Alignas(char) static char pipeopenflags[64];
return DescribeFlags(pipeopenflags, sizeof(pipeopenflags), kPipeOpenFlags, return DescribeFlags(pipeopenflags, sizeof(pipeopenflags), kPipeOpenFlags,
ARRAYLEN(kPipeOpenFlags), "kNtPipeAccess", x); ARRAYLEN(kPipeOpenFlags), "kNtPipeAccess", x);
} }

View file

@ -38,7 +38,7 @@ static const struct DescribeFlags kProcessAccessflags[] = {
}; };
const char *DescribeNtProcessAccessFlags(uint32_t x) { const char *DescribeNtProcessAccessFlags(uint32_t x) {
static char ntprocessaccessflags[256]; _Alignas(char) static char ntprocessaccessflags[256];
return DescribeFlags(ntprocessaccessflags, sizeof(ntprocessaccessflags), return DescribeFlags(ntprocessaccessflags, sizeof(ntprocessaccessflags),
kProcessAccessflags, ARRAYLEN(kProcessAccessflags), kProcessAccessflags, ARRAYLEN(kProcessAccessflags),
"kNtProcess", x); "kNtProcess", x);

View file

@ -39,7 +39,7 @@ static const struct DescribeFlags kNtStartFlags[] = {
}; };
const char *DescribeNtStartFlags(uint32_t x) { const char *DescribeNtStartFlags(uint32_t x) {
static char startflags[128]; _Alignas(char) static char startflags[128];
return DescribeFlags(startflags, sizeof(startflags), kNtStartFlags, return DescribeFlags(startflags, sizeof(startflags), kNtStartFlags,
ARRAYLEN(kNtStartFlags), "kNtStartf", x); ARRAYLEN(kNtStartFlags), "kNtStartf", x);
} }

View file

@ -26,7 +26,7 @@ static const struct DescribeFlags kSymbolicLinkflags[] = {
}; };
const char *DescribeNtSymbolicLinkFlags(uint32_t x) { const char *DescribeNtSymbolicLinkFlags(uint32_t x) {
static char ntsymboliclinkflags[64]; _Alignas(char) static char ntsymboliclinkflags[64];
return DescribeFlags(ntsymboliclinkflags, sizeof(ntsymboliclinkflags), return DescribeFlags(ntsymboliclinkflags, sizeof(ntsymboliclinkflags),
kSymbolicLinkflags, ARRAYLEN(kSymbolicLinkflags), kSymbolicLinkflags, ARRAYLEN(kSymbolicLinkflags),
"kNtSymbolicLinkFlag", x); "kNtSymbolicLinkFlag", x);

View file

@ -27,7 +27,7 @@ static const struct DescribeFlags kProtFlags[] = {
}; };
const char *DescribeProtFlags(int x) { const char *DescribeProtFlags(int x) {
static char protflags[64]; _Alignas(char) static char protflags[64];
return DescribeFlags(protflags, sizeof(protflags), kProtFlags, return DescribeFlags(protflags, sizeof(protflags), kProtFlags,
ARRAYLEN(kProtFlags), "PROT_", x); ARRAYLEN(kProtFlags), "PROT_", x);
} }

View file

@ -26,7 +26,7 @@ static const struct DescribeFlags kRemapFlags[] = {
}; };
const char *DescribeRemapFlags(int x) { const char *DescribeRemapFlags(int x) {
static char remapflags[64]; _Alignas(char) static char remapflags[64];
return DescribeFlags(remapflags, sizeof(remapflags), kRemapFlags, return DescribeFlags(remapflags, sizeof(remapflags), kRemapFlags,
ARRAYLEN(kRemapFlags), "MREMAP_", x); ARRAYLEN(kRemapFlags), "MREMAP_", x);
} }

View file

@ -18,11 +18,11 @@
*/ */
#include "libc/fmt/magnumstrs.internal.h" #include "libc/fmt/magnumstrs.internal.h"
const char *GetMagnumStr(const struct MagnumStr *ms, int x) { char *GetMagnumStr(const struct MagnumStr *ms, int x) {
int i; int i;
for (i = 0; ms[i].x != -123; ++i) { for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) {
if (x == *(const int *)((uintptr_t)ms + ms[i].x)) { if (x == MAGNUM_NUMBER(ms, i)) {
return (const char *)((uintptr_t)ms + ms[i].s); return MAGNUM_STRING(ms, i);
} }
} }
return 0; return 0;

View file

@ -28,6 +28,8 @@
STATIC_YOINK("__get_symbol_by_addr"); STATIC_YOINK("__get_symbol_by_addr");
#define MAXLEAKS 1000
static bool once; static bool once;
static bool hasleaks; static bool hasleaks;
@ -46,15 +48,18 @@ static noasan void CheckLeak(void *x, void *y, size_t n, void *a) {
static noasan void OnMemory(void *x, void *y, size_t n, void *a) { static noasan void OnMemory(void *x, void *y, size_t n, void *a) {
static int i; static int i;
if (n) { if (n) {
if (++i < 20) { if (MAXLEAKS) {
kprintf("%p %,lu bytes [dlmalloc]", x, n); if (i < MAXLEAKS) {
if (IsAsan()) { ++i;
__asan_print_trace(x); kprintf("%p %,lu bytes [dlmalloc]", x, n);
if (IsAsan()) {
__asan_print_trace(x);
}
kprintf("\n");
} else if (i == MAXLEAKS) {
++i;
kprintf("etc. etc.\n");
} }
kprintf("\n");
}
if (i == 20) {
kprintf("etc. etc.\n");
} }
} }
} }

13
libc/nt/struct/linger.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_
#define COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct linger_nt {
uint16_t l_onoff; /* on/off */
uint16_t l_linger; /* seconds */
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ */

View file

@ -22,7 +22,7 @@
/** /**
* Describes setsockopt() level arguments. * Describes setsockopt() level arguments.
*/ */
const char *DescribeSockLevel(int x) { char *DescribeSockLevel(int x) {
static char buf[12]; static char buf[12];
if (x == SOL_IP) return "SOL_IP"; if (x == SOL_IP) return "SOL_IP";
if (x == SOL_TCP) return "SOL_TCP"; if (x == SOL_TCP) return "SOL_TCP";

View file

@ -18,26 +18,32 @@
*/ */
#include "libc/fmt/itoa.h" #include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h" #include "libc/fmt/magnumstrs.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/sol.h"
/** /**
* Describes setsockopt() optname arguments. * Describes setsockopt() optname arguments.
*/ */
const char *DescribeSockOptname(int l, int x) { char *DescribeSockOptname(int l, int x) {
int i; int i;
static char buf[12], *s; char *ps, *s;
const struct MagnumStr *ms = 0; const struct MagnumStr *ms = 0;
_Alignas(char) static char buf[32];
if (x) { if (x) {
if (l == SOL_SOCKET) { if (l == SOL_SOCKET) {
ps = "SO_";
ms = kSockOptnames; ms = kSockOptnames;
} else if (l == SOL_TCP) { } else if (l == SOL_TCP) {
ps = "TCP_";
ms = kTcpOptnames; ms = kTcpOptnames;
} else if (l == SOL_IP) { } else if (l == SOL_IP) {
ps = "IP_";
ms = kIpOptnames; ms = kIpOptnames;
} }
} }
if (ms && (s = GetMagnumStr(ms, x))) { if (ms && (s = GetMagnumStr(ms, x))) {
return s; stpcpy(stpcpy(buf, ps), s);
return buf;
} else { } else {
FormatInt32(buf, x); FormatInt32(buf, x);
return buf; return buf;

View file

@ -20,8 +20,10 @@
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/calls/struct/timeval.h" #include "libc/calls/struct/timeval.h"
#include "libc/nt/struct/linger.h"
#include "libc/nt/winsock.h" #include "libc/nt/winsock.h"
#include "libc/sock/internal.h" #include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sock/yoink.inc" #include "libc/sock/yoink.inc"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/so.h" #include "libc/sysv/consts/so.h"
@ -33,6 +35,7 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname,
uint32_t *inout_optlen) { uint32_t *inout_optlen) {
uint64_t ms; uint64_t ms;
uint32_t in_optlen; uint32_t in_optlen;
struct linger_nt linger;
assert(fd->kind == kFdSocket); assert(fd->kind == kFdSocket);
if (out_opt_optval && inout_optlen) { if (out_opt_optval && inout_optlen) {
@ -55,6 +58,11 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname,
((struct timeval *)out_opt_optval)->tv_sec = ms / 1000; ((struct timeval *)out_opt_optval)->tv_sec = ms / 1000;
((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000; ((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000;
*inout_optlen = sizeof(struct timeval); *inout_optlen = sizeof(struct timeval);
} else if (optname == SO_LINGER && in_optlen == sizeof(struct linger)) {
linger = *(struct linger_nt *)out_opt_optval;
((struct linger *)out_opt_optval)->l_onoff = !!linger.l_onoff;
((struct linger *)out_opt_optval)->l_linger = linger.l_linger;
*inout_optlen = sizeof(struct linger);
} }
} }

View file

@ -16,24 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.macro .e e .macro .e e s
.long \e - kIpOptnames .long \e - kIpOptnames
.long 1f - kIpOptnames .long 1f - kIpOptnames
.rodata.str1.1 .rodata.str1.1
1: .string "\e" 1: .string "\s"
.previous .previous
.endm .endm
.section .rodata .section .rodata
.align 4 .align 4
.underrun .underrun
kIpOptnames: kIpOptnames:
.e IP_TOS # int .e IP_TOS,"TOS" # int
.e IP_MTU # int .e IP_MTU,"MTU" # int
.e IP_TTL # int .e IP_TTL,"TTL" # int
.e IP_HDRINCL # bool32 .e IP_HDRINCL,"HDRINCL" # bool32
.long -123 .long MAGNUM_TERMINATOR
.endobj kIpOptnames,globl,hidden .endobj kIpOptnames,globl,hidden
.overrun .overrun

View file

@ -16,13 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.macro .e e .macro .e e s
.long \e - kSockOptnames .long \e - kSockOptnames
.long 1f - kSockOptnames .long 1f - kSockOptnames
.rodata.str1.1 .rodata.str1.1
1: .string "\e" 1: .string "\s"
.previous .previous
.endm .endm
@ -30,20 +31,22 @@
.align 4 .align 4
.underrun .underrun
kSockOptnames: kSockOptnames:
.e SO_DEBUG # bool32 .e SO_DEBUG,"DEBUG" # bool32
.e SO_BROADCAST # bool32 .e SO_ACCEPTCONN,"ACCEPTCONN" # bool32
.e SO_REUSEADDR # bool32 .e SO_BROADCAST,"BROADCAST" # bool32
.e SO_REUSEPORT # bool32 .e SO_REUSEADDR,"REUSEADDR" # bool32
.e SO_KEEPALIVE # bool32 .e SO_REUSEPORT,"REUSEPORT" # bool32
.e SO_DONTROUTE # bool32 .e SO_KEEPALIVE,"KEEPALIVE" # bool32
.e SO_RCVTIMEO # timeval .e SO_DONTROUTE,"DONTROUTE" # bool32
.e SO_SNDTIMEO # timeval .e SO_RCVTIMEO,"RCVTIMEO" # timeval
.e SO_LINGER # linger .e SO_SNDTIMEO,"SNDTIMEO" # timeval
.e SO_SNDBUF # int .e SO_LINGER,"LINGER" # linger
.e SO_RCVBUF # int .e SO_TYPE,"TYPE" # int
.e SO_RCVLOWAT # int .e SO_SNDBUF,"SNDBUF" # int
.e SO_SNDLOWAT # int .e SO_RCVBUF,"RCVBUF" # int
.e SO_ERROR # int .e SO_RCVLOWAT,"RCVLOWAT" # int
.long -123 .e SO_SNDLOWAT,"SNDLOWAT" # int
.e SO_ERROR,"ERROR" # int
.long MAGNUM_TERMINATOR
.endobj kSockOptnames,globl,hidden .endobj kSockOptnames,globl,hidden
.overrun .overrun

View file

@ -16,33 +16,34 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.macro .e e .macro .e e s
.long \e - kTcpOptnames .long \e - kTcpOptnames
.long 1f - kTcpOptnames .long 1f - kTcpOptnames
.rodata.str1.1 .rodata.str1.1
1: .string "\e" 1: .string "\s"
.previous .previous
.endm .endm
.section .rodata .section .rodata
.align 4 .align 4
.underrun .underrun
kTcpOptnames: kTcpOptnames:
.e TCP_NODELAY # bool32 .e TCP_NODELAY,"NODELAY" # bool32
.e TCP_CORK # bool32 .e TCP_CORK,"CORK" # bool32
.e TCP_QUICKACK # bool32 .e TCP_QUICKACK,"QUICKACK" # bool32
.e TCP_FASTOPEN_CONNECT # bool32 .e TCP_FASTOPEN_CONNECT,"FASTOPEN_CONNECT" # bool32
.e TCP_DEFER_ACCEPT # bool32 .e TCP_DEFER_ACCEPT,"DEFER_ACCEPT" # bool32
.e TCP_KEEPIDLE # int (seconds) .e TCP_KEEPIDLE,"KEEPIDLE" # int (seconds)
.e TCP_KEEPINTVL # int (seconds) .e TCP_KEEPINTVL,"KEEPINTVL" # int (seconds)
.e TCP_FASTOPEN # int .e TCP_FASTOPEN,"FASTOPEN" # int
.e TCP_KEEPCNT # int .e TCP_KEEPCNT,"KEEPCNT" # int
.e TCP_MAXSEG # int .e TCP_MAXSEG,"MAXSEG" # int
.e TCP_SYNCNT # int .e TCP_SYNCNT,"SYNCNT" # int
.e TCP_NOTSENT_LOWAT # int .e TCP_NOTSENT_LOWAT,"NOTSENT_LOWAT" # int
.e TCP_WINDOW_CLAMP # int .e TCP_WINDOW_CLAMP,"WINDOW_CLAMP" # int
.long -123 .long MAGNUM_TERMINATOR
.endobj kTcpOptnames,globl,hidden .endobj kTcpOptnames,globl,hidden
.overrun .overrun

View file

@ -19,16 +19,12 @@
#include "libc/calls/struct/timeval.h" #include "libc/calls/struct/timeval.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nt/struct/linger.h"
#include "libc/sock/internal.h" #include "libc/sock/internal.h"
#include "libc/sysv/consts/so.h" #include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/sol.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
struct linger_nt { /* Linux+XNU+BSD ABI */
uint16_t l_onoff; /* on/off */
uint16_t l_linger; /* seconds */
};
textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname, textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname,
const void *optval, uint32_t optlen) { const void *optval, uint32_t optlen) {
int64_t ms; int64_t ms;

View file

@ -22,6 +22,7 @@
#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaction.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/paths.h" #include "libc/paths.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
.macro .e e s .macro .e e s
@ -65,6 +66,6 @@ kSignalNames:
.e SIGRTMIN,"RTMIN" .e SIGRTMIN,"RTMIN"
.e SIGEMT,"EMT" .e SIGEMT,"EMT"
.e SIGPWR,"PWR" .e SIGPWR,"PWR"
.long -123 .long MAGNUM_TERMINATOR
.endobj kSignalNames,globl,hidden .endobj kSignalNames,globl,hidden
.overrun .overrun

View file

@ -262,8 +262,8 @@ wint_t towctrans(wint_t, wctrans_t);
char *strsignal(int) returnsnonnull libcesque; char *strsignal(int) returnsnonnull libcesque;
char *strerror(int) returnsnonnull dontthrow nocallback; char *strerror(int) returnsnonnull dontthrow nocallback;
const char *strerrno(int) nosideeffect libcesque; char *strerrno(int) nosideeffect libcesque;
const char *strerdoc(int) nosideeffect libcesque; char *strerdoc(int) nosideeffect libcesque;
int strerror_r(int, char *, size_t) dontthrow nocallback; int strerror_r(int, char *, size_t) dontthrow nocallback;
int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback; int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback;

View file

@ -620,7 +620,7 @@ syscon clock CLOCK_MONOTONIC_RAW 4 4 0x4000 0x4000 0x4000 4 # actu
syscon clock CLOCK_REALTIME_COARSE 5 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_REALTIME_COARSE 5 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5
syscon clock CLOCK_MONOTONIC_COARSE 6 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_MONOTONIC_COARSE 6 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5
syscon clock CLOCK_PROF -1 -1 2 -1 2 -1 # syscon clock CLOCK_PROF -1 -1 2 -1 2 -1 #
syscon clock CLOCK_BOOTTIME 7 -1 -1 6 6 -1 # syscon clock CLOCK_BOOTTIME 7 -1 -1 6 -1 -1 #
syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 # syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 #
syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 # syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 #
syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 # syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 #
@ -669,16 +669,19 @@ syscon epoll EPOLLET 0x80000000 0x80000000 0x80000000 0x80000000 0x80000
# * -1 we define as no-op # * -1 we define as no-op
# #
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon so SO_DEBUG 1 1 1 1 1 1 # debugging is enabled; consensus
syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus
syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus
syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus
syscon so SO_REUSEPORT 15 0x0200 0x0200 0x0200 0x0200 4 # bsd consensus (NT calls it SO_REUSEADDR) syscon so SO_REUSEPORT 15 0x0200 0x0200 0x0200 0x0200 4 # bsd consensus (NT calls it SO_REUSEADDR)
syscon so SO_REUSEADDR 2 4 4 4 4 0 # bsd consensus (default behavior on NT) syscon so SO_REUSEADDR 2 4 4 4 4 4 # bsd consensus (default behavior on NT)
syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 ~4 # bsd consensus (default behavior on NT)
syscon so SO_KEEPALIVE 9 8 8 8 8 8 # bsd consensus syscon so SO_KEEPALIVE 9 8 8 8 8 8 # bsd consensus
syscon so SO_DONTROUTE 5 0x10 0x10 0x10 0x10 0x10 # bsd consensus syscon so SO_DONTROUTE 5 0x10 0x10 0x10 0x10 0x10 # bsd consensus
syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # bsd consensus syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # socket is configured for broadcast messages; bsd consensus
syscon so SO_USELOOPBACK 0 0x40 0x40 0x40 0x40 0x40 # bsd consensus syscon so SO_USELOOPBACK 0 0x40 0x40 0x40 0x40 0x40 # bsd consensus
syscon so SO_LINGER 13 0x80 0x80 0x80 0x80 0x80 # takes struct linger; causes close() return value to actually mean something; bsd consensus syscon so SO_LINGER 13 0x80 0x80 0x80 0x80 0x80 # takes struct linger; causes close() return value to actually mean something; bsd consensus
syscon so SO_DEBUG 1 1 1 1 1 1 # consensus syscon so SO_DONTLINGER 0 0 0 0 0 ~0x80 # disables so_linger on windows
syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus
syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus
syscon so SO_OOBINLINE 10 0x0100 0x0100 0x0100 0x0100 0x0100 # bsd consensus syscon so SO_OOBINLINE 10 0x0100 0x0100 0x0100 0x0100 0x0100 # bsd consensus
syscon so SO_SNDBUF 7 0x1001 0x1001 0x1001 0x1001 0x1001 # bsd consensus syscon so SO_SNDBUF 7 0x1001 0x1001 0x1001 0x1001 0x1001 # bsd consensus
syscon so SO_RCVBUF 8 0x1002 0x1002 0x1002 0x1002 0x1002 # bsd consensus syscon so SO_RCVBUF 8 0x1002 0x1002 0x1002 0x1002 0x1002 # bsd consensus
@ -686,7 +689,6 @@ syscon so SO_RCVTIMEO 20 0x1006 0x1006 0x1006 0x100c 0x1006 # rec
syscon so SO_SNDTIMEO 21 0x1005 0x1005 0x1005 0x100b 0x1005 # send timeout; takes struct timeval; bsd consensus syscon so SO_SNDTIMEO 21 0x1005 0x1005 0x1005 0x100b 0x1005 # send timeout; takes struct timeval; bsd consensus
syscon so SO_RCVLOWAT 18 0x1004 0x1004 0x1004 0x1004 0x1004 # bsd consensus syscon so SO_RCVLOWAT 18 0x1004 0x1004 0x1004 0x1004 0x1004 # bsd consensus
syscon so SO_SNDLOWAT 19 0x1003 0x1003 0x1003 0x1003 0x1003 # bsd consensus syscon so SO_SNDLOWAT 19 0x1003 0x1003 0x1003 0x1003 0x1003 # bsd consensus
syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus
syscon so SO_TIMESTAMP 29 0x0400 0x0400 0x0800 0x2000 0 syscon so SO_TIMESTAMP 29 0x0400 0x0400 0x0800 0x2000 0
syscon so SO_SETFIB 0 0 0x1014 0 0 0 syscon so SO_SETFIB 0 0 0x1014 0 0 0
syscon so SO_DOMAIN 39 0 0x1019 0x1024 0 0 syscon so SO_DOMAIN 39 0 0x1019 0x1024 0 0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,6,-1 .syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,-1,-1

View file

@ -0,0 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon so,SO_DONTLINGER,0,0,0,0,0,~0x80

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon so,SO_REUSEADDR,2,4,4,4,4,0 .syscon so,SO_REUSEADDR,2,4,4,4,4,4

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_ #ifndef COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_
#define COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_ #define COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nexgen32e/bench.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
#include "libc/testlib/bench.h" #include "libc/testlib/bench.h"
@ -9,151 +10,162 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#define EZBENCH_COUNT 128
#define EZBENCH_TRIES 10
#define EZBENCH(INIT, EXPR) EZBENCH2(#EXPR, INIT, EXPR) #define EZBENCH(INIT, EXPR) EZBENCH2(#EXPR, INIT, EXPR)
#define EZBENCH2(NAME, INIT, EXPR) \ #define EZBENCH2(NAME, INIT, EXPR) \
do { \ do { \
int Core, Tries, Interrupts; \ int Core, Tries, Interrupts; \
int64_t Speculative, MemoryStrict; \ int64_t Speculative, MemoryStrict; \
Tries = 0; \ Tries = 0; \
do { \ do { \
__testlib_yield(); \ __testlib_yield(); \
Core = __testlib_getcore(); \ Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \ Interrupts = __testlib_getinterrupts(); \
INIT; \ INIT; \
EXPR; \ EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, 128, ({ \ Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, ({ \
INIT; \ INIT; \
polluteregisters(); \ __polluteregisters(); \
}), \ }), \
(EXPR)); \ (EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ } while (++Tries < EZBENCH_TRIES && \
__testlib_getinterrupts() > Interrupts)); \ (__testlib_getcore() != Core && \
if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ __testlib_getinterrupts() > Interrupts)); \
Tries = 0; \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \
do { \ Tries = 0; \
__testlib_yield(); \ do { \
Core = __testlib_getcore(); \ __testlib_yield(); \
Interrupts = __testlib_getinterrupts(); \ Core = __testlib_getcore(); \
INIT; \ Interrupts = __testlib_getinterrupts(); \
EXPR; \ INIT; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \ EXPR; \
INIT; \ MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \
thrashcodecache(); \ INIT; \
polluteregisters(); \ thrashcodecache(); \
}), \ __polluteregisters(); \
(EXPR)); \ }), \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ (EXPR)); \
__testlib_getinterrupts() > Interrupts)); \ } while (++Tries < EZBENCH_TRIES && \
if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ (__testlib_getcore() != Core && \
__testlib_ezbenchreport( \ __testlib_getinterrupts() > Interrupts)); \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ __testlib_ezbenchreport( \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \
} while (0) } while (0)
#define EZBENCH3(NAME, NUM, INIT, EXPR) \ #define EZBENCH3(NAME, NUM, INIT, EXPR) \
do { \ do { \
int Core, Tries, Interrupts; \ int Core, Tries, Interrupts; \
int64_t Speculative, MemoryStrict; \ int64_t Speculative, MemoryStrict; \
Tries = 0; \ Tries = 0; \
do { \ do { \
__testlib_yield(); \ __testlib_yield(); \
Core = __testlib_getcore(); \ Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \ Interrupts = __testlib_getinterrupts(); \
INIT; \ INIT; \
EXPR; \ EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \ Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \
INIT; \ INIT; \
polluteregisters(); \ __polluteregisters(); \
}), \ }), \
(EXPR)); \ (EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ } while (++Tries < EZBENCH_TRIES && \
__testlib_getinterrupts() > Interrupts)); \ (__testlib_getcore() != Core && \
if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ __testlib_getinterrupts() > Interrupts)); \
Tries = 0; \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \
do { \ Tries = 0; \
__testlib_yield(); \ do { \
Core = __testlib_getcore(); \ __testlib_yield(); \
Interrupts = __testlib_getinterrupts(); \ Core = __testlib_getcore(); \
INIT; \ Interrupts = __testlib_getinterrupts(); \
EXPR; \ INIT; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \ EXPR; \
INIT; \ MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \
thrashcodecache(); \ INIT; \
polluteregisters(); \ thrashcodecache(); \
}), \ __polluteregisters(); \
(EXPR)); \ }), \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ (EXPR)); \
__testlib_getinterrupts() > Interrupts)); \ } while (++Tries < EZBENCH_TRIES && \
if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ (__testlib_getcore() != Core && \
__testlib_ezbenchreport( \ __testlib_getinterrupts() > Interrupts)); \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \ __testlib_ezbenchreport( \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \
} while (0) } while (0)
#define EZBENCH_C(NAME, CONTROL, EXPR) \ #define EZBENCH_C(NAME, CONTROL, EXPR) \
do { \ do { \
int Core, Tries, Interrupts; \ int Core, Tries, Interrupts; \
int64_t Control, Speculative, MemoryStrict; \ int64_t Control, Speculative, MemoryStrict; \
Tries = 0; \ Tries = 0; \
do { \ do { \
__testlib_yield(); \ __testlib_yield(); \
Core = __testlib_getcore(); \ Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \ Interrupts = __testlib_getinterrupts(); \
Control = BENCHLOOP(__startbench_m, __endbench_m, 128, ({ \ Control = BENCHLOOP(__startbench_m, __endbench_m, EZBENCH_COUNT, ({ \
thrashcodecache(); \ thrashcodecache(); \
polluteregisters(); \ __polluteregisters(); \
}), \ }), \
(CONTROL)); \ (CONTROL)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ } while (++Tries < EZBENCH_TRIES && \
__testlib_getinterrupts() > Interrupts)); \ (__testlib_getcore() != Core && \
if (Tries == 10) __testlib_ezbenchwarn(" control"); \ __testlib_getinterrupts() > Interrupts)); \
Tries = 0; \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" control"); \
do { \ Tries = 0; \
__testlib_yield(); \ do { \
Core = __testlib_getcore(); \ __testlib_yield(); \
Interrupts = __testlib_getinterrupts(); \ Core = __testlib_getcore(); \
EXPR; \ Interrupts = __testlib_getinterrupts(); \
Speculative = BENCHLOOP(__startbench, __endbench, 128, \ EXPR; \
polluteregisters(), (EXPR)); \ Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ __polluteregisters(), (EXPR)); \
__testlib_getinterrupts() > Interrupts)); \ } while (++Tries < EZBENCH_TRIES && \
if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \ (__testlib_getcore() != Core && \
Tries = 0; \ __testlib_getinterrupts() > Interrupts)); \
do { \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \
__testlib_yield(); \ Tries = 0; \
Core = __testlib_getcore(); \ do { \
Interrupts = __testlib_getinterrupts(); \ __testlib_yield(); \
EXPR; \ Core = __testlib_getcore(); \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \ Interrupts = __testlib_getinterrupts(); \
thrashcodecache(); \ EXPR; \
polluteregisters(); \ MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \
}), \ thrashcodecache(); \
(EXPR)); \ __polluteregisters(); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ }), \
__testlib_getinterrupts() > Interrupts)); \ (EXPR)); \
if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \ } while (++Tries < EZBENCH_TRIES && \
__testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \ (__testlib_getcore() != Core && \
MAX(0, MemoryStrict - Control)); \ __testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \
MAX(0, MemoryStrict - Control)); \
} while (0) } while (0)
#define EZBENCH_N(NAME, N, EXPR) \ #define EZBENCH_N(NAME, N, EXPR) \
do { \ do { \
int64_t Speculative, Toto; \ int64_t Speculative, Toto; \
int Core, Tries, Interrupts; \ int Core, Tries, Interrupts; \
Tries = 0; \ Tries = 0; \
do { \ do { \
__testlib_yield(); \ __testlib_yield(); \
Core = __testlib_getcore(); \ Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \ Interrupts = __testlib_getinterrupts(); \
EXPR; \ EXPR; \
Speculative = \ Speculative = BENCHLOOP(__startbench, __endbench, 32, \
BENCHLOOP(__startbench, __endbench, 32, polluteregisters(), (EXPR)); \ __polluteregisters(), (EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \ } while (++Tries < EZBENCH_TRIES && \
__testlib_getinterrupts() > Interrupts)); \ (__testlib_getcore() != Core && \
if (Tries == 10) __testlib_ezbenchwarn(""); \ __testlib_getinterrupts() > Interrupts)); \
__testlib_ezbenchreport_n( \ if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(""); \
NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ __testlib_ezbenchreport_n( \
NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \
} while (0) } while (0)
#define EZBENCH_K(NAME, K, EXPR) \ #define EZBENCH_K(NAME, K, EXPR) \
@ -164,14 +176,14 @@ COSMOPOLITAN_C_START_
__testlib_yield(); \ __testlib_yield(); \
Core = __testlib_getcore(); \ Core = __testlib_getcore(); \
EXPR; \ EXPR; \
Speculative = \ Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \
BENCHLOOP(__startbench, __endbench, 128, donothing, (EXPR)); \ donothing, (EXPR)); \
} while (Core != __testlib_getcore()); \ } while (Core != __testlib_getcore()); \
__testlib_ezbenchreport_n( \ __testlib_ezbenchreport_n( \
NAME, 'k', K, MAX(0, Speculative - __testlib_ezbenchcontrol())); \ NAME, 'k', K, MAX(0, Speculative - __testlib_ezbenchcontrol())); \
} while (0) } while (0)
void polluteregisters(void); void __polluteregisters(void);
void __testlib_yield(void); void __testlib_yield(void);
int __testlib_getcore(void); int __testlib_getcore(void);
int64_t __testlib_getinterrupts(void); int64_t __testlib_getinterrupts(void);

View file

@ -19,15 +19,15 @@
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
polluteregisters: __polluteregisters:
.leafprologue .leafprologue
xor %eax,%eax xor %eax,%eax
mov %ecx,%ecx xor %ecx,%ecx
mov %edx,%edx xor %edx,%edx
mov %r8d,%r8d xor %r8d,%r8d
mov %r9d,%r9d xor %r9d,%r9d
mov %r10d,%r10d xor %r10d,%r10d
mov %r11d,%r11d xor %r11d,%r11d
testb X86_HAVE(AVX)+kCpuids(%rip) testb X86_HAVE(AVX)+kCpuids(%rip)
jz .Lsse jz .Lsse
vpxor %xmm0,%xmm0,%xmm0 vpxor %xmm0,%xmm0,%xmm0
@ -48,13 +48,13 @@ polluteregisters:
xorps %xmm6,%xmm6 xorps %xmm6,%xmm6
xorps %xmm7,%xmm7 xorps %xmm7,%xmm7
.leafepilogue .leafepilogue
.endfn polluteregisters,globl .endfn __polluteregisters,globl
.end .end
// Fill registers with junk data to create false dependencies. // Fill registers with junk data to create false dependencies.
// Which shall create the problem that happens w/o vzeroupper. // Which shall create the problem that happens w/o vzeroupper.
// Or the Core Architecture errata regarding BSR/BSF w/ 64bit. // Or the Core Architecture errata regarding BSR/BSF w/ 64bit.
polluteregisters: __polluteregisters:
.leafprologue .leafprologue
mov $-1,%rax mov $-1,%rax
mov %rax,%rcx mov %rax,%rcx
@ -92,4 +92,4 @@ polluteregisters:
punpcklqdq %xmm0,%xmm6 punpcklqdq %xmm0,%xmm6
punpcklqdq %xmm0,%xmm7 punpcklqdq %xmm0,%xmm7
.leafepilogue .leafepilogue
.endfn polluteregisters,globl .endfn __polluteregisters,globl

View file

@ -17,6 +17,8 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
@ -24,6 +26,7 @@
#include "libc/runtime/gc.internal.h" #include "libc/runtime/gc.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/nr.h"
#include "libc/testlib/ezbench.h" #include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -68,18 +71,34 @@ static long Stat(const char *path, struct stat *st) {
return ax; return ax;
} }
static long Fstatat(const char *path, struct stat *st) {
long ax, di, si, dx;
register long r10 asm("r10") = 0;
asm volatile("syscall"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(r10)
: "0"(__NR_fstatat), "1"(AT_FDCWD), "2"(path), "3"(st)
: "rcx", "r8", "r9", "r11", "memory", "cc");
return ax;
}
BENCH(stat, bench) { BENCH(stat, bench) {
struct stat st; struct stat st;
union metastat ms;
EXPECT_SYS(0, 0, makedirs(".python/test", 0755)); EXPECT_SYS(0, 0, makedirs(".python/test", 0755));
EZBENCH2("__stat2cosmo", donothing, __stat2cosmo(&st, &ms));
EXPECT_SYS(0, 0, EXPECT_SYS(0, 0,
touch(".python/test/" touch(".python/test/"
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
0644)); 0644));
if (!IsWindows()) { if (!IsWindows() && !IsFreebsd()) {
EZBENCH2("stat syscall", donothing, EZBENCH2("stat syscall", donothing,
Stat(".python/test/" Stat(".python/test/"
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt", "tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
&st)); &st));
EZBENCH2("fstatat syscall", donothing,
Fstatat(".python/test/"
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
&st));
} }
EZBENCH2("stat() fs", donothing, EZBENCH2("stat() fs", donothing,
stat(".python/test/" stat(".python/test/"

View file

@ -19,8 +19,11 @@
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/bits/xchg.internal.h" #include "libc/bits/xchg.internal.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/linux/mmap.h"
#include "libc/linux/munmap.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/rand/rand.h" #include "libc/rand/rand.h"
@ -33,6 +36,7 @@
#include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -281,3 +285,57 @@ TEST(mmap, sharedFileMapFork) {
EXPECT_NE(-1, close(fd)); EXPECT_NE(-1, close(fd));
EXPECT_NE(-1, unlink(path)); EXPECT_NE(-1, unlink(path));
} }
////////////////////////////////////////////////////////////////////////////////
// BENCHMARKS
#define N (EZBENCH_COUNT * EZBENCH_TRIES)
int count;
void *ptrs[N];
void BenchUnmap(void) {
int i;
for (i = 0; i < count; ++i) {
if (ptrs[i]) {
ASSERT_EQ(0, munmap(ptrs[i], FRAMESIZE));
}
}
}
void BenchMmapPrivate(void) {
void *p;
p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
if (p == MAP_FAILED) abort();
ptrs[count++] = p;
}
BENCH(mmap, bench) {
EZBENCH2("mmap", donothing, BenchMmapPrivate());
BenchUnmap();
}
void BenchUnmapLinux(void) {
int i;
for (i = 0; i < count; ++i) {
if (ptrs[i]) {
ASSERT_EQ(0, LinuxMunmap(ptrs[i], FRAMESIZE));
}
}
}
void BenchMmapPrivateLinux(void) {
void *p;
p = (void *)LinuxMmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED) abort();
ptrs[count++] = p;
}
BENCH(mmap, benchLinux) {
void *p;
if (!IsLinux()) return;
EZBENCH2("mmap (linux)", donothing, BenchMmapPrivateLinux());
BenchUnmapLinux();
}

View file

@ -193,13 +193,14 @@ Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>\"");
#define DEBUG(L, ...) (void)0 #define DEBUG(L, ...) (void)0
#endif #endif
#define DUFF_ROUTINE_LOOP 0 #define DUFF_ROUTINE_LOOP 0
#define DUFF_ROUTINE_START 5 #define DUFF_ROUTINE_SEARCH 1
#define DUFF_ROUTINE_START 5
#define DUFF_ROUTINE_LABEL(STATE) \ #define DUFF_ROUTINE_LABEL(STATE) \
case STATE: \ case STATE: \
linenoiseRefreshLineForce(l); \ linenoiseRefreshLineForce(l); \
l->state = DUFF_ROUTINE_LOOP l->state = STATE
#define DUFF_ROUTINE_READ(STATE) \ #define DUFF_ROUTINE_READ(STATE) \
DUFF_ROUTINE_LABEL(STATE); \ DUFF_ROUTINE_LABEL(STATE); \
@ -469,11 +470,11 @@ static const char *FindSubstringReverse(const char *p, size_t n, const char *q,
n -= m; n -= m;
do { do {
for (i = 0; i < m; ++i) { for (i = 0; i < m; ++i) {
if (p[n + i] != q[i]) { if (kToLower[p[n + i] & 255] != kToLower[q[i] & 255]) {
break; break;
} }
} }
if (i == m) { if (kToLower[i & 255] == kToLower[m & 255]) {
return p + n; return p + n;
} }
} while (n--); } while (n--);
@ -1852,7 +1853,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
char seq[16]; char seq[16];
gotint = 0; gotint = 0;
if (prompt && (!l->prompt || strcmp(prompt, l->prompt))) { if (prompt && l->state != DUFF_ROUTINE_SEARCH &&
(!l->prompt || strcmp(prompt, l->prompt))) {
free(l->prompt); free(l->prompt);
l->prompt = strdup(prompt); l->prompt = strdup(prompt);
} }
@ -1885,7 +1887,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
for (fail = l->matlen = 0;;) { for (fail = l->matlen = 0;;) {
free(l->prompt); free(l->prompt);
l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen); l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen);
DUFF_ROUTINE_READ(1); DUFF_ROUTINE_READ(DUFF_ROUTINE_SEARCH);
fail = 1; fail = 1;
added = 0; added = 0;
l->j = l->pos; l->j = l->pos;
@ -1906,7 +1908,6 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
} else if (seq[0] == CTRL('G')) { } else if (seq[0] == CTRL('G')) {
linenoiseEditHistoryGoto(l, l->oldindex); linenoiseEditHistoryGoto(l, l->oldindex);
l->pos = l->olderpos; l->pos = l->olderpos;
rc = 0;
break; break;
} else if (iswcntrl(seq[0])) { // only sees canonical c0 } else if (iswcntrl(seq[0])) { // only sees canonical c0
break; break;
@ -1953,7 +1954,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
rc = 0; rc = 0;
linenoiseFreeCompletions(&l->lc); linenoiseFreeCompletions(&l->lc);
i = Backwards(l, l->pos, iswname); i = Backwards(l, l->pos, iswname);
j = Forwards(l, l->pos, iswname); j = l->pos;
{ {
char *s = strndup(l->buf + i, j - i); char *s = strndup(l->buf + i, j - i);
completionCallback(s, &l->lc); completionCallback(s, &l->lc);
@ -1966,7 +1967,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
if (linenoiseGrow(l, n + 1)) { if (linenoiseGrow(l, n + 1)) {
memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1); memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1);
memcpy(l->buf + i, l->lc.cvec[0], m); memcpy(l->buf + i, l->lc.cvec[0], m);
l->len = l->pos = n; l->pos = i + m;
l->len = n;
} }
continue; continue;
} }

View file

@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_
char *LuaFormatStack(lua_State *) dontdiscard; char *LuaFormatStack(lua_State *) dontdiscard;
int LuaCallWithTrace(lua_State *, int, int, lua_State *); int LuaCallWithTrace(lua_State *, int, int, lua_State *);
int LuaEncodeJsonData(lua_State *, char **, int, char *); int LuaEncodeJsonData(lua_State *, char **, int, char *, int);
int LuaEncodeLuaData(lua_State *, char **, int, char *); int LuaEncodeLuaData(lua_State *, char **, int, char *, int);
int LuaEncodeUrl(lua_State *); int LuaEncodeUrl(lua_State *);
int LuaParseUrl(lua_State *); int LuaParseUrl(lua_State *);
int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int);

View file

@ -16,20 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
#include "libc/str/str.h"
#include "third_party/lua/cosmo.h" #include "third_party/lua/cosmo.h"
#include "third_party/lua/lua.h" #include "third_party/lua/lua.h"
void EscapeLuaString(char *s, size_t len, char **buf) { void EscapeLuaString(char *s, size_t len, char **buf) {
appendw(buf, '"'); appendw(buf, '"');
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
if (s[i] == '\\' || s[i] == '\"' || s[i] == '\n' || s[i] == '\0' || if (' ' <= s[i] && s[i] <= 0x7e) {
s[i] == '\r') { appendw(buf, s[i]);
} else if (s[i] == '\n') {
appendw(buf, '\\' | 'n' << 8);
} else if (s[i] == '\\' || s[i] == '\'' || s[i] == '\"') {
appendw(buf, '\\' | s[i] << 8);
} else {
appendw(buf, '\\' | 'x' << 010 | appendw(buf, '\\' | 'x' << 010 |
"0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 | "0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 |
"0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030); "0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030);
} else {
appendd(buf, s + i, 1);
} }
} }
appendw(buf, '"'); appendw(buf, '"');

View file

@ -32,6 +32,7 @@
#include "libc/intrin/kprintf.h" #include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.internal.h" #include "libc/intrin/nomultics.internal.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/runtime/gc.h" #include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
@ -50,6 +51,17 @@ Copyright 19942021 Lua.org, PUC-Rio.\"");
asm(".include \"libc/disclaimer.inc\""); asm(".include \"libc/disclaimer.inc\"");
static const char *const kKeywordHints[] = {
"else", //
"elseif", //
"function", //
"function", //
"repeat", //
"then", //
"until", //
"while", //
};
bool lua_repl_blocking; bool lua_repl_blocking;
bool lua_repl_isterminal; bool lua_repl_isterminal;
linenoiseCompletionCallback *lua_repl_completions_callback; linenoiseCompletionCallback *lua_repl_completions_callback;
@ -74,46 +86,59 @@ static const char *g_historypath;
#define LUA_MAXINPUT 512 #define LUA_MAXINPUT 512
#endif #endif
static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) {
char **p = c->cvec; static void lua_readline_addcompletion (linenoiseCompletions *c, const char *s) {
size_t n = c->len + 1; char **p, *t;
if ((p = realloc(p, n * sizeof(*p)))) { if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) {
p[n - 1] = s;
c->cvec = p; c->cvec = p;
c->len = n; if ((t = strdup(s))) {
c->cvec[c->len++] = t;
}
} }
} }
void lua_readline_completions(const char *p, linenoiseCompletions *c) {
void lua_readline_completions (const char *p, linenoiseCompletions *c) {
int i;
lua_State *L; lua_State *L;
const char *name; const char *name;
for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) {
if (startswithi(kKeywordHints[i], p)) {
lua_readline_addcompletion(c, kKeywordHints[i]);
}
}
L = globalL; L = globalL;
lua_pushglobaltable(L); lua_pushglobaltable(L);
lua_pushnil(L); lua_pushnil(L);
while (lua_next(L, -2) != 0) { while (lua_next(L, -2) != 0) {
name = lua_tostring(L, -2); name = lua_tostring(L, -2);
if (startswithi(name, p)) { if (startswithi(name, p)) {
lua_readline_addcompletion(c, strdup(name)); lua_readline_addcompletion(c, name);
} }
lua_pop(L, 1); lua_pop(L, 1);
} }
lua_pop(L, 1); lua_pop(L, 1);
lua_repl_completions_callback(p, c); if (lua_repl_completions_callback) {
lua_repl_completions_callback(p, c);
}
} }
char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) {
char *h = 0; char *lua_readline_hint (const char *p, const char **ansi1, const char **ansi2) {
char *h;
linenoiseCompletions c = {0}; linenoiseCompletions c = {0};
lua_readline_completions(p, &c); lua_readline_completions(p, &c);
if (c.len == 1) h = strdup(c.cvec[0] + strlen(p)); h = c.len == 1 ? strdup(c.cvec[0] + strlen(p)) : 0;
linenoiseFreeCompletions(&c); linenoiseFreeCompletions(&c);
return h; return h;
} }
static void lua_freeline (lua_State *L, char *b) { static void lua_freeline (lua_State *L, char *b) {
free(b); free(b);
} }
/* /*
** Return the string to be used as a prompt by the interpreter. Leave ** Return the string to be used as a prompt by the interpreter. Leave
** the string (or nil, if using the default value) on the stack, to keep ** the string (or nil, if using the default value) on the stack, to keep
@ -129,6 +154,7 @@ static const char *get_prompt (lua_State *L, int firstline) {
} }
} }
/* mark in error messages for incomplete statements */ /* mark in error messages for incomplete statements */
#define EOFMARK "<eof>" #define EOFMARK "<eof>"
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1) #define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
@ -165,7 +191,7 @@ static ssize_t pushline (lua_State *L, int firstline) {
prmt = strdup(get_prompt(L, firstline)); prmt = strdup(get_prompt(L, firstline));
lua_pop(L, 1); /* remove prompt */ lua_pop(L, 1); /* remove prompt */
LUA_REPL_UNLOCK; LUA_REPL_UNLOCK;
rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking); rc = linenoiseEdit(lua_repl_linenoise, 0, &b, !firstline || lua_repl_blocking);
free(prmt); free(prmt);
if (rc != -1) { if (rc != -1) {
if (b && *b) { if (b && *b) {
@ -187,10 +213,11 @@ static ssize_t pushline (lua_State *L, int firstline) {
l = strlen(b); l = strlen(b);
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
b[--l] = '\0'; /* remove it */ b[--l] = '\0'; /* remove it */
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ if (firstline && b[0] == '=') { /* for compatibility with 5.2, ... */
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
else } else {
lua_pushlstring(L, b, l); lua_pushlstring(L, b, l);
}
lua_freeline(L, b); lua_freeline(L, b);
return 1; return 1;
} }

View file

@ -80,6 +80,14 @@ o/$(MODE)/third_party/lua/lua.com: \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \ @$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/third_party/lua/.lua/.symtab o/$(MODE)/third_party/lua/.lua/.symtab
o//third_party/lua/lgc.o: \
OVERRIDE_CFLAGS += \
-O2
o/$(MODE)/third_party/lua/lvm.o: \
OVERRIDE_CFLAGS += \
-fno-gcse
o/$(MODE)/third_party/lua/lauxlib.o: \ o/$(MODE)/third_party/lua/lauxlib.o: \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
-DSTACK_FRAME_UNLIMITED -DSTACK_FRAME_UNLIMITED
@ -89,6 +97,8 @@ $(THIRD_PARTY_LUA_OBJS): \
-ffunction-sections \ -ffunction-sections \
-fdata-sections -fdata-sections
$(THIRD_PARTY_LUA_OBJS): third_party/lua/lua.mk
.PHONY: o/$(MODE)/third_party/lua .PHONY: o/$(MODE)/third_party/lua
o/$(MODE)/third_party/lua: \ o/$(MODE)/third_party/lua: \
$(THIRD_PARTY_LUA_BINS) \ $(THIRD_PARTY_LUA_BINS) \

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/fmt/itoa.h"
#include "libc/runtime/gc.internal.h" #include "libc/runtime/gc.internal.h"
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
#include "net/http/escape.h" #include "net/http/escape.h"
@ -23,63 +25,115 @@
#include "third_party/lua/lauxlib.h" #include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h" #include "third_party/lua/lua.h"
int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat) { int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat,
size_t idx = -1; int idx) {
size_t tbllen, buflen; char *s;
bool isarray; bool isarray;
int t = lua_type(L, idx); size_t tbllen, z;
if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); char ibuf[21], fmt[] = "%.14g";
if (LUA_TSTRING == t) { if (level > 0) {
appendw(buf, '"'); switch (lua_type(L, idx)) {
appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, idx), -1, 0))); case LUA_TSTRING:
appendw(buf, '"'); s = lua_tolstring(L, idx, &z);
} else if (LUA_TNUMBER == t) { s = EscapeJsStringLiteral(s, z, &z);
appendf(buf, numformat, lua_tonumber(L, idx));
} else if (LUA_TBOOLEAN == t) {
appends(buf, lua_toboolean(L, idx) ? "true" : "false");
} else if (LUA_TTABLE == t) {
tbllen = lua_rawlen(L, idx);
// encode tables with numeric indices and empty tables as arrays
isarray = tbllen > 0 || // integer keys present
(lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
appendw(buf, isarray ? '[' : '{');
if (isarray) {
for (int i = 1; i <= tbllen; i++) {
if (i > 1) appendw(buf, ',');
lua_rawgeti(L, -1, i); // table/-2, value/-1
LuaEncodeJsonData(L, buf, level - 1, numformat);
lua_pop(L, 1);
}
} else {
int i = 1;
lua_pushnil(L); // push the first key
while (lua_next(L, -2) != 0) {
if (!lua_isstring(L, -2))
return luaL_argerror(L, 1, "expected number or string as key value");
if (i++ > 1) appendw(buf, ',');
appendw(buf, '"'); appendw(buf, '"');
if (lua_type(L, -2) == LUA_TSTRING) { appendd(buf, s, z);
appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, -2), -1, 0))); appendw(buf, '"');
free(s);
return 0;
case LUA_TNIL:
appendw(buf, READ32LE("null"));
return 0;
case LUA_TFUNCTION:
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TUSERDATA:
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TLIGHTUSERDATA:
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TTHREAD:
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TNUMBER:
if (lua_isinteger(L, idx)) {
appendd(buf, ibuf,
FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf);
} else { } else {
// we'd still prefer to use lua_tostring on a numeric index, but can't // TODO(jart): replace this api
// use it in-place, as it breaks lua_next (changes numeric key to a while (*numformat == '%' || *numformat == '.' ||
// string) isdigit(*numformat)) {
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 ++numformat;
appends(buf, lua_tostring(L, idx)); // use the copy }
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 switch (*numformat) {
case 'a':
case 'g':
case 'f':
fmt[4] = *numformat;
break;
default:
return luaL_error(L, "numformat string not allowed");
}
appendf(buf, fmt, lua_tonumber(L, idx));
} }
appendw(buf, '"' | ':' << 010); return 0;
LuaEncodeJsonData(L, buf, level - 1, numformat); case LUA_TBOOLEAN:
lua_pop(L, 1); // table/-2, key/-1 appends(buf, lua_toboolean(L, idx) ? "true" : "false");
} return 0;
// stack: table/-1, as the key was popped by lua_next case LUA_TTABLE:
tbllen = lua_rawlen(L, idx);
// encode tables with numeric indices and empty tables as arrays
isarray =
tbllen > 0 || // integer keys present
(lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
appendw(buf, isarray ? '[' : '{');
if (isarray) {
for (size_t i = 1; i <= tbllen; i++) {
if (i > 1) appendw(buf, ',');
lua_rawgeti(L, -1, i); // table/-2, value/-1
LuaEncodeJsonData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1);
}
} else {
int i = 1;
lua_pushnil(L); // push the first key
while (lua_next(L, -2)) {
if (!lua_isstring(L, -2)) {
luaL_error(L, "expected number or string as key value");
unreachable;
}
if (i++ > 1) appendw(buf, ',');
appendw(buf, '"');
if (lua_type(L, -2) == LUA_TSTRING) {
s = lua_tolstring(L, -2, &z);
s = EscapeJsStringLiteral(s, z, &z);
appendd(buf, s, z);
free(s);
} else {
// we'd still prefer to use lua_tostring on a numeric index, but
// can't use it in-place, as it breaks lua_next (changes numeric
// key to a string)
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
s = lua_tolstring(L, idx, &z);
appendd(buf, s, z); // use the copy
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
}
appendw(buf, '"' | ':' << 010);
LuaEncodeJsonData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1); // table/-2, key/-1
}
// stack: table/-1, as the key was popped by lua_next
}
appendw(buf, isarray ? ']' : '}');
return 0;
default:
luaL_error(L, "can't serialize value of this type");
unreachable;
} }
appendw(buf, isarray ? ']' : '}');
} else if (LUA_TNIL == t) {
appendd(buf, "null", 4);
} else { } else {
return luaL_argerror(L, 1, "can't serialize value of this type"); luaL_error(L, "too many nested tables");
unreachable;
} }
return 0;
} }

View file

@ -16,51 +16,97 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h"
#include "libc/fmt/itoa.h"
#include "libc/math.h" #include "libc/math.h"
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
#include "third_party/lua/cosmo.h" #include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h" #include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h" #include "third_party/lua/lua.h"
int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat) { int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat,
size_t idx = -1; int idx) {
size_t tbllen, buflen, slen;
char *s; char *s;
int ktype; int ktype;
int t = lua_type(L, idx); lua_Integer i;
if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); size_t tbllen, buflen, slen;
if (LUA_TSTRING == t) { char ibuf[21], fmt[] = "%.14g";
s = lua_tolstring(L, idx, &slen); if (level > 0) {
EscapeLuaString(s, slen, buf); switch (lua_type(L, idx)) {
} else if (LUA_TNUMBER == t) { case LUA_TNIL:
appendf(buf, numformat, lua_tonumber(L, idx)); appendw(buf, READ32LE("nil"));
} else if (LUA_TBOOLEAN == t) { return 0;
appends(buf, lua_toboolean(L, idx) ? "true" : "false"); case LUA_TSTRING:
} else if (LUA_TTABLE == t) { s = lua_tolstring(L, idx, &slen);
appendw(buf, '{'); EscapeLuaString(s, slen, buf);
int i = 0; return 0;
lua_pushnil(L); // push the first key case LUA_TFUNCTION:
while (lua_next(L, -2) != 0) { appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
ktype = lua_type(L, -2); return 0;
if (ktype == LUA_TTABLE) case LUA_TUSERDATA:
return luaL_argerror(L, 1, "can't serialize key of this type"); appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
if (i++ > 0) appendd(buf, ",", 1); return 0;
if (ktype != LUA_TNUMBER || floor(lua_tonumber(L, -2)) != i) { case LUA_TLIGHTUSERDATA:
appendw(buf, '['); appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 return 0;
LuaEncodeLuaData(L, buf, level, numformat); case LUA_TTHREAD:
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
appendw(buf, ']' | '=' << 010); return 0;
} case LUA_TNUMBER:
LuaEncodeLuaData(L, buf, level - 1, numformat); if (lua_isinteger(L, idx)) {
lua_pop(L, 1); // table/-2, key/-1 appendd(buf, ibuf,
FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf);
} else {
// TODO(jart): replace this api
while (*numformat == '%' || *numformat == '.' ||
isdigit(*numformat)) {
++numformat;
}
switch (*numformat) {
case 'a':
case 'g':
case 'f':
fmt[4] = *numformat;
break;
default:
return luaL_error(L, "numformat string not allowed");
}
appendf(buf, fmt, lua_tonumber(L, idx));
}
return 0;
case LUA_TBOOLEAN:
if (lua_toboolean(L, idx)) {
appendw(buf, READ32LE("true"));
} else {
appendw(buf, READ64LE("false\0\0"));
}
return 0;
case LUA_TTABLE:
i = 0;
appendw(buf, '{');
lua_pushnil(L); // push the first key
while (lua_next(L, -2) != 0) {
ktype = lua_type(L, -2);
if (i++ > 0) appendw(buf, ',');
if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) {
appendw(buf, '[');
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
LuaEncodeLuaData(L, buf, level - 1, numformat, -1);
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1
appendw(buf, ']' | '=' << 010);
}
LuaEncodeLuaData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1); // table/-2, key/-1
}
// stack: table/-1, as the key was popped by lua_next
appendw(buf, '}');
return 0;
default:
luaL_error(L, "can't serialize value of this type");
unreachable;
} }
// stack: table/-1, as the key was popped by lua_next
appendw(buf, '}');
} else if (LUA_TNIL == t) {
appendd(buf, "nil", 3);
} else { } else {
return luaL_argerror(L, 1, "can't serialize value of this type"); luaL_error(L, "too many nested tables");
unreachable;
} }
return 0;
} }

View file

@ -18,31 +18,17 @@
*/ */
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
#include "third_party/lua/cosmo.h" #include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
dontdiscard char *LuaFormatStack(lua_State *L) { dontdiscard char *LuaFormatStack(lua_State *L) {
size_t l;
int i, top; int i, top;
char *b = 0; char *p, *b = 0;
top = lua_gettop(L); top = lua_gettop(L);
for (i = 1; i <= top; i++) { for (i = 1; i <= top; i++) {
if (i > 1) appendw(&b, '\n'); if (i > 1) appendw(&b, '\n');
appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i)); appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i));
switch (lua_type(L, i)) { LuaEncodeLuaData(L, &b, 64, "g", -1);
case LUA_TNUMBER:
appendf(&b, "%g", lua_tonumber(L, i));
break;
case LUA_TSTRING:
appends(&b, lua_tostring(L, i));
break;
case LUA_TBOOLEAN:
appends(&b, lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNIL:
appends(&b, "nil");
break;
default:
appendf(&b, "%p", lua_topointer(L, i));
break;
}
} }
return b; return b;
} }

View file

@ -2270,8 +2270,7 @@ static void OnVidyaServiceTeletypeOutput(void) {
char buf[12]; char buf[12];
n = 0 /* FormatCga(m->bx[0], buf) */; n = 0 /* FormatCga(m->bx[0], buf) */;
w = tpenc(VidyaServiceXlatTeletype(m->ax[0])); w = tpenc(VidyaServiceXlatTeletype(m->ax[0]));
do do buf[n++] = w;
buf[n++] = w;
while ((w >>= 8)); while ((w >>= 8));
PtyWrite(pty, buf, n); PtyWrite(pty, buf, n);
} }

View file

@ -89,6 +89,7 @@ o/$(MODE)/tool/build/blinkenlights.com.dbg: \
$(APE_NO_MODIFY_SELF) $(APE_NO_MODIFY_SELF)
@$(APELINK) @$(APELINK)
.PRECIOUS: o/$(MODE)/tool/build/blinkenlights.com
o/$(MODE)/tool/build/blinkenlights.com: \ o/$(MODE)/tool/build/blinkenlights.com: \
o/$(MODE)/tool/build/blinkenlights.com.dbg \ o/$(MODE)/tool/build/blinkenlights.com.dbg \
o/$(MODE)/third_party/zip/zip.com \ o/$(MODE)/third_party/zip/zip.com \

View file

@ -88,6 +88,14 @@ else
Write('<dd>%s\r\n' % {enabled}) Write('<dd>%s\r\n' % {enabled})
end end
errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)
Write('<dt>unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n')
if errno then
Write('<dd>%s\r\n' % {EscapeHtml(unix.strerrno(errno))})
else
Write('<dd>%s\r\n' % {enabled})
end
errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR) errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)
Write('<dt>unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n') Write('<dt>unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n')
if errno then if errno then

View file

@ -13,9 +13,9 @@ function main()
syscall = 'pipe' syscall = 'pipe'
reader, writer, errno = unix.pipe() reader, writer, errno = unix.pipe()
if reader then if reader then
-- oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN) oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN)
-- oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN) oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)
-- oldmask = unix.sigprocmask(unix.SIG_BLOCK, unix.SIGCHLD) oldmask = unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1)))
syscall = 'fork' syscall = 'fork'
child, errno = unix.fork() child, errno = unix.fork()
if child then if child then

View file

@ -42,6 +42,7 @@ FLAGS
-u uniprocess -u uniprocess
-z print port -z print port
-m log messages -m log messages
-i interpreter mode
-b log message bodies -b log message bodies
-a log resource usage -a log resource usage
-g log handler latency -g log handler latency
@ -236,9 +237,9 @@ USAGE
REPL REPL
Your redbean displays a REPL that lets you modify the state of the Your redbean displays a Read-Eval-Print-Loop that lets you modify the
main server process while your server is running. Any changes will state of the main server process while your server is running. Any
propagate into forked clients. changes will propagate into forked clients.
Your REPL is displayed only when redbean is run as a non-daemon in a Your REPL is displayed only when redbean is run as a non-daemon in a
UNIX terminal or the Windows 10 command prompt or powershell. Since UNIX terminal or the Windows 10 command prompt or powershell. Since
@ -250,6 +251,20 @@ REPL
A history of your commands is saved to `~/.redbean_history`. A history of your commands is saved to `~/.redbean_history`.
If you love the redbean repl and want to use it as your language
interpreter then you can pass the `-i` flag to put redbean into
interpreter mode.
redbean.com -i binarytrees.lua 15
In this mode redbean won't start a web server and instead functions
like the `lua` command. The first command line argument becomes the
script you want to run. If you don't supply a script, then the repl
without a web server is displayed.
This can be useful for testing, since the redbean extensions and
modules for the Lua language are still made available.
SECURITY SECURITY
@ -357,12 +372,36 @@ SPECIAL PATHS
GLOBALS GLOBALS
argv: array[str] arg: array[str]
Array of command line arguments, excluding those parsed by Array of command line arguments, excluding those parsed by
getopt() in the C code, which stops parsing at the first getopt() in the C code, which stops parsing at the first
non-hyphenated arg. In some cases you can use the magic -- non-hyphenated arg. In some cases you can use the magic --
argument to delimit C from Lua arguments. argument to delimit C from Lua arguments.
For example, if you launch your redbean as follows:
redbean.com -v arg1 arg2
Then your `/.init.lua` file will have the `arg` array like:
arg[-1] = '/usr/bin/redbean.com'
arg[ 0] = '/zip/.init.lua'
arg[ 1] = 'arg1'
arg[ 2] = 'arg2'
If you launch redbean in interpreter mode (rather than web
server) mode, then an invocation like this:
./redbean.com -i script.lua arg1 arg2
Would have an `arg` array like this:
arg[-1] = './redbean.com'
arg[ 0] = 'script.lua'
arg[ 1] = 'arg1'
arg[ 2] = 'arg2'
HOOKS HOOKS
@ -522,7 +561,8 @@ FUNCTIONS
- useoutput: (bool=false) encodes the result directly to the - useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is output buffer and returns `nil` value. This option is
ignored if used outside of request handling code. ignored if used outside of request handling code.
- numformat: (string="%.14g") sets numeric format to be used. - numformat: sets numeric format to be used, which can be 'g',
'f', or 'a' [experimental api]
- maxdepth: (number=64) sets the max number of nested tables. - maxdepth: (number=64) sets the max number of nested tables.
EncodeLua(value[,options:table]) → json:str EncodeLua(value[,options:table]) → json:str
@ -531,7 +571,6 @@ FUNCTIONS
- useoutput: (bool=false) encodes the result directly to the - useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is output buffer and returns `nil` value. This option is
ignored if used outside of request handling code. ignored if used outside of request handling code.
- numformat: (string="%.14g") sets numeric format to be used.
- maxdepth: (number=64) sets the max number of nested tables. - maxdepth: (number=64) sets the max number of nested tables.
EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str
@ -1329,52 +1368,25 @@ MAXMIND MODULE
UNIX MODULE UNIX MODULE
This module exposes the low-level UNIX system call interface. The way This module exposes the low-level UNIX system call interface. This
these functions work is they'll only throw a Lua exception if there's module works on all supported platforms, including Windows NT.
some kind of error obtaining the required arguments. Once Lua reads
the arguments and the call is delegated to the system call interface,
all further errors won't be raised, but rather returned as errnos,
which should always be checked. For example, most syscalls follow:
errno = unix.foo(...) unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno
if errno then
Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)})
end
Any POSIX API that's defined as returning 0 on success or -1 on error
is wrapped here to return nil on success and an integer on error. To
see which errnos are possible for which system calls, please see the
comprehensive index at the bottom of this section.
In cases where POSIX defines an API as returning codes on success we
wrap the APIs as follows:
rc, errno = unix.bar(...)
if rc then
Log(kLogWarn, 'foo() succeeded: %d' % {rc})
else
Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)})
end
If the above code succeeds, `rc` will be non-nil and `errno` will be
`nil`. If the above code fails, `rc` will be nil and `errno` will be
an integer greater than zero.
UNIX FUNCTIONS
unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int]
Reads from file descriptor.
unix.write(fd:int, data[, offset:int]) → wrote:int[, errno:int]
Writes to file descriptor.
unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int]
Opens file. Opens file.
`flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`. Returns a file descriptor integer that needs to be closed, e.g.
fd = assert(open("/etc/passwd", unix.O_RDONLY))
print(unix.read(fd))
unix.close(fd)
`flags` should have one of:
- `O_RDONLY`: open for reading
- `O_WRONLY`: open for writing
- `O_RDWR`: open for reading and writing
The following values may also be OR'd into `flags`: The following values may also be OR'd into `flags`:
- `O_CREAT`: create file if it doesn't exist - `O_CREAT`: create file if it doesn't exist
@ -1412,10 +1424,18 @@ UNIX MODULE
already. If it does exist then `nil` is returned along with already. If it does exist then `nil` is returned along with
`errno` set to `EEXIST`. `errno` set to `EEXIST`.
unix.close(fd:int) → errno:int unix.close(fd:int) → ok:bool, unix.Errno
Closes file descriptor. Closes file descriptor.
unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, unix.Errno
Reads from file descriptor.
unix.write(fd:int, data[, offset:int]) → wrote:int, unix.Errno
Writes to file descriptor.
unix.exit([exitcode]) → ⊥ unix.exit([exitcode]) → ⊥
Invokes `_Exit(exitcode)` on the process. This will immediately Invokes `_Exit(exitcode)` on the process. This will immediately
@ -1443,21 +1463,27 @@ UNIX MODULE
command prompt inserts multiple environment variables with empty command prompt inserts multiple environment variables with empty
string as keys, for its internal bookkeeping. string as keys, for its internal bookkeeping.
unix.fork() → childpid|0:int[, errno:int] unix.fork() → childpid|0:int, unix.Errno
Creates a new process mitosis style. This returns twice. The Creates a new process mitosis style. This returns twice. The
parent process gets the nonzero pid. The child gets zero. parent process gets the nonzero pid. The child gets zero.
unix.commandv(prog:str) → path:str[, errno:int] unix.commandv(prog:str) → path:str, unix.Errno
Performs `$PATH` lookup of executable. We automatically suffix Performs `$PATH` lookup of executable.
`.com` and `.exe` for all platforms when path searching.
By default, the current directory is not on the path.
If `prog` is an absolute path, then it's returned as-is. If
`prog` contains slashes then it's not path searched either and
will be returned if it exists.
unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → errno:int unix = require "unix"
prog = assert(unix.commandv("ls"))
unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"})
unix.exit(127)
We automatically suffix `.com` and `.exe` for all platforms when
path searching. By default, the current directory is not on the
path. If `prog` is an absolute path, then it's returned as-is. If
`prog` contains slashes then it's not path searched either and will
be returned if it exists.
unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → false, unix.Errno
Exits current process, replacing it with a new instance of the Exits current process, replacing it with a new instance of the
specified program. `prog` needs to be an absolute path, see specified program. `prog` needs to be an absolute path, see
@ -1490,7 +1516,7 @@ UNIX MODULE
`EAGAIN` is returned if you've enforced a max number of `EAGAIN` is returned if you've enforced a max number of
processes using `setrlimit(RLIMIT_NPROC)`. processes using `setrlimit(RLIMIT_NPROC)`.
unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, errno:int unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno
Duplicates file descriptor. Duplicates file descriptor.
@ -1501,7 +1527,7 @@ UNIX MODULE
`flags` can have `O_CLOEXEC` which means the returned file `flags` can have `O_CLOEXEC` which means the returned file
descriptors will be automatically closed upon execve(). descriptors will be automatically closed upon execve().
unix.pipe([flags:int]) → reader:int, writer:int[, errno:int] unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int
Creates fifo which enables communication between processes. Creates fifo which enables communication between processes.
Returns two file descriptors: one for reading and one for Returns two file descriptors: one for reading and one for
@ -1537,7 +1563,7 @@ UNIX MODULE
end end
unix.wait([pid:int, options:int]) unix.wait([pid:int, options:int])
→ pid:int, wstatus:int, nil, errno:int → pid:int, unix.Errno, wstatus:int
Waits for subprocess to terminate. Waits for subprocess to terminate.
@ -1590,22 +1616,22 @@ UNIX MODULE
This function does not fail. This function does not fail.
unix.kill(pid, sig) → errno:int unix.kill(pid, sig) → ok:bool, unix.Errno
Returns process id of current process. Returns process id of current process.
unix.raise(sig) → rc:int[, errno:int] unix.raise(sig) → rc:int, unix.Errno
Triggers signal in current process. Triggers signal in current process.
This is pretty much the same as `kill(getpid(), sig)`. This is pretty much the same as `kill(getpid(), sig)`.
unix.access(path:str, how) → errno:int unix.access(path:str, how) → ok:bool, unix.Errno
Checks if effective user of current process has permission to access Checks if effective user of current process has permission to access
file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for
read, write, execute, and existence respectively. read, write, execute, and existence respectively.
unix.mkdir(path:str, mode) → errno:int unix.mkdir(path:str, mode) → ok:bool, unix.Errno
Makes directory. Makes directory.
@ -1628,7 +1654,7 @@ UNIX MODULE
Fails with `ENAMETOOLONG` if the path is too long. Fails with `ENAMETOOLONG` if the path is too long.
unix.makedirs(path:str, mode) → errno:int unix.makedirs(path:str, mode) → ok:bool, unix.Errno
Makes directories. Makes directories.
@ -1639,48 +1665,48 @@ UNIX MODULE
Unlike mkdir() this convenience wrapper will automatically create Unlike mkdir() this convenience wrapper will automatically create
parent parent directories as needed. parent parent directories as needed.
unix.chdir(path:str) → errno:int unix.chdir(path:str) → ok:bool, unix.Errno
Changes current directory to `path`. Changes current directory to `path`.
unix.unlink(path:str) → errno:int unix.unlink(path:str) → ok:bool, unix.Errno
Removes file at `path`. Removes file at `path`.
unix.rmdir(path:str) → errno:int unix.rmdir(path:str) → ok:bool, unix.Errno
Removes empty directory at `path`. Removes empty directory at `path`.
unix.rename(oldpath:str, newpath:str) → errno:int unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno
Renames file or directory. Renames file or directory.
unix.link(existingpath:str, newpath:str) → errno:int unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno
Creates hard link, so your underlying inode has two names. Creates hard link, so your underlying inode has two names.
unix.symlink(target:str, linkpath:str) → errno:int unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno
Creates soft link, or a symbolic link. Creates soft link, or a symbolic link.
unix.realpath(filename:str) → abspath:str[, errno:int] unix.realpath(filename:str) → abspath:str, unix.Errno
Returns absolute path of filename, with `.` and `..` components Returns absolute path of filename, with `.` and `..` components
removed, and symlinks will be resolved. removed, and symlinks will be resolved.
unix.chown(path:str, uid, gid) → errno:int unix.chown(path:str, uid, gid) → ok:bool, unix.Errno
Changes user and gorup on file. Changes user and gorup on file.
unix.chmod(path:str, mode) → errno:int unix.chmod(path:str, mode) → ok:bool, unix.Errno
Changes mode bits on file. Changes mode bits on file.
unix.getcwd() → path:str[, errno:int] unix.getcwd() → path:str, unix.Errno
Returns current working directory. Returns current working directory.
unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int] unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno
Manipulates file descriptor. Manipulates file descriptor.
@ -1691,27 +1717,27 @@ UNIX MODULE
POSIX advisory locks can be controlled by setting `cmd` to POSIX advisory locks can be controlled by setting `cmd` to
`F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`. `F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`.
unix.getsid(pid:int) → sid:int[, errno:int] unix.getsid(pid:int) → sid:int, unix.Errno
Gets session id. Gets session id.
unix.getpgrp() → pgid:int[, errno:int] unix.getpgrp() → pgid:int, unix.Errno
Gets process group id. Gets process group id.
unix.setpgrp() → pgid:int[, errno:int] unix.setpgrp() → pgid:int, unix.Errno
Sets process group id. This is the same as `setpgid(0,0)`. Sets process group id. This is the same as `setpgid(0,0)`.
unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int] unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno
Sets process group id the modern way. Sets process group id the modern way.
unix.getpgid(pid) → pgid:int[, errno:int] unix.getpgid(pid) → pgid:int, unix.Errno
Gets process group id the modern wayp. Gets process group id the modern wayp.
unix.setsid() → sid:int[, errno:int] unix.setsid() → sid:int, unix.Errno
Sets session id. Sets session id.
@ -1756,13 +1782,13 @@ UNIX MODULE
This function does not fail. This function does not fail.
unix.chroot(path:str) → errno:int unix.chroot(path:str) → ok:bool, unix.Errno
Changes root directory. Changes root directory.
Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Windows NT.
unix.setuid(uid:int) → errno:int unix.setuid(uid:int) → ok:bool, unix.Errno
Sets user id. Sets user id.
@ -1798,13 +1824,13 @@ UNIX MODULE
Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`. Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`.
unix.setgid(gid:int) → errno:int unix.setgid(gid:int) → ok:bool, unix.Errno
Sets group id. Sets group id.
Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`. Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`.
unix.setresuid(real:int, effective:int, saved:int) → errno:int unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno
Sets real, effective, and saved user ids. Sets real, effective, and saved user ids.
@ -1813,7 +1839,7 @@ UNIX MODULE
Returns `ENOSYS` on Windows NT. Returns `ENOSYS` on Windows NT.
Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1. Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1.
unix.setresgid(real:int, effective:int, saved:int) → errno:int unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno
Sets real, effective, and saved group ids. Sets real, effective, and saved group ids.
@ -1854,30 +1880,40 @@ UNIX MODULE
This function currently works on Linux, Windows, and NetBSD. On This function currently works on Linux, Windows, and NetBSD. On
WIN32 it uses the ReportEvent() facility. WIN32 it uses the ReportEvent() facility.
unix.clock_gettime([clock]) → seconds, nanos, errno:int unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int
Returns nanosecond precision timestamp from the system. Returns nanosecond precision timestamp from the system.
`clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or `clock` can be any one of of:
`CLOCK_MONOTONIC_RAW` since they work across platforms.
You may also try your luck with `CLOCK_REALTIME_COARSE`, - `CLOCK_REALTIME`: universally supported
`CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`, - `CLOCK_MONOTONIC`: universally supported
`CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`, - `CLOCK_MONOTONIC_RAW`: nearly universally supported
`CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`, - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd
- `CLOCK_THREAD_CPUTIME_ID`: linux and bsd
- `CLOCK_REALTIME_COARSE`: : linux and openbsd
- `CLOCK_MONOTONIC_COARSE`: linux
- `CLOCK_PROF`: linux and netbsd
- `CLOCK_BOOTTIME`: linux and openbsd
- `CLOCK_REALTIME_ALARM`: linux-only
- `CLOCK_BOOTTIME_ALARM`: linux-only
- `CLOCK_TAI`: ilnux-only
Returns `EINVAL` if clock isn't supported on platform.
unix.nanosleep(seconds:int[, nanos:int]) unix.nanosleep(seconds:int[, nanos:int])
→ remseconds:int, remnanos:int[, errno:int] → remseconds:int, unix.Errno, remnanos:int
Sleeps with nanosecond precision. Sleeps with nanosecond precision.
unix.sync() unix.sync()
unix.fsync(fd:int) → errno:int unix.fsync(fd:int) → ok:bool, unix.Errno
unix.fdatasync(fd:int) → errno:int unix.fdatasync(fd:int) → ok:bool, unix.Errno
These functions are used to make programs slower by asking the These functions are used to make programs slower by asking the
operating system to flush data to the physical medium. operating system to flush data to the physical medium.
unix.lseek(fd:int, offset:int, whence:int) → newpos:int[, errno:int] unix.lseek(fd:int, offset:int, whence:int) → newpos:int, unix.Errno
Seeks to file position. Seeks to file position.
@ -1889,21 +1925,21 @@ UNIX MODULE
Returns the new position relative to the start of the file. Returns the new position relative to the start of the file.
unix.truncate(path:str[, length:int]) → errno:int unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno
Reduces or extends underlying physical medium of file. Reduces or extends underlying physical medium of file.
If file was originally larger, content >length is lost. If file was originally larger, content >length is lost.
`length` defaults to zero. `length` defaults to zero.
unix.ftruncate(fd:int[, length:int]) → errno:int unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno
Reduces or extends underlying physical medium of open file. Reduces or extends underlying physical medium of open file.
If file was originally larger, content >length is lost. If file was originally larger, content >length is lost.
`length` defaults to zero. `length` defaults to zero.
unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int] unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int, unix.Errno
`family` defaults to `AF_INET` and can be: `family` defaults to `AF_INET` and can be:
@ -1929,14 +1965,14 @@ UNIX MODULE
`SOCK_CLOEXEC` may be bitwise or'd into `type`. `SOCK_CLOEXEC` may be bitwise or'd into `type`.
unix.socketpair([family:int[, type:int[, protocol:int]]]) unix.socketpair([family:int[, type:int[, protocol:int]]])
→ fd1:int, fd2:int[, errno:int] → fd1:int, unix.Errno, fd2:int
`SOCK_CLOEXEC` may be or'd into type `SOCK_CLOEXEC` may be or'd into type
`family` defaults to `AF_INET` `family` defaults to `AF_INET`
`type` defaults to `SOCK_STREAM` `type` defaults to `SOCK_STREAM`
`protocol` defaults to `IPPROTO_TCP` `protocol` defaults to `IPPROTO_TCP`
unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno
Binds socket. Binds socket.
@ -1970,19 +2006,21 @@ UNIX MODULE
Further note that calling `unix.bind(sock)` is equivalent to not Further note that calling `unix.bind(sock)` is equivalent to not
calling bind() at all, since the above behavior is the default. calling bind() at all, since the above behavior is the default.
unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, errno:int] unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno
Returns list of network adapter addresses. Returns list of network adapter addresses.
unix.getsockopt(fd:int, level:int, optname:int) → errno:int, ... unix.getsockopt(fd:int, level:int, optname:int) → ok:bool, unix.Errno, ...
unix.setsockopt(fd:int, level:int, optname:int, ...) → errno:int unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno
Tunes networking parameters. Tunes networking parameters.
`level` and `optname` may be one of the following. Please note the `level` and `optname` may be one of the following. The ellipses type
type signature for getsockopt() changes depending on these values: signature above changes depending on which options are used.
- `SOL_SOCKET` + `SO_TYPE`: bool
- `SOL_SOCKET` + `SO_DEBUG`: bool - `SOL_SOCKET` + `SO_DEBUG`: bool
- `SOL_SOCKET` + `SO_ACCEPTCONN`: bool
- `SOL_SOCKET` + `SO_BROADCAST`: bool - `SOL_SOCKET` + `SO_BROADCAST`: bool
- `SOL_SOCKET` + `SO_REUSEADDR`: bool - `SOL_SOCKET` + `SO_REUSEADDR`: bool
- `SOL_SOCKET` + `SO_REUSEPORT`: bool - `SOL_SOCKET` + `SO_REUSEPORT`: bool
@ -2016,15 +2054,8 @@ UNIX MODULE
Returns `ENOSYS` if setting isn't supported by the host o/s. Returns `ENOSYS` if setting isn't supported by the host o/s.
NOTE: The API for this function diverges from the the norm. `errno`
needs to come first in the results, because otherwise we
wouldn't know the arity of items to push before it. It's
because Cosmopolitan Libc polyfills the magic numbers above as
zero if the host operating system doesn't support them. If
there's no error, then `errno` will be set to nil.
unix.poll({fd:int=events:int, ...}[, timeoutms:int]) unix.poll({fd:int=events:int, ...}[, timeoutms:int])
→ {fd:int=revents:int, ...}[, errno:int] → {fd:int=revents:int, ...}, unix.Errno
Checks for events on a set of file descriptors. Checks for events on a set of file descriptors.
@ -2037,19 +2068,25 @@ UNIX MODULE
then that means block as long as it takes until there's an event or then that means block as long as it takes until there's an event or
an interrupt. If the timeout expires, an empty table is returned. an interrupt. If the timeout expires, an empty table is returned.
unix.gethostname() → host:str[, errno:int] unix.gethostname() → host:str, unix.Errno
Returns hostname of system. Returns hostname of system.
unix.listen(fd:int[, backlog]) → errno:int unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno
Begins listening for incoming connections on a socket. Begins listening for incoming connections on a socket.
unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int] unix.accept(serverfd:int[, flags:int])
→ clientfd:int, unix.Errno, ip:uint32, port:uint16
Accepts new client socket descriptor for a listening tcp socket. Accepts new client socket descriptor for a listening tcp socket.
unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int] `flags` can have any of:
- `SOCK_CLOEXEC`
- `SOCK_NONBLOCK`
unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno
Connects a TCP socket to a remote host. Connects a TCP socket to a remote host.
@ -2057,24 +2094,34 @@ UNIX MODULE
remembers the intended address so that send() or write() may be used remembers the intended address so that send() or write() may be used
rather than sendto(). rather than sendto().
unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int] unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16
Retrieves the local address of a socket. Retrieves the local address of a socket.
unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int] unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16
Retrieves the remote address of a socket. Retrieves the remote address of a socket.
unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int] unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno
`flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. `flags` can have:
- `MSG_WAITALL`
- `MSG_DONTROUTE`
- `MSG_PEEK`
- `MSG_OOB`
unix.recvfrom(fd:int[, bufsiz:int[, flags:int]]) unix.recvfrom(fd:int[, bufsiz:int[, flags:int]])
→ data:str, ip:uint32, port:uint16, errno:int → data:str, unix.Errno, ip:uint32, port:uint16
`flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc. `flags` can have:
unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int] - `MSG_WAITALL`
- `MSG_DONTROUTE`
- `MSG_PEEK`
- `MSG_OOB`
unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno
This is the same as `write` except it has a `flags` argument This is the same as `write` except it has a `flags` argument
that's intended for sockets. that's intended for sockets.
@ -2082,24 +2129,36 @@ UNIX MODULE
`flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`.
unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int]) unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int])
→ sent:int, errno:int → sent:int, unix.Errno
This is useful for sending messages over UDP sockets to specific This is useful for sending messages over UDP sockets to specific
addresses. addresses.
`flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`. `flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`.
unix.shutdown(fd:int, how:int) → errno:int unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno
Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or
`SHUT_RDWR`. `SHUT_RDWR`.
unix.sigprocmask(how[, mask]) → oldmask, errno:int unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno
`how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK` Manipulates bitset of signals blocked by process.
`how` can be one of:
- `SIG_BLOCK`: bitwise ors `mask` into set of blocked signals
- `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals
- `SIG_SETMASK`: replaces process signal mask with `mask`
`mask` is a word encoded bitset of signals. Valid signal numbers
start at 1 and vary between platforms. The most famous `SIGKILL`
can't be masked, but if it could, it's assigned the number `9`
across all platforms, so if you wanted to add it to a bitset, you
would say, `1 << 8` or in general terms `1 << (sig - 1)`.
unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]]) unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]])
→ oldhandler:func|int, flags:int, mask:int, errno:int → oldhandler:func|int, unix.Errno, flags:int, mask:int
`handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua `handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua
function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc. function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc.
@ -2115,7 +2174,7 @@ UNIX MODULE
It's a good idea to not do too much work in a signal handler. It's a good idea to not do too much work in a signal handler.
unix.sigsuspend([mask:int]) → errno:int unix.sigsuspend([mask]) → false, unix.Errno
Waits for signal to be delivered. Waits for signal to be delivered.
@ -2123,7 +2182,7 @@ UNIX MODULE
system call. `mask` specifies which signals should be blocked. system call. `mask` specifies which signals should be blocked.
unix.setitimer(which[, intsec, intmicros, valsec, valmicros]) unix.setitimer(which[, intsec, intmicros, valsec, valmicros])
→ intsec, intns, valsec, valns, errno:int → intsec, unix.Errno, intns, valsec, valns
Causes `SIGALRM` signals to be generated at some point(s) in the Causes `SIGALRM` signals to be generated at some point(s) in the
future. The `which` parameter should be `ITIMER_REAL`. future. The `which` parameter should be `ITIMER_REAL`.
@ -2145,17 +2204,17 @@ UNIX MODULE
unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND) unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND)
unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0) unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0)
unix.strerrno(errno:int) → str unix.strerrno(unix.Errno) → str
Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If
`errno` isn't known, this function returns nil. `errno` isn't known, this function returns nil.
unix.strerdoc(errno:int) → str unix.strerdoc(unix.Errno) → str
Turns `errno` code into a descriptive string. If `errno` isn't Turns `errno` code into a descriptive string. If `errno` isn't
known, this function returns nil. known, this function returns nil.
unix.strerror(errno:int) → str unix.strerror(unix.Errno) → str
Turns `errno` code into longest string describing the error. This Turns `errno` code into longest string describing the error. This
includes the output of both strerrno() and strerror() as well as the includes the output of both strerrno() and strerror() as well as the
@ -2167,7 +2226,7 @@ UNIX MODULE
Turns platform-specific `sig` code into its name, e.g. Turns platform-specific `sig` code into its name, e.g.
`strsignal(9)` always returns `"SIGKILL"`. `strsignal(9)` always returns `"SIGKILL"`.
unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno
Changes resource limit. Changes resource limit.
@ -2198,72 +2257,79 @@ UNIX MODULE
127. On most platforms these limits are enforced by the kernel and 127. On most platforms these limits are enforced by the kernel and
as such are inherited by subprocesses. as such are inherited by subprocesses.
unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int] unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int
Returns information about resource limit. Returns information about resource limit.
unix.stat(x) → UnixStat*[, errno:int] unix.stat(x) → unix.Stat, Errno*
Gets information about file or directory. `x` may be a file or Gets information about file or directory. `x` may be a file or
directory path string, or it may be a file descriptor int that directory path string, or it may be a file descriptor int that
was made by open(). was made by open().
unix.opendir(path:str) → UnixDir*[, errno:int] unix.opendir(path:str) → unix.Dir, Errno*
Opens directory for listing its contents. Opens directory for listing its contents.
unix.fdopendir(fd:int) → UnixDir*[, errno:int] unix.fdopendir(fd:int) → unix.Dir, Errno*
Opens directory for listing its contents, via an fd. Opens directory for listing its contents, via an fd.
`fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The `fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The
returned UnixDir* ownership takes ownership of the file descriptor returned unix.Dir ownership takes ownership of the file descriptor
and will close it automatically when garbage collected. and will close it automatically when garbage collected.
UNIX DIR OBJECT UNIX DIR OBJECT
UnixDir* objects are created by opendir() or fdopendir(). The unix.Dir objects are created by opendir() or fdopendir(). The
following methods are available: following methods are available:
UnixDir:close() → errno:int unix.Dir:close() → ok:bool, unix.Errno
may be called multiple times may be called multiple times
called by the garbage collector too called by the garbage collector too
UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int] unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int
Returns `nil` if there are no more entries. Or error, `nil` will Returns `nil` if there are no more entries. Or error, `nil` will
be returned and `errno` will be non-nil. be returned and `errno` will be non-nil.
`kind` can be `DT_UNKNOWN`, `DT_REG`, `DT_DIR`, `DT_BLK`, `kind` can be any of:
`DT_LNK`, `DT_CHR`, `DT_FIFO`, or `DT_SOCK`.
UnixDir:fd() → fd:int[, errno:int] - `DT_UNKNOWN`
- `DT_REG`
- `DT_DIR`
- `DT_BLK`
- `DT_LNK`
- `DT_CHR`
- `DT_FIFO`
- `DT_SOCK`
unix.Dir:fd() → fd:int, unix.Errno
Returns file descriptor of open directory object. Returns file descriptor of open directory object.
Returns `EOPNOTSUPP` if using a `/zip/...` path. Returns `EOPNOTSUPP` if using a `/zip/...` path.
Returns `EOPNOTSUPP` if using Windows NT. Returns `EOPNOTSUPP` if using Windows NT.
UnixDir:tell() → offset:int unix.Dir:tell() → offset:int
Returns current arbitrary offset into stream. Returns current arbitrary offset into stream.
UnixDir:rewind() unix.Dir:rewind()
Resets stream back to beginning. Resets stream back to beginning.
UNIX STAT OBJECT UNIX STAT OBJECT
UnixStat* objects are created by stat() or fstat(). The following unix.Stat objects are created by stat() or fstat(). The following
methods are available: methods are available:
UnixStat:size() → bytes:int unix.Stat:size() → bytes:int
Size of file in bytes. Size of file in bytes.
UnixStat:mode() → mode:int unix.Stat:mode() → mode:int
Contains file type and permissions. Contains file type and permissions.
@ -2280,49 +2346,49 @@ UNIX MODULE
- `(st:mode() & 0170000) == 0120000` means symbolic link - `(st:mode() & 0170000) == 0120000` means symbolic link
- `(st:mode() & 0170000) == 0140000` means socket - `(st:mode() & 0170000) == 0140000` means socket
UnixStat:atim() → secs:int, nanos:int unix.Stat:atim() → secs:int, nanos:int
Size of file in bytes. Size of file in bytes.
UnixStat:uid() → int unix.Stat:uid() → int
User ID of file owner. User ID of file owner.
UnixStat:gid() → int unix.Stat:gid() → int
Group ID of file owner. Group ID of file owner.
UnixStat:mtim() → secs:int, nanos:int unix.Stat:mtim() → secs:int, nanos:int
Last modified time. Last modified time.
UnixStat:birthtim() → secs:int, nanos:int unix.Stat:birthtim() → secs:int, nanos:int
Creation time. Note that on Linux this is the mimimum of Creation time. Note that on Linux this is the mimimum of
atom/mtim/ctim. atom/mtim/ctim.
UnixStat:ctim() → secs:int, nanos:int unix.Stat:ctim() → secs:int, nanos:int
Complicated time. Means time file status was last changed on Complicated time. Means time file status was last changed on
UNIX. Means creation time on Windows. UNIX. Means creation time on Windows.
UnixStat:blocks() → int unix.Stat:blocks() → int
Number of blocks used by storage medium. Number of blocks used by storage medium.
UnixStat:blksize() → int unix.Stat:blksize() → int
Block size is usually 4096 for file system files. Block size is usually 4096 for file system files.
UnixStat:dev() → int unix.Stat:dev() → int
ID of device containing file. ID of device containing file.
UnixStat:ino() → int unix.Stat:ino() → int
Inode number. Inode number.
UnixStat:rdev() → int unix.Stat:rdev() → int
Device ID (if special file) Device ID (if special file)

View file

@ -531,16 +531,16 @@ luaopen_argon2(lua_State *L)
largon2_push_argon2_variants_table(L); largon2_push_argon2_variants_table(L);
lua_setfield(L, -2, "variants"); lua_setfield(L, -2, "variants");
lua_pushstring(L, "3.0.1"); lua_pushliteral(L, "3.0.1");
lua_setfield(L, -2, "_VERSION"); lua_setfield(L, -2, "_VERSION");
lua_pushstring(L, "Thibault Charbonnier"); lua_pushliteral(L, "Thibault Charbonnier");
lua_setfield(L, -2, "_AUTHOR"); lua_setfield(L, -2, "_AUTHOR");
lua_pushstring(L, "MIT"); lua_pushliteral(L, "MIT");
lua_setfield(L, -2, "_LICENSE"); lua_setfield(L, -2, "_LICENSE");
lua_pushstring(L, "https://github.com/thibaultcha/lua-argon2"); lua_pushliteral(L, "https://github.com/thibaultcha/lua-argon2");
lua_setfield(L, -2, "_URL"); lua_setfield(L, -2, "_URL");
return 1; return 1;

View file

@ -18,10 +18,14 @@
*/ */
#include "dsp/scale/cdecimate2xuint8x8.h" #include "dsp/scale/cdecimate2xuint8x8.h"
#include "libc/bits/popcnt.h" #include "libc/bits/popcnt.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rusage.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/nexgen32e/bench.h"
#include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsf.h"
#include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/bsr.h"
#include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/crc32.h"
@ -31,6 +35,7 @@
#include "libc/runtime/gc.internal.h" #include "libc/runtime/gc.internal.h"
#include "libc/sock/sock.h" #include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/time/time.h" #include "libc/time/time.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "net/http/escape.h" #include "net/http/escape.h"
@ -48,6 +53,10 @@
#include "third_party/mbedtls/sha512.h" #include "third_party/mbedtls/sha512.h"
#include "tool/net/lfuncs.h" #include "tool/net/lfuncs.h"
static int Rdpid(void) {
return rdpid();
}
int LuaGetTime(lua_State *L) { int LuaGetTime(lua_State *L) {
lua_pushnumber(L, nowl()); lua_pushnumber(L, nowl());
return 1; return 1;
@ -64,12 +73,12 @@ int LuaRdtsc(lua_State *L) {
} }
int LuaGetCpuNode(lua_State *L) { int LuaGetCpuNode(lua_State *L) {
lua_pushinteger(L, TSC_AUX_NODE(rdpid())); lua_pushinteger(L, TSC_AUX_NODE(Rdpid()));
return 1; return 1;
} }
int LuaGetCpuCore(lua_State *L) { int LuaGetCpuCore(lua_State *L) {
lua_pushinteger(L, TSC_AUX_CORE(rdpid())); lua_pushinteger(L, TSC_AUX_CORE(Rdpid()));
return 1; return 1;
} }
@ -560,3 +569,44 @@ void LuaPushUrlView(lua_State *L, struct UrlView *v) {
lua_pushnil(L); lua_pushnil(L);
} }
} }
static int64_t GetInterrupts(void) {
struct rusage ru;
if (!getrusage(RUSAGE_SELF, &ru)) {
return ru.ru_nivcsw;
} else {
return 0;
}
}
int LuaBenchmark(lua_State *L) {
double avgticks;
uint64_t t1, t2;
int64_t interrupts;
int core, iter, count, tries, attempts, maxattempts;
luaL_checktype(L, 1, LUA_TFUNCTION);
count = luaL_optinteger(L, 2, 100);
maxattempts = luaL_optinteger(L, 3, 10);
for (attempts = 0;;) {
sched_yield();
core = TSC_AUX_CORE(Rdpid());
interrupts = GetInterrupts();
for (avgticks = iter = 1; iter < count; ++iter) {
t1 = __startbench();
lua_pushvalue(L, 1);
lua_call(L, 0, 0);
t2 = __endbench();
avgticks += 1. / iter * ((int)(t2 - t1) - avgticks);
}
++attempts;
if (TSC_AUX_CORE(Rdpid()) == core && GetInterrupts() == interrupts) {
break;
} else if (attempts >= maxattempts) {
return luaL_error(L, "system is under too much load to run benchmark");
}
}
lua_pushnumber(L, ConvertTicksToNanos(avgticks));
lua_pushinteger(L, avgticks);
lua_pushinteger(L, attempts);
return 3;
}

View file

@ -11,6 +11,7 @@ int LuaUnix(lua_State *);
int luaopen_argon2(lua_State *); int luaopen_argon2(lua_State *);
int luaopen_lsqlite3(lua_State *); int luaopen_lsqlite3(lua_State *);
int LuaBenchmark(lua_State *);
int LuaBsf(lua_State *); int LuaBsf(lua_State *);
int LuaBsr(lua_State *); int LuaBsr(lua_State *);
int LuaCategorizeIp(lua_State *); int LuaCategorizeIp(lua_State *);

View file

@ -1936,7 +1936,7 @@ static const luaL_Reg sqlitelib[] = {
static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) {
luaL_newmetatable(L, name); luaL_newmetatable(L, name);
lua_pushstring(L, "__index"); lua_pushliteral(L, "__index");
lua_pushvalue(L, -2); /* push metatable */ lua_pushvalue(L, -2); /* push metatable */
lua_rawset(L, -3); /* metatable.__index = metatable */ lua_rawset(L, -3); /* metatable.__index = metatable */

File diff suppressed because it is too large Load diff

View file

@ -191,10 +191,10 @@ STATIC_YOINK("zip_uri_support");
#define HeaderEqualCase(H, S) \ #define HeaderEqualCase(H, S) \
SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H))
// letters not used: EIJNOQWXYinoqwxy // letters not used: EIJNOQWXYnoqwxy
// digits not used: 0123456789 // digits not used: 0123456789
// puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~ // puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~
#define GETOPTS "BSVZabdfghjkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:" #define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:"
static const uint8_t kGzipHeader[] = { static const uint8_t kGzipHeader[] = {
0x1F, // MAGNUM 0x1F, // MAGNUM
@ -379,6 +379,7 @@ static bool checkedmethod;
static bool sslinitialized; static bool sslinitialized;
static bool sslfetchverify; static bool sslfetchverify;
static bool hascontenttype; static bool hascontenttype;
static bool interpretermode;
static bool sslclientverify; static bool sslclientverify;
static bool connectionclose; static bool connectionclose;
static bool hasonworkerstop; static bool hasonworkerstop;
@ -1185,10 +1186,10 @@ static void ReportWorkerExit(int pid, int ws) {
static void ReportWorkerResources(int pid, struct rusage *ru) { static void ReportWorkerResources(int pid, struct rusage *ru) {
char *s, *b = 0; char *s, *b = 0;
if (logrusage || LOGGABLE(kLogDebug)) { if (logrusage || LOGGABLE(kLogDebug)) {
AppendResourceReport(&b, ru, "\r\n"); AppendResourceReport(&b, ru, "\n");
if (b) { if (b) {
if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) { if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) {
LOGF(kLogDebug, "(stat) resource report for pid %d\r\n%s", pid, s); LOGF(kLogDebug, "(stat) resource report for pid %d\n%s", pid, s);
free(s); free(s);
} }
free(b); free(b);
@ -4116,7 +4117,7 @@ static int LuaLog(lua_State *L) {
} }
static int LuaEncodeSmth(lua_State *L, static int LuaEncodeSmth(lua_State *L,
int Encoder(lua_State *, char **, int, char *)) { int Encoder(lua_State *, char **, int, char *, int)) {
int useoutput = false; int useoutput = false;
int maxdepth = 64; int maxdepth = 64;
char *numformat = "%.14g"; char *numformat = "%.14g";
@ -4133,7 +4134,7 @@ static int LuaEncodeSmth(lua_State *L,
numformat = luaL_optstring(L, -1, numformat); numformat = luaL_optstring(L, -1, numformat);
} }
lua_settop(L, 1); // keep the passed argument on top lua_settop(L, 1); // keep the passed argument on top
Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat); Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1);
if (useoutput) { if (useoutput) {
lua_pushnil(L); lua_pushnil(L);
} else { } else {
@ -4920,6 +4921,7 @@ static const char *const kDontAutoComplete[] = {
// </SORTED> // </SORTED>
static const luaL_Reg kLuaFuncs[] = { static const luaL_Reg kLuaFuncs[] = {
{"Benchmark", LuaBenchmark}, //
{"Bsf", LuaBsf}, // {"Bsf", LuaBsf}, //
{"Bsr", LuaBsr}, // {"Bsr", LuaBsr}, //
{"CategorizeIp", LuaCategorizeIp}, // {"CategorizeIp", LuaCategorizeIp}, //
@ -5085,13 +5087,21 @@ static const luaL_Reg kLuaLibs[] = {
}; };
static void LuaSetArgv(lua_State *L) { static void LuaSetArgv(lua_State *L) {
size_t i; int i, j = -1;
lua_newtable(L); lua_newtable(L);
lua_pushstring(L, __argv[0]);
lua_seti(L, -2, j++);
if (!interpretermode) {
lua_pushstring(L, "/zip/.init.lua");
lua_seti(L, -2, j++);
}
for (i = optind; i < __argc; ++i) { for (i = optind; i < __argc; ++i) {
lua_pushstring(L, __argv[i]); lua_pushstring(L, __argv[i]);
lua_seti(L, -2, i - optind + 1); lua_seti(L, -2, j++);
} }
lua_setglobal(L, "argv"); lua_pushvalue(L, -1);
lua_setglobal(L, "argv"); // deprecated
lua_setglobal(L, "arg");
} }
static void LuaSetConstant(lua_State *L, const char *s, long x) { static void LuaSetConstant(lua_State *L, const char *s, long x) {
@ -5133,10 +5143,111 @@ static void LuaStart(void) {
#endif #endif
} }
static bool ShouldAutocomplete(const char *s) {
int c, m, l, r;
l = 0;
r = ARRAYLEN(kDontAutoComplete) - 1;
while (l <= r) {
m = (l + r) >> 1;
c = strcmp(kDontAutoComplete[m], s);
if (c < 0) {
l = m + 1;
} else if (c > 0) {
r = m - 1;
} else {
return false;
}
}
return true;
}
static void HandleCompletions(const char *p, linenoiseCompletions *c) {
size_t i, j;
for (j = i = 0; i < c->len; ++i) {
if (ShouldAutocomplete(c->cvec[i])) {
c->cvec[j++] = c->cvec[i];
} else {
free(c->cvec[i]);
}
}
c->len = j;
}
static void LuaPrint(lua_State *L) {
int i, n;
char *b = 0;
const char *s;
n = lua_gettop(L);
for (i = 1; i <= n; i++) {
if (i > 1) appendw(&b, '\t');
LuaEncodeLuaData(L, &b, 64, "g", i);
}
appendw(&b, '\n');
WRITE(1, b, appendz(b).i);
free(b);
}
static void LuaInterpreter(lua_State *L) {
int i, n, sig, status;
const char *script;
if (optind < __argc) {
script = __argv[optind];
if (!strcmp(script, "-")) script = 0;
if ((status = luaL_loadfile(L, script)) == LUA_OK) {
lua_getglobal(L, "arg");
n = luaL_len(L, -1);
luaL_checkstack(L, n + 3, "too many script args");
for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i);
lua_remove(L, -i); // remove arg table from stack
status = lua_runchunk(L, n, LUA_MULTRET);
}
lua_report(L, status);
} else {
lua_repl_blocking = true;
lua_repl_completions_callback = HandleCompletions;
lua_initrepl(GL, "redbean");
if (lua_repl_isterminal) {
linenoiseEnableRawMode(0);
}
for (;;) {
status = lua_loadline(L);
write(1, "\n", 1);
if (status == -1) break; // eof
if (status == -2) {
if (errno == EINTR) {
if ((sig = linenoiseGetInterrupt())) {
raise(sig);
}
}
fprintf(stderr, "i/o error: %m\n");
exit(1);
}
if (status == LUA_OK) {
status = lua_runchunk(GL, 0, LUA_MULTRET);
}
if (status == LUA_OK) {
LuaPrint(GL);
} else {
lua_report(GL, status);
}
}
linenoiseDisableRawMode();
lua_freerepl();
lua_settop(GL, 0); // clear stack
if ((sig = linenoiseGetInterrupt())) {
raise(sig);
}
}
}
static void LuaInit(void) { static void LuaInit(void) {
#ifndef STATIC #ifndef STATIC
lua_State *L = GL; lua_State *L = GL;
LuaSetArgv(L); LuaSetArgv(L);
if (interpretermode) {
LuaInterpreter(L);
exit(0);
}
if (LuaRunAsset("/.init.lua", true)) { if (LuaRunAsset("/.init.lua", true)) {
hasonhttprequest = IsHookDefined("OnHttpRequest"); hasonhttprequest = IsHookDefined("OnHttpRequest");
hasonclientconnection = IsHookDefined("OnClientConnection"); hasonclientconnection = IsHookDefined("OnClientConnection");
@ -6369,34 +6480,6 @@ static void RestoreApe(void) {
} }
} }
static bool ShouldAutocomplete(const char *s) {
int c, m, l, r;
l = 0;
r = ARRAYLEN(kDontAutoComplete) - 1;
while (l <= r) {
m = (l + r) >> 1;
c = strcmp(kDontAutoComplete[m], s);
if (c < 0) {
l = m + 1;
} else if (c > 0) {
r = m - 1;
} else {
return false;
}
}
return true;
}
static void HandleCompletions(const char *p, linenoiseCompletions *c) {
size_t i, j;
for (j = i = 0; i < c->len; ++i) {
if (ShouldAutocomplete(c->cvec[i])) {
c->cvec[j++] = c->cvec[i];
}
}
c->len = j;
}
static int HandleReadline(void) { static int HandleReadline(void) {
int status; int status;
for (;;) { for (;;) {
@ -6405,7 +6488,7 @@ static int HandleReadline(void) {
if (status == -1) { if (status == -1) {
OnTerm(SIGHUP); // eof OnTerm(SIGHUP); // eof
INFOF("got repl eof"); INFOF("got repl eof");
write(1, "\r\n", 2); write(1, "\n", 1);
return -1; return -1;
} else if (errno == EINTR) { } else if (errno == EINTR) {
errno = 0; errno = 0;
@ -6419,14 +6502,14 @@ static int HandleReadline(void) {
return -1; return -1;
} }
} }
write(1, "\r\n", 2); write(1, "\n", 1);
linenoiseDisableRawMode(); linenoiseDisableRawMode();
LUA_REPL_LOCK; LUA_REPL_LOCK;
if (status == LUA_OK) { if (status == LUA_OK) {
status = lua_runchunk(GL, 0, LUA_MULTRET); status = lua_runchunk(GL, 0, LUA_MULTRET);
} }
if (status == LUA_OK) { if (status == LUA_OK) {
lua_l_print(GL); LuaPrint(GL);
} else { } else {
lua_report(GL, status); lua_report(GL, status);
} }
@ -6781,6 +6864,7 @@ static void GetOpts(int argc, char *argv[]) {
#ifndef STATIC #ifndef STATIC
CASE('e', LuaEvalCode(optarg)); CASE('e', LuaEvalCode(optarg));
CASE('F', LuaEvalFile(optarg)); CASE('F', LuaEvalFile(optarg));
CASE('i', interpretermode = true);
CASE('E', leakcrashreports = true); CASE('E', leakcrashreports = true);
CASE('A', storeasset = true; StorePath(optarg)); CASE('A', storeasset = true; StorePath(optarg));
#endif #endif