mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Write more redbean unit tests
- Fix DescribeSigset() - Introduce new unix.rmrf() API - Fix redbean sigaction() doc example code - Fix unix.sigaction() w/ more than two args - Improve redbean re module API (non-breaking) - Enhance Lua with Python string multiplication - Make third parameter of unix.socket() default to 0
This commit is contained in:
parent
c5b9902ac9
commit
1c83670229
20 changed files with 738 additions and 204 deletions
|
@ -7,6 +7,7 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "dsp/core/core.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/log/check.h"
|
||||
|
@ -17,6 +18,10 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
#define CSI "s"
|
||||
#define SGR1 "?80"
|
||||
#define SGR2 "?81"
|
||||
|
||||
struct Ring {
|
||||
int i; // read index
|
||||
int j; // write index
|
||||
|
@ -30,7 +35,7 @@ struct Speaker {
|
|||
struct Ring buf; // audio playback buffer
|
||||
};
|
||||
|
||||
const int maxar = 32;
|
||||
const int maxar = 31;
|
||||
const int ptime = 20;
|
||||
|
||||
struct Speaker s;
|
||||
|
@ -64,14 +69,14 @@ void OnAlrm(int sig) {
|
|||
int count;
|
||||
int samps = s.rate / (1000 / ptime);
|
||||
for (i = 0; i < samps; i += count) {
|
||||
printf("\e[");
|
||||
printf("\e[" SGR2);
|
||||
count = MIN(samps - i, maxar);
|
||||
for (j = 0; j < count; ++j) {
|
||||
if (j) printf(";");
|
||||
printf("%d", s.buf.p[s.buf.i++] & 0xffff);
|
||||
printf(";%d", s.buf.p[s.buf.i++] & 0xffff);
|
||||
/* printf(";%d", mulaw(s.buf.p[s.buf.i++])); */
|
||||
if (s.buf.i == s.buf.n) break;
|
||||
}
|
||||
printf("p");
|
||||
printf(CSI);
|
||||
if (s.buf.i == s.buf.n) break;
|
||||
}
|
||||
fflush(stdout);
|
||||
|
@ -87,7 +92,7 @@ int main(int argc, char* argv[]) {
|
|||
s.channels = 1;
|
||||
LoadAudioFile(&s, "/home/jart/Music/numbers.s16");
|
||||
|
||||
printf("\e[%d;%dy", s.rate, s.channels);
|
||||
printf("\e[" SGR1 "%d;%d;0" CSI, s.rate, s.channels);
|
||||
fflush(stdout);
|
||||
|
||||
struct sigaction sa = {.sa_handler = OnAlrm};
|
||||
|
|
|
@ -57,9 +57,9 @@ const char *DescribeRlimitName(char[20], int);
|
|||
const char *DescribeSchedParam(char[32], const struct sched_param *);
|
||||
const char *DescribeSchedPolicy(char[48], int);
|
||||
const char *DescribeSeccompOperation(int);
|
||||
const char *DescribeSigaction(char[128], int, const struct sigaction *);
|
||||
const char *DescribeSigaction(char[256], int, const struct sigaction *);
|
||||
const char *DescribeSigaltstk(char[128], int, const struct sigaltstack *);
|
||||
const char *DescribeSigset(char[64], int, const sigset_t *);
|
||||
const char *DescribeSigset(char[128], int, const sigset_t *);
|
||||
const char *DescribeSockLevel(char[12], int);
|
||||
const char *DescribeSockOptname(char[32], int, int);
|
||||
const char *DescribeSockaddr(char[128], const struct sockaddr *, size_t);
|
||||
|
@ -102,9 +102,9 @@ void DescribeIovNt(const struct NtIovec *, uint32_t, ssize_t);
|
|||
#define DescribeRlimitName(rl) DescribeRlimitName(alloca(20), rl)
|
||||
#define DescribeSchedParam(x) DescribeSchedParam(alloca(32), x)
|
||||
#define DescribeSchedPolicy(x) DescribeSchedPolicy(alloca(48), x)
|
||||
#define DescribeSigaction(rc, sa) DescribeSigaction(alloca(128), rc, sa)
|
||||
#define DescribeSigaction(rc, sa) DescribeSigaction(alloca(256), rc, sa)
|
||||
#define DescribeSigaltstk(rc, ss) DescribeSigaltstk(alloca(128), rc, ss)
|
||||
#define DescribeSigset(rc, ss) DescribeSigset(alloca(64), rc, ss)
|
||||
#define DescribeSigset(rc, ss) DescribeSigset(alloca(128), rc, ss)
|
||||
#define DescribeSockLevel(x) DescribeSockLevel(alloca(12), x)
|
||||
#define DescribeSockOptname(x, y) DescribeSockOptname(alloca(32), x, y)
|
||||
#define DescribeSockaddr(sa, sz) DescribeSockaddr(alloca(128), sa, sz)
|
||||
|
|
|
@ -21,15 +21,15 @@
|
|||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
||||
const char *(DescribeSigaction)(char buf[128], int rc,
|
||||
const char *(DescribeSigaction)(char buf[256], int rc,
|
||||
const struct sigaction *sa) {
|
||||
if (rc == -1) return "n/a";
|
||||
if (!sa) return "NULL";
|
||||
if ((!IsAsan() && kisdangerous(sa)) ||
|
||||
(IsAsan() && !__asan_is_valid(sa, sizeof(*sa)))) {
|
||||
ksnprintf(buf, 128, "%p", sa);
|
||||
ksnprintf(buf, 256, "%p", sa);
|
||||
} else {
|
||||
ksnprintf(buf, 128, "{.sa_handler=%p, .sa_flags=%#lx, .sa_mask=%s}",
|
||||
ksnprintf(buf, 256, "{.sa_handler=%t, .sa_flags=%#lx, .sa_mask=%s}",
|
||||
sa->sa_handler, sa->sa_flags, DescribeSigset(rc, &sa->sa_mask));
|
||||
}
|
||||
return buf;
|
||||
|
|
|
@ -23,40 +23,41 @@
|
|||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
const char *(DescribeSigset)(char buf[64], int rc, const sigset_t *ss) {
|
||||
#define N 128
|
||||
|
||||
const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) {
|
||||
bool gotsome;
|
||||
int i, n, sig;
|
||||
int i, sig;
|
||||
sigset_t sigset;
|
||||
|
||||
if (rc == -1) return "n/a";
|
||||
if (!ss) return "NULL";
|
||||
if ((!IsAsan() && kisdangerous(ss)) ||
|
||||
(IsAsan() && !__asan_is_valid(ss, sizeof(*ss)))) {
|
||||
ksnprintf(buf, 64, "%p", ss);
|
||||
ksnprintf(buf, N, "%p", ss);
|
||||
return buf;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
n = 64;
|
||||
sigset = *ss;
|
||||
gotsome = false;
|
||||
if (popcnt(sigset.__bits[0]) + popcnt(sigset.__bits[1]) > 64) {
|
||||
i += ksnprintf(buf + i, n - i, "~");
|
||||
if (popcnt(sigset.__bits[0] & 0xffffffff) > 16) {
|
||||
i += ksnprintf(buf + i, N - i, "~");
|
||||
sigset.__bits[0] = ~sigset.__bits[0];
|
||||
sigset.__bits[1] = ~sigset.__bits[1];
|
||||
}
|
||||
i += ksnprintf(buf + i, n - i, "{");
|
||||
for (sig = 1; sig < 128; ++sig) {
|
||||
i += ksnprintf(buf + i, N - i, "{");
|
||||
for (sig = 1; sig < 32; ++sig) {
|
||||
if (sigismember(&sigset, sig)) {
|
||||
if (gotsome) {
|
||||
sig += ksnprintf(buf + sig, n - sig, ", ");
|
||||
i += ksnprintf(buf + i, N - i, ",");
|
||||
} else {
|
||||
gotsome = true;
|
||||
}
|
||||
sig += ksnprintf(buf + sig, n - sig, "%s", strsignal(sig));
|
||||
i += ksnprintf(buf + i, N - i, "%s", strsignal(sig) + 3);
|
||||
}
|
||||
}
|
||||
i += ksnprintf(buf + i, n - i, "}");
|
||||
i += ksnprintf(buf + i, N - i, "}");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ relegated void RestoreDefaultCrashSignalHandlers(void) {
|
|||
int e;
|
||||
size_t i;
|
||||
sigset_t ss;
|
||||
--__strace;
|
||||
sigemptyset(&ss);
|
||||
sigprocmask(SIG_SETMASK, &ss, NULL);
|
||||
for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) {
|
||||
|
@ -74,10 +75,10 @@ relegated void RestoreDefaultCrashSignalHandlers(void) {
|
|||
errno = e;
|
||||
}
|
||||
}
|
||||
++__strace;
|
||||
}
|
||||
|
||||
static void FreeSigAltStack(void *p) {
|
||||
InstallCrashHandlers(0);
|
||||
sigaltstack(&g_oldsigaltstack, 0);
|
||||
munmap(p, GetStackSize());
|
||||
}
|
||||
|
|
|
@ -266,7 +266,8 @@ DIR *opendir(const char *name) {
|
|||
}
|
||||
} else if (!IsWindows()) {
|
||||
res = 0;
|
||||
if ((fd = open(name, O_RDONLY | O_DIRECTORY | O_CLOEXEC)) != -1) {
|
||||
if ((fd = open(name, O_RDONLY | O_NOCTTY | O_DIRECTORY | O_CLOEXEC)) !=
|
||||
-1) {
|
||||
if (!(res = fdopendir(fd))) {
|
||||
close(fd);
|
||||
}
|
||||
|
|
38
test/libc/intrin/describesigset_test.c
Normal file
38
test/libc/intrin/describesigset_test.c
Normal 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/calls/struct/sigset.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
TEST(DescribeSigset, present) {
|
||||
sigset_t ss;
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGINT);
|
||||
sigaddset(&ss, SIGUSR1);
|
||||
EXPECT_STREQ("{INT,USR1}", DescribeSigset(0, &ss));
|
||||
}
|
||||
|
||||
TEST(DescribeSigset, absent) {
|
||||
sigset_t ss;
|
||||
sigfillset(&ss);
|
||||
sigdelset(&ss, SIGINT);
|
||||
sigdelset(&ss, SIGUSR1);
|
||||
EXPECT_STREQ("~{INT,USR1}", DescribeSigset(0, &ss));
|
||||
}
|
28
test/tool/net/argon2_test.lua
Normal file
28
test/tool/net/argon2_test.lua
Normal file
|
@ -0,0 +1,28 @@
|
|||
-- 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.
|
||||
|
||||
assert(assert(argon2.hash_encoded("password", "somesalt", {
|
||||
variant = argon2.variants.argon2_i,
|
||||
m_cost = 65536,
|
||||
hash_len = 24,
|
||||
parallelism = 4,
|
||||
t_cost = 2,
|
||||
})) ==
|
||||
"$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG")
|
||||
|
||||
assert(argon2.verify(
|
||||
"$argon2i$v=19$m=65536,t=2," ..
|
||||
"p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG",
|
||||
"password"))
|
|
@ -17,6 +17,9 @@ x = Rdtsc()
|
|||
y = Rdtsc()
|
||||
assert(y > x)
|
||||
|
||||
assert(Rdrand() ~= Rdrand())
|
||||
assert(Rdseed() ~= Rdseed())
|
||||
|
||||
assert(Bsr(1) == 0)
|
||||
assert(Bsr(2) == 1)
|
||||
assert(Bsr(3) == 1)
|
||||
|
@ -29,22 +32,54 @@ assert(Bsf(3) == 0)
|
|||
assert(Bsf(4) == 2)
|
||||
assert(Bsf(0x80000001) == 0)
|
||||
|
||||
assert(Popcnt(0) == 0)
|
||||
assert(Popcnt(1) == 1)
|
||||
assert(Popcnt(2) == 1)
|
||||
assert(Popcnt(3) == 2)
|
||||
assert(Popcnt(0b0111101001101001) == 9)
|
||||
|
||||
assert(Lemur64() == 0x1940efe9d47ae889)
|
||||
assert(Lemur64() == 0xd4b3103f567f9974)
|
||||
|
||||
assert(hex(0x1940efe9d47ae889) == "0x1940efe9d47ae889")
|
||||
assert(oct(0x1940efe9d47ae889) == "0145007376472436564211")
|
||||
assert(bin(0x1940efe9d47ae889) == "0b0001100101000000111011111110100111010100011110101110100010001001")
|
||||
|
||||
assert(EscapeHtml("?hello&there<>") == "?hello&there<>")
|
||||
assert(EscapeParam("?hello&there<>") == "%3Fhello%26there%3C%3E")
|
||||
|
||||
assert(DecodeLatin1("hello\xff\xc0") == "helloÿÀ")
|
||||
assert(EncodeLatin1("helloÿÀ") == "hello\xff\xc0")
|
||||
|
||||
assert(EncodeLua(nil) == "nil")
|
||||
assert(EncodeLua(0) == "0")
|
||||
assert(EncodeLua(3.14) == "3.14")
|
||||
assert(EncodeLua({1, 2}) == "{1, 2}")
|
||||
x = {1, 2}
|
||||
x[3] = x
|
||||
assert(string.match(EncodeLua(x), "{1, 2, \"cyclic@0x%x+\"}"))
|
||||
|
||||
-- TODO(jart): EncodeLua() should sort tables
|
||||
-- x = {}
|
||||
-- x.c = 'c'
|
||||
-- x.a = 'a'
|
||||
-- x.b = 'b'
|
||||
-- assert(EncodeLua(x) == '{a="a", b="b", c="c"}')
|
||||
|
||||
assert(EncodeJson(nil) == "null")
|
||||
assert(EncodeJson(0) == "0")
|
||||
assert(EncodeJson(3.14) == "3.14")
|
||||
assert(EncodeJson({1, 2}) == "[1,2]")
|
||||
|
||||
assert(hex(0x1940efe9d47ae889) == "0x1940efe9d47ae889")
|
||||
assert(oct(0x1940efe9d47ae889) == "0145007376472436564211")
|
||||
assert(bin(0x1940efe9d47ae889) == "0b0001100101000000111011111110100111010100011110101110100010001001")
|
||||
url = ParseUrl("https://jart:pass@redbean.dev/2.0.html?x&y=z#frag")
|
||||
assert(url.scheme == "https")
|
||||
assert(url.user == "jart")
|
||||
assert(url.pass == "pass")
|
||||
assert(url.host == "redbean.dev")
|
||||
assert(not url.port)
|
||||
assert(url.path == "/2.0.html")
|
||||
assert(EncodeLua(url.params) == '{{"x"}, {"y", "z"}}')
|
||||
assert(url.fragment == "frag")
|
||||
|
||||
assert(DecodeBase64("abcdefgABCDE") == "\x69\xb7\x1d\x79\xf8\x00\x04\x20\xc4")
|
||||
assert(EncodeBase64("\x69\xb7\x1d\x79\xf8\x00\x04\x20\xc4") == "abcdefgABCDE")
|
||||
|
@ -82,6 +117,7 @@ assert(IndentLines("hi\nthere\n") == " hi\n there\n")
|
|||
assert(IndentLines("hi\nthere\n", 2) == " hi\n there\n")
|
||||
|
||||
assert(ParseHttpDateTime("Fri, 08 Jul 2022 16:17:43 GMT") == 1657297063)
|
||||
assert(FormatHttpDateTime(1657297063) == "Fri, 08 Jul 2022 16:17:43 GMT")
|
||||
|
||||
assert(VisualizeControlCodes("hello\x00") == "hello␀")
|
||||
|
||||
|
@ -97,8 +133,8 @@ assert(Sha256("hello") == "\x2c\xf2\x4d\xba\x5f\xb0\xa3\x0e\x26\xe8\x3b\x2a\xc5\
|
|||
assert(Sha384("hello") == "\x59\xe1\x74\x87\x77\x44\x8c\x69\xde\x6b\x80\x0d\x7a\x33\xbb\xfb\x9f\xf1\xb4\x63\xe4\x43\x54\xc3\x55\x3b\xcd\xb9\xc6\x66\xfa\x90\x12\x5a\x3c\x79\xf9\x03\x97\xbd\xf5\xf6\xa1\x3d\xe8\x28\x68\x4f")
|
||||
assert(Sha512("hello") == "\x9b\x71\xd2\x24\xbd\x62\xf3\x78\x5d\x96\xd4\x6a\xd3\xea\x3d\x73\x31\x9b\xfb\xc2\x89\x0c\xaa\xda\xe2\xdf\xf7\x25\x19\x67\x3c\xa7\x23\x23\xc3\xd9\x9b\xa5\xc1\x1d\x7c\x7a\xcc\x6e\x14\xb8\xc5\xda\x0c\x46\x63\x47\x5c\x2e\x5c\x3a\xde\xf4\x6f\x73\xbc\xde\xc0\x43")
|
||||
|
||||
assert(Deflate("hello") == "\xcbH\xcd\xc9\xc9\x07\x00")
|
||||
assert(Inflate("\xcbH\xcd\xc9\xc9\x07\x00", 5) == "hello")
|
||||
assert(assert(Deflate("hello")) == "\xcbH\xcd\xc9\xc9\x07\x00")
|
||||
assert(assert(Inflate("\xcbH\xcd\xc9\xc9\x07\x00", 5)) == "hello")
|
||||
|
||||
-- deprecated compression api we wish to forget as quickly as possible
|
||||
assert(Uncompress(Compress("hello")) == "hello")
|
||||
|
|
40
test/tool/net/lre_test.lua
Normal file
40
test/tool/net/lre_test.lua
Normal file
|
@ -0,0 +1,40 @@
|
|||
-- 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.
|
||||
|
||||
m,a,b,c,d = assert(re.search([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]], "127.0.0.1"))
|
||||
assert(m == "127.0.0.1")
|
||||
assert(a == "127")
|
||||
assert(b == "0")
|
||||
assert(c == "0")
|
||||
assert(d == "1")
|
||||
|
||||
p = assert(re.compile[[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]])
|
||||
m,a,b,c,d = assert(p:search("127.0.0.1"))
|
||||
assert(m == "127.0.0.1")
|
||||
assert(a == "127")
|
||||
assert(b == "0")
|
||||
assert(c == "0")
|
||||
assert(d == "1")
|
||||
|
||||
m,a,b,c,d = assert(re.search([[\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)]], "127.0.0.1", re.BASIC))
|
||||
assert(m == "127.0.0.1")
|
||||
assert(a == "127")
|
||||
assert(b == "0")
|
||||
assert(c == "0")
|
||||
assert(d == "1")
|
||||
|
||||
p,e = re.compile("[{")
|
||||
assert(e:errno() == re.EBRACK)
|
||||
assert(e:doc() == "Missing ']'")
|
21
test/tool/net/lua_test.lua
Normal file
21
test/tool/net/lua_test.lua
Normal file
|
@ -0,0 +1,21 @@
|
|||
-- 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.
|
||||
|
||||
-- test redbean lua language extensions
|
||||
assert(0b100 == 4)
|
||||
assert(0200 == 128)
|
||||
assert("\e" == "\x1b")
|
||||
assert("hi" * 3 == "hihihi")
|
||||
assert("hello %d" % {123} == "hello 123")
|
|
@ -13,40 +13,147 @@
|
|||
-- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
-- PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
-- dup()+close()
|
||||
fd = assert(unix.dup(2))
|
||||
assert(unix.close(fd))
|
||||
gotsigusr1 = false
|
||||
tmpdir = "o/tmp/lunix_test.%d" % {unix.getpid()}
|
||||
|
||||
-- dup2()+close()
|
||||
assert(assert(unix.dup(2, 10)) == 10)
|
||||
assert(unix.close(10))
|
||||
|
||||
-- fork()+exit()
|
||||
if assert(unix.fork()) == 0 then
|
||||
unix.exit(42)
|
||||
function OnSigUsr1(sig)
|
||||
gotsigusr1 = true
|
||||
end
|
||||
pid, ws = assert(unix.wait())
|
||||
assert(unix.WIFEXITED(ws))
|
||||
assert(unix.WEXITSTATUS(ws) == 42)
|
||||
|
||||
-- pledge()
|
||||
if GetHostOs() == "LINUX" then
|
||||
function UnixTest()
|
||||
|
||||
-- strsignal
|
||||
assert(unix.strsignal(9) == "SIGKILL")
|
||||
assert(unix.strsignal(unix.SIGKILL) == "SIGKILL")
|
||||
|
||||
-- gmtime
|
||||
year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst,zone = assert(unix.gmtime(1657297063))
|
||||
assert(year == 2022)
|
||||
assert(mon == 7)
|
||||
assert(mday == 8)
|
||||
assert(hour == 16)
|
||||
assert(min == 17)
|
||||
assert(sec == 43)
|
||||
assert(gmtoffsec == 0)
|
||||
assert(wday == 5)
|
||||
assert(yday == 188)
|
||||
assert(dst == 0)
|
||||
assert(zone == "GMT")
|
||||
|
||||
-- dup
|
||||
-- 1. duplicate stderr as lowest available fd
|
||||
-- 1. close the newly assigned file descriptor
|
||||
fd = assert(unix.dup(2))
|
||||
assert(unix.close(fd))
|
||||
|
||||
-- dup2
|
||||
-- 1. duplicate stderr as fd 10
|
||||
-- 1. close the new file descriptor
|
||||
assert(assert(unix.dup(2, 10)) == 10)
|
||||
assert(unix.close(10))
|
||||
|
||||
-- fork
|
||||
-- basic subprocess creation
|
||||
if assert(unix.fork()) == 0 then
|
||||
assert(unix.pledge("stdio"))
|
||||
_, err = unix.socket()
|
||||
assert(err:errno() == unix.EPERM)
|
||||
unix.exit(0)
|
||||
unix.pledge("")
|
||||
unix.exit(42)
|
||||
end
|
||||
pid, ws = assert(unix.wait())
|
||||
assert(unix.WIFEXITED(ws))
|
||||
assert(unix.WEXITSTATUS(ws) == 0)
|
||||
elseif GetHostOs() == "OPENBSD" then
|
||||
if assert(unix.fork()) == 0 then
|
||||
assert(unix.pledge("stdio"))
|
||||
unix.socket()
|
||||
unix.exit(1)
|
||||
assert(unix.WEXITSTATUS(ws) == 42)
|
||||
|
||||
-- pledge
|
||||
-- 1. fork off a process
|
||||
-- 2. sandbox the process
|
||||
-- 3. then violate its security
|
||||
if GetHostOs() == "LINUX" then
|
||||
if assert(unix.fork()) == 0 then
|
||||
assert(unix.pledge("stdio"))
|
||||
_, err = unix.socket()
|
||||
assert(err:errno() == unix.EPERM)
|
||||
unix.exit(0)
|
||||
end
|
||||
pid, ws = assert(unix.wait())
|
||||
assert(unix.WIFEXITED(ws))
|
||||
assert(unix.WEXITSTATUS(ws) == 0)
|
||||
elseif GetHostOs() == "OPENBSD" then
|
||||
if assert(unix.fork()) == 0 then
|
||||
assert(unix.pledge("stdio"))
|
||||
unix.socket()
|
||||
unix.exit(1)
|
||||
end
|
||||
pid, ws = assert(unix.wait())
|
||||
assert(unix.WIFSIGNALED(ws))
|
||||
assert(unix.WTERMSIG(ws) == unix.SIGABRT)
|
||||
end
|
||||
pid, ws = assert(unix.wait())
|
||||
assert(unix.WIFSIGNALED(ws))
|
||||
assert(unix.WTERMSIG(ws) == unix.SIGABRT)
|
||||
|
||||
-- sigaction
|
||||
-- 1. install a signal handler for USR1
|
||||
-- 2. block USR1
|
||||
-- 3. trigger USR1 signal [it gets enqueued]
|
||||
-- 4. pause() w/ atomic unblocking of USR1 [now it gets delivered!]
|
||||
-- 5. restore old signal mask
|
||||
-- 6. restore old sig handler
|
||||
oldhand, oldflags, oldmask = assert(unix.sigaction(unix.SIGUSR1, OnSigUsr1))
|
||||
oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, unix.Sigset(unix.SIGUSR1)))
|
||||
assert(unix.raise(unix.SIGUSR1))
|
||||
assert(not gotsigusr1)
|
||||
ok, err = unix.sigsuspend(oldmask)
|
||||
assert(not ok)
|
||||
assert(err:errno() == unix.EINTR)
|
||||
assert(gotsigusr1)
|
||||
assert(unix.sigprocmask(unix.SIG_SETMASK, oldmask))
|
||||
assert(unix.sigaction(unix.SIGUSR1, oldhand, oldflags, oldmask))
|
||||
|
||||
-- open
|
||||
-- 1. create file
|
||||
-- 2. fill it up
|
||||
-- 3. inspect it
|
||||
-- 4. mess with it
|
||||
fd = assert(unix.open("%s/foo" % {tmpdir}, unix.O_RDWR | unix.O_CREAT | unix.O_TRUNC, 0600))
|
||||
assert(assert(unix.fstat(fd)):size() == 0)
|
||||
assert(unix.ftruncate(fd, 8192))
|
||||
assert(assert(unix.fstat(fd)):size() == 8192)
|
||||
assert(unix.write(fd, "hello"))
|
||||
assert(unix.lseek(fd, 4096))
|
||||
assert(unix.write(fd, "poke"))
|
||||
assert(unix.lseek(fd, 8192-4))
|
||||
assert(unix.write(fd, "poke"))
|
||||
st = assert(unix.fstat(fd))
|
||||
assert(st:size() == 8192)
|
||||
assert(st:blocks() == 8192/512)
|
||||
assert((st:mode() & 0777) == 0600)
|
||||
assert(st:uid() == unix.getuid())
|
||||
assert(st:gid() == unix.getgid())
|
||||
assert(unix.write(fd, "bear", 4))
|
||||
assert(unix.read(fd, 10, 0) == "hellbear\x00\x00")
|
||||
assert(unix.close(fd))
|
||||
fd = assert(unix.open("%s/foo" % {tmpdir}))
|
||||
assert(unix.lseek(fd, 4))
|
||||
assert(unix.read(fd, 4) == "bear")
|
||||
assert(unix.close(fd))
|
||||
fd = assert(unix.open("%s/foo" % {tmpdir}, unix.O_RDWR))
|
||||
assert(unix.write(fd, "bear"))
|
||||
assert(unix.close(fd))
|
||||
fd = assert(unix.open("%s/foo" % {tmpdir}))
|
||||
assert(unix.read(fd, 8) == "bearbear")
|
||||
assert(unix.close(fd))
|
||||
|
||||
-- getdents
|
||||
for name, kind, ino, off in assert(unix.opendir(tmpdir)) do
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function main()
|
||||
assert(unix.makedirs(tmpdir))
|
||||
ok, err = pcall(UnixTest)
|
||||
if ok then
|
||||
assert(unix.rmrf(tmpdir))
|
||||
else
|
||||
print(err)
|
||||
error('UnixTest failed (%s)' % {tmpdir})
|
||||
end
|
||||
end
|
||||
|
||||
main()
|
||||
|
|
2
third_party/lua/README.cosmo
vendored
2
third_party/lua/README.cosmo
vendored
|
@ -34,3 +34,5 @@ LOCAL MODIFICATIONS
|
|||
Added luaL_traceback2() for function parameters in traceback.
|
||||
|
||||
Added Python-like printf modulus operator for strings.
|
||||
|
||||
Added Python-like printf multiply operator for strings.
|
||||
|
|
14
third_party/lua/lstrlib.c
vendored
14
third_party/lua/lstrlib.c
vendored
|
@ -320,12 +320,22 @@ static int arith_sub (lua_State *L) {
|
|||
}
|
||||
|
||||
static int arith_mul (lua_State *L) {
|
||||
return arith(L, LUA_OPMUL, "__mul");
|
||||
if (lua_isinteger(L, 2)) {
|
||||
// [jart] python multiply string operator
|
||||
lua_pushcfunction(L, str_rep);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
lua_call(L, 2, 1);
|
||||
return 1;
|
||||
} else {
|
||||
return arith(L, LUA_OPMUL, "__mul");
|
||||
}
|
||||
}
|
||||
|
||||
static int arith_mod (lua_State *L) {
|
||||
int i, n;
|
||||
if (lua_istable(L, 2)) { // [jart] python printf operator
|
||||
if (lua_istable(L, 2)) {
|
||||
// [jart] python printf operator
|
||||
lua_len(L, 2);
|
||||
n = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
|
26
third_party/lua/lunix.c
vendored
26
third_party/lua/lunix.c
vendored
|
@ -386,6 +386,14 @@ static int LuaUnixMakedirs(lua_State *L) {
|
|||
makedirs(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0755)));
|
||||
}
|
||||
|
||||
// unix.rmrf(path:str)
|
||||
// ├─→ true
|
||||
// └─→ nil, unix.Errno
|
||||
static int LuaUnixRmrf(lua_State *L) {
|
||||
int olderr = errno;
|
||||
return SysretBool(L, "rmrf", olderr, rmrf(luaL_checkstring(L, 1)));
|
||||
}
|
||||
|
||||
// unix.chdir(path:str)
|
||||
// ├─→ true
|
||||
// └─→ nil, unix.Errno
|
||||
|
@ -1204,10 +1212,9 @@ static int LuaUnixGetsockopt(lua_State *L) {
|
|||
static int LuaUnixSocket(lua_State *L) {
|
||||
int olderr = errno;
|
||||
int family = luaL_optinteger(L, 1, AF_INET);
|
||||
return SysretInteger(
|
||||
L, "socket", olderr,
|
||||
socket(family, luaL_optinteger(L, 2, SOCK_STREAM),
|
||||
luaL_optinteger(L, 3, family == AF_INET ? IPPROTO_TCP : 0)));
|
||||
return SysretInteger(L, "socket", olderr,
|
||||
socket(family, luaL_optinteger(L, 2, SOCK_STREAM),
|
||||
luaL_optinteger(L, 3, 0)));
|
||||
}
|
||||
|
||||
// unix.socketpair([family:int[, type:int[, protocol:int]]])
|
||||
|
@ -1600,11 +1607,17 @@ static int LuaUnixSigaction(lua_State *L) {
|
|||
luaL_argerror(L, 2, "sigaction handler not integer or function");
|
||||
unreachable;
|
||||
}
|
||||
sa.sa_flags = luaL_optinteger(L, 3, 0);
|
||||
if (!lua_isnoneornil(L, 4)) {
|
||||
mask = luaL_checkudata(L, 4, "unix.Sigset");
|
||||
sa.sa_mask.__bits[0] |= mask->__bits[0];
|
||||
sa.sa_mask.__bits[1] |= mask->__bits[1];
|
||||
lua_remove(L, 4);
|
||||
}
|
||||
if (lua_isnoneornil(L, 3)) {
|
||||
sa.sa_flags = 0;
|
||||
} else {
|
||||
sa.sa_flags = lua_tointeger(L, 3);
|
||||
lua_remove(L, 3);
|
||||
}
|
||||
if (!sigaction(sig, saptr, &oldsa)) {
|
||||
lua_getglobal(L, "__signal_handlers");
|
||||
|
@ -2529,7 +2542,6 @@ static const luaL_Reg kLuaUnix[] = {
|
|||
{"getsid", LuaUnixGetsid}, // get session id of pid
|
||||
{"getsockname", LuaUnixGetsockname}, // get address of local end
|
||||
{"getsockopt", LuaUnixGetsockopt}, // get socket tunings
|
||||
{"tiocgwinsz", LuaUnixTiocgwinsz}, // pseudoteletypewriter dimensions
|
||||
{"getuid", LuaUnixGetuid}, // get real user id of process
|
||||
{"gmtime", LuaUnixGmtime}, // destructure unix timestamp
|
||||
{"isatty", LuaUnixIsatty}, // detects pseudoteletypewriters
|
||||
|
@ -2556,6 +2568,7 @@ static const luaL_Reg kLuaUnix[] = {
|
|||
{"recvfrom", LuaUnixRecvfrom}, // receive udp from some address
|
||||
{"rename", LuaUnixRename}, // rename file or directory
|
||||
{"rmdir", LuaUnixRmdir}, // remove empty directory
|
||||
{"rmrf", LuaUnixRmrf}, // remove file recursively
|
||||
{"send", LuaUnixSend}, // send tcp to some address
|
||||
{"sendto", LuaUnixSendto}, // send udp to some address
|
||||
{"setgid", LuaUnixSetgid}, // set real group id of process
|
||||
|
@ -2580,6 +2593,7 @@ static const luaL_Reg kLuaUnix[] = {
|
|||
{"symlink", LuaUnixSymlink}, // create symbolic link
|
||||
{"sync", LuaUnixSync}, // flushes files and disks
|
||||
{"syslog", LuaUnixSyslog}, // logs to system log
|
||||
{"tiocgwinsz", LuaUnixTiocgwinsz}, // pseudoteletypewriter dimensions
|
||||
{"truncate", LuaUnixTruncate}, // shrink or extend file medium
|
||||
{"umask", LuaUnixUmask}, // set default file mask
|
||||
{"unlink", LuaUnixUnlink}, // remove file
|
||||
|
|
76
third_party/regex/regcomp.c
vendored
76
third_party/regex/regcomp.c
vendored
|
@ -2388,12 +2388,15 @@ static reg_errcode_t tre_ast_to_tnfa(tre_ast_node_t *node,
|
|||
* Compiles regular expression, e.g.
|
||||
*
|
||||
* regex_t rx;
|
||||
* EXPECT_EQ(REG_OK, regcomp(&rx, "^[A-Za-z]{2}$", REG_EXTENDED));
|
||||
* EXPECT_EQ(REG_OK, regexec(&rx, "→A", 0, NULL, 0));
|
||||
* CHECK_EQ(REG_OK, regcomp(&rx, "^[A-Za-z]{2}$", REG_EXTENDED));
|
||||
* CHECK_EQ(REG_OK, regexec(&rx, "→A", 0, NULL, 0));
|
||||
* regfree(&rx);
|
||||
*
|
||||
* @param preg points to state, and needs regfree() afterwards
|
||||
* @param regex is utf-8 regular expression string
|
||||
* @param preg points to caller allocated memory that's used to store
|
||||
* your regular expression. This memory needn't be initialized. If
|
||||
* this function succeeds, then `preg` must be passed to regfree()
|
||||
* later on, to free its associated resources
|
||||
* @param regex is utf-8 regular expression nul-terminated string
|
||||
* @param cflags can have REG_EXTENDED, REG_ICASE, REG_NEWLINE, REG_NOSUB
|
||||
* @return REG_OK, REG_NOMATCH, REG_BADPAT, etc.
|
||||
* @see regexec(), regfree(), regerror()
|
||||
|
@ -2579,39 +2582,48 @@ error_exit:
|
|||
|
||||
/**
|
||||
* Frees any memory allocated by regcomp().
|
||||
*
|
||||
* The same object may be destroyed by regfree() multiple times, in
|
||||
* which case subsequent calls do nothing. Once a regex is freed, it may
|
||||
* be passed to regcomp() to reinitialize it.
|
||||
*/
|
||||
void regfree(regex_t *preg) {
|
||||
tre_tnfa_t *tnfa;
|
||||
unsigned int i;
|
||||
tre_tnfa_t *tnfa;
|
||||
tre_tnfa_transition_t *trans;
|
||||
tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
if (!tnfa) return;
|
||||
for (i = 0; i < tnfa->num_transitions; i++)
|
||||
if (tnfa->transitions[i].state) {
|
||||
if (tnfa->transitions[i].tags)
|
||||
free(tnfa->transitions[i].tags), tnfa->transitions[i].tags = NULL;
|
||||
if (tnfa->transitions[i].neg_classes)
|
||||
free(tnfa->transitions[i].neg_classes),
|
||||
tnfa->transitions[i].neg_classes = NULL;
|
||||
if ((tnfa = preg->TRE_REGEX_T_FIELD)) {
|
||||
preg->TRE_REGEX_T_FIELD = 0;
|
||||
for (i = 0; i < tnfa->num_transitions; i++)
|
||||
if (tnfa->transitions[i].state) {
|
||||
if (tnfa->transitions[i].tags) {
|
||||
free(tnfa->transitions[i].tags);
|
||||
}
|
||||
if (tnfa->transitions[i].neg_classes) {
|
||||
free(tnfa->transitions[i].neg_classes);
|
||||
}
|
||||
}
|
||||
if (tnfa->transitions) {
|
||||
free(tnfa->transitions);
|
||||
}
|
||||
if (tnfa->transitions) free(tnfa->transitions), tnfa->transitions = NULL;
|
||||
if (tnfa->initial) {
|
||||
for (trans = tnfa->initial; trans->state; trans++) {
|
||||
if (trans->tags) free(trans->tags), trans->tags = NULL;
|
||||
if (tnfa->initial) {
|
||||
for (trans = tnfa->initial; trans->state; trans++) {
|
||||
if (trans->tags) {
|
||||
free(trans->tags);
|
||||
}
|
||||
}
|
||||
free(tnfa->initial);
|
||||
}
|
||||
free(tnfa->initial), tnfa->initial = NULL;
|
||||
if (tnfa->submatch_data) {
|
||||
for (i = 0; i < tnfa->num_submatches; i++) {
|
||||
if (tnfa->submatch_data[i].parents) {
|
||||
free(tnfa->submatch_data[i].parents);
|
||||
}
|
||||
}
|
||||
free(tnfa->submatch_data);
|
||||
}
|
||||
if (tnfa->tag_directions) free(tnfa->tag_directions);
|
||||
if (tnfa->firstpos_chars) free(tnfa->firstpos_chars);
|
||||
if (tnfa->minimal_tags) free(tnfa->minimal_tags);
|
||||
free(tnfa);
|
||||
}
|
||||
if (tnfa->submatch_data) {
|
||||
for (i = 0; i < tnfa->num_submatches; i++)
|
||||
if (tnfa->submatch_data[i].parents)
|
||||
free(tnfa->submatch_data[i].parents),
|
||||
tnfa->submatch_data[i].parents = NULL;
|
||||
free(tnfa->submatch_data), tnfa->submatch_data = NULL;
|
||||
}
|
||||
if (tnfa->tag_directions)
|
||||
free(tnfa->tag_directions), tnfa->tag_directions = NULL;
|
||||
if (tnfa->firstpos_chars)
|
||||
free(tnfa->firstpos_chars), tnfa->firstpos_chars = NULL;
|
||||
if (tnfa->minimal_tags) free(tnfa->minimal_tags), tnfa->minimal_tags = NULL;
|
||||
free(tnfa), tnfa = NULL;
|
||||
}
|
||||
|
|
|
@ -357,6 +357,9 @@ LUA ENHANCEMENTS
|
|||
example, you can say `"hello %s" % {"world"}` instead of
|
||||
`string.format("hello %s", "world")`.
|
||||
|
||||
- redbean supports a string multiply operator, like Python. For
|
||||
example, you can say `"hi" * 2` instead of `string.rep("hi", 2)`.
|
||||
|
||||
- redbean supports octal (base 8) integer literals. For example
|
||||
`0644 == 420` is the case in redbean, whereas in upstream Lua
|
||||
`0644 == 644` would be the case.
|
||||
|
@ -1495,6 +1498,7 @@ CONSTANTS
|
|||
Logging anything at this level will result in a backtrace and
|
||||
process exit.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
LSQLITE3 MODULE
|
||||
|
||||
|
@ -1531,6 +1535,7 @@ LSQLITE3 MODULE
|
|||
we provide an APE build of the SQLite shell which you can use to
|
||||
administrate your redbean database. See the sqlite3.com download above.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
RE MODULE
|
||||
|
||||
|
@ -1540,29 +1545,144 @@ RE MODULE
|
|||
|
||||
# Example IPv4 Address Regular Expression (see also ParseIP)
|
||||
p = re.compile([[^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$]])
|
||||
m,a,b,c,d = p:search(𝑠)
|
||||
m,a,b,c,d = assert(p:search(𝑠))
|
||||
if m then
|
||||
print("ok", tonumber(a), tonumber(b), tonumber(c), tonumber(d))
|
||||
else
|
||||
print("not ok")
|
||||
end
|
||||
|
||||
re.search(regex:str,text:str[,flags:int]) → [match[,group_1,...]]
|
||||
Shortcut for re.compile plus regex_t*:search.
|
||||
re.search(regex:str, text:str[, flags:int])
|
||||
├─→ match:str[, group1:str, ...]
|
||||
└─→ nil, re.Errno
|
||||
|
||||
re.compile(regex:str[,flags:int]) → regex_t*
|
||||
Compiles regular expression, using the POSIX extended syntax. This
|
||||
has an O(2^𝑛) cost, so it's a good idea to do this from your
|
||||
/.init.lua file. Flags may contain re.BASIC, re.ICASE, re.NOSUB,
|
||||
and/or re.NEWLINE. See also regcomp() from libc.
|
||||
Searches for regular expression match in text.
|
||||
|
||||
regex_t*:search(text:str[,flags:int]) → [match[,group_1,...]]
|
||||
Executes regular expression. This has an O(𝑛) cost. This returns
|
||||
nothing (nil) if the pattern doesn't match anything. Otherwise it
|
||||
pushes the matched substring and any parenthesis-captured values
|
||||
too. Flags may contain re.NOTBOL or re.NOTEOL to indicate whether
|
||||
or not text should be considered at the start and/or end of a
|
||||
line.
|
||||
This is a shorthand notation roughly equivalent to:
|
||||
|
||||
preg = re.compile(regex)
|
||||
patt = preg:search(re, text)
|
||||
|
||||
`flags` defaults to zero and may have any of:
|
||||
|
||||
- `re.BASIC`
|
||||
- `re.ICASE`
|
||||
- `re.NEWLINE`
|
||||
- `re.NOSUB`
|
||||
- `re.NOTBOL`
|
||||
- `re.NOTEOL`
|
||||
|
||||
This has exponential complexity. Please use re.compile() to
|
||||
compile your regular expressions once from `/.init.lua`. This
|
||||
API exists for convenience. This isn't recommended for prod.
|
||||
|
||||
This uses POSIX extended syntax by default.
|
||||
|
||||
re.compile(regex:str[, flags:int]) → re.Regex
|
||||
├─→ preg:re.Regex
|
||||
└─→ nil, re.Errno
|
||||
|
||||
Compiles regular expression.
|
||||
|
||||
`flags` defaults to zero and may have any of:
|
||||
|
||||
- `re.BASIC`
|
||||
- `re.ICASE`
|
||||
- `re.NEWLINE`
|
||||
- `re.NOSUB`
|
||||
|
||||
This has an O(2^𝑛) cost. Consider compiling regular
|
||||
expressions once from your `/.init.lua` file.
|
||||
|
||||
If `regex` is an untrusted user value, then `unix.setrlimit`
|
||||
should be used to impose cpu and memory quotas for security.
|
||||
|
||||
This uses POSIX extended syntax by default.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
RE REGEX OBJECT
|
||||
|
||||
re.Regex:search(text:str[, flags:int])
|
||||
├─→ match:str[, group1:str, ...]
|
||||
└─→ nil, re.Errno
|
||||
|
||||
Executes precompiled regular expression.
|
||||
|
||||
Returns nothing (nil) if the pattern doesn't match anything.
|
||||
Otherwise it pushes the matched substring and any
|
||||
parenthesis-captured values too. Flags may contain re.NOTBOL
|
||||
or re.NOTEOL to indicate whether or not text should be
|
||||
considered at the start and/or end of a line.
|
||||
|
||||
`flags` defaults to zero and may have any of:
|
||||
|
||||
- `re.NOTBOL`
|
||||
- `re.NOTEOL`
|
||||
|
||||
This has an O(𝑛) cost.
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
RE ERRNO OBJECT
|
||||
|
||||
re.Errno:errno()
|
||||
└─→ errno:int
|
||||
|
||||
Returns regex error number.
|
||||
|
||||
re.Errno:doc()
|
||||
└─→ description:str
|
||||
|
||||
Returns English string describing error code.
|
||||
|
||||
re.Errno:__tostring()
|
||||
└─→ str
|
||||
|
||||
Delegates to re.Errno:doc()
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
RE ERRORS
|
||||
|
||||
re.NOMATCH
|
||||
No match
|
||||
|
||||
re.BADPAT
|
||||
Invalid regex
|
||||
|
||||
re.ECOLLATE
|
||||
Unknown collating element
|
||||
|
||||
re.ECTYPE
|
||||
Unknown character class name
|
||||
|
||||
re.EESCAPE
|
||||
Trailing backslash
|
||||
|
||||
re.ESUBREG
|
||||
Invalid back reference
|
||||
|
||||
re.EBRACK
|
||||
Missing `]`
|
||||
|
||||
re.EPAREN
|
||||
Missing `)`
|
||||
|
||||
re.EBRACE
|
||||
Missing `}`
|
||||
|
||||
re.BADBR
|
||||
Invalid contents of `{}`
|
||||
|
||||
re.ERANGE
|
||||
Invalid character range.
|
||||
|
||||
re.ESPACE
|
||||
Out of memory
|
||||
|
||||
re.BADRPT
|
||||
Repetition not preceded by valid expression
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
RE FLAGS
|
||||
|
||||
re.BASIC
|
||||
Use this flag if you prefer the default POSIX regex syntax. We use
|
||||
|
@ -1578,27 +1698,29 @@ RE MODULE
|
|||
may only be used with re.compile and re.search.
|
||||
|
||||
re.NEWLINE
|
||||
Use this flag to change the handling of NEWLINE (\x0a) characters.
|
||||
When this flag is set, (1) a NEWLINE shall not be matched by a "."
|
||||
or any form of a non-matching list, (2) a "^" shall match the
|
||||
zero-length string immediately after a NEWLINE (regardless of
|
||||
re.NOTBOL), and (3) a "$" shall match the zero-length string
|
||||
immediately before a NEWLINE (regardless of re.NOTEOL).
|
||||
Use this flag to change the handling of NEWLINE (\x0a)
|
||||
characters. When this flag is set, (1) a NEWLINE shall not be
|
||||
matched by a "." or any form of a non-matching list, (2) a "^"
|
||||
shall match the zero-length string immediately after a NEWLINE
|
||||
(regardless of re.NOTBOL), and (3) a "$" shall match the
|
||||
zero-length string immediately before a NEWLINE (regardless of
|
||||
re.NOTEOL).
|
||||
|
||||
re.NOSUB
|
||||
Causes re.search to only report success and failure. This is
|
||||
reported via the API by returning empty string for success. This
|
||||
flag may only be used with re.compile and re.search.
|
||||
reported via the API by returning empty string for success.
|
||||
This flag may only be used with re.compile and re.search.
|
||||
|
||||
re.NOTBOL
|
||||
The first character of the string pointed to by string is not the
|
||||
beginning of the line. This flag may only be used with re.search
|
||||
and regex_t*:search.
|
||||
The first character of the string pointed to by string is not
|
||||
the beginning of the line. This flag may only be used with
|
||||
re.search and re.Regex:search.
|
||||
|
||||
re.NOTEOL
|
||||
The last character of the string pointed to by string is not the
|
||||
end of the line. This flag may only be used with re.search and
|
||||
regex_t*:search.
|
||||
The last character of the string pointed to by string is not
|
||||
the end of the line. This flag may only be used with re.search
|
||||
and re.Regex:search.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
MAXMIND MODULE
|
||||
|
@ -1621,6 +1743,7 @@ MAXMIND MODULE
|
|||
|
||||
For further details, please see maxmind.lua in redbean-demo.com.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
ARGON2 MODULE
|
||||
|
||||
|
@ -1688,6 +1811,7 @@ ARGON2 MODULE
|
|||
"password")
|
||||
true
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
UNIX MODULE
|
||||
|
||||
|
@ -2276,6 +2400,18 @@ UNIX MODULE
|
|||
thereby assisting with simple absolute filename checks in addition
|
||||
to enabling one to exceed the traditional 260 character limit.
|
||||
|
||||
unix.rmrf(path:str)
|
||||
├─→ true
|
||||
└─→ nil, unix.Errno
|
||||
|
||||
Recursively removes filesystem path.
|
||||
|
||||
Like unix.makedirs() this function isn't actually a system call but
|
||||
rather is a Libc convenience wrapper. It's intended to be equivalent
|
||||
to using the UNIX shell's `rm -rf path` command.
|
||||
|
||||
`path` is the file or directory path you wish to destroy.
|
||||
|
||||
unix.fcntl(fd:int, cmd:int, ...)
|
||||
├─→ ...
|
||||
└─→ nil, unix.Errno
|
||||
|
@ -2532,7 +2668,7 @@ UNIX MODULE
|
|||
|
||||
`whence` can be one of:
|
||||
|
||||
- `SEEK_SET`: Sets the file position to `offset`
|
||||
- `SEEK_SET`: Sets the file position to `offset` [default]
|
||||
- `SEEK_CUR`: Sets the file position to `position + offset`
|
||||
- `SEEK_END`: Sets the file position to `filesize + offset`
|
||||
|
||||
|
@ -2580,14 +2716,14 @@ UNIX MODULE
|
|||
- `SOCK_CLOEXEC`
|
||||
- `SOCK_NONBLOCK`
|
||||
|
||||
`protocol` defaults to `IPPROTO_TCP` for AF_INET` and `0` for
|
||||
`AF_UNIX`. It can also be:
|
||||
`protocol` may be any of:
|
||||
|
||||
- `IPPROTO_IP`
|
||||
- `IPPROTO_ICMP`
|
||||
- `0` to let kernel choose [default]
|
||||
- `IPPROTO_TCP`
|
||||
- `IPPROTO_UDP`
|
||||
- `IPPROTO_RAW`
|
||||
- `IPPROTO_IP`
|
||||
- `IPPROTO_ICMP`
|
||||
|
||||
unix.socketpair([family:int[, type:int[, protocol:int]]])
|
||||
├─→ fd1:int, fd2:int
|
||||
|
@ -2964,18 +3100,19 @@ UNIX MODULE
|
|||
|
||||
Example:
|
||||
|
||||
assert(unix.sigaction(unix.SIGUSR1, function(sig)
|
||||
gotsigusr1 = true
|
||||
end))
|
||||
gotsigusr1 = false
|
||||
assert(unix.raise(unix.SIGUSR1))
|
||||
ok, err = unix.sigsuspend()
|
||||
assert(err:errno == unix.EINTR)
|
||||
if gotsigusr1
|
||||
print('hooray the signal was delivered')
|
||||
else
|
||||
print('oh no some other signal was handled')
|
||||
function OnSigUsr1(sig)
|
||||
gotsigusr1 = true
|
||||
end
|
||||
gotsigusr1 = false
|
||||
oldmask = assert(unix.sigprocmask(unix.SIG_BLOCK, unix.Sigset(unix.SIGUSR1)))
|
||||
assert(unix.sigaction(unix.SIGUSR1, OnSigUsr1))
|
||||
assert(unix.raise(unix.SIGUSR1))
|
||||
assert(not gotsigusr1)
|
||||
ok, err = unix.sigsuspend(oldmask)
|
||||
assert(not ok)
|
||||
assert(err:errno() == unix.EINTR)
|
||||
assert(gotsigusr1)
|
||||
assert(unix.sigprocmask(unix.SIG_SETMASK, oldmask))
|
||||
|
||||
It's a good idea to not do too much work in a signal handler.
|
||||
|
||||
|
|
|
@ -405,7 +405,7 @@ int LuaResolveIp(lua_State *L) {
|
|||
}
|
||||
|
||||
static int LuaCheckControlFlags(lua_State *L, int idx) {
|
||||
int f = luaL_checkinteger(L, idx);
|
||||
int f = luaL_optinteger(L, idx, 0);
|
||||
if (f & ~(kControlWs | kControlC0 | kControlC1)) {
|
||||
luaL_argerror(L, idx, "invalid control flags");
|
||||
unreachable;
|
||||
|
|
193
tool/net/lre.c
193
tool/net/lre.c
|
@ -17,13 +17,12 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/regex/regex.h"
|
||||
|
||||
static const char kRegCode[][8] = {
|
||||
"OK", "NOMATCH", "BADPAT", "COLLATE", "ECTYPE", "EESCAPE", "ESUBREG",
|
||||
"EBRACK", "EPAREN", "EBRACE", "BADBR", "ERANGE", "ESPACE", "BADRPT",
|
||||
struct ReErrno {
|
||||
int err;
|
||||
char doc[64];
|
||||
};
|
||||
|
||||
static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
|
||||
|
@ -31,40 +30,53 @@ static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
|
|||
lua_setfield(L, -2, k);
|
||||
}
|
||||
|
||||
static int LuaReReturnError(lua_State *L, regex_t *r, int rc) {
|
||||
struct ReErrno *e;
|
||||
lua_pushnil(L);
|
||||
e = lua_newuserdatauv(L, sizeof(struct ReErrno), 0);
|
||||
luaL_setmetatable(L, "re.Errno");
|
||||
e->err = rc;
|
||||
regerror(rc, r, e->doc, sizeof(e->doc));
|
||||
return 2;
|
||||
}
|
||||
|
||||
static regex_t *LuaReCompileImpl(lua_State *L, const char *p, int f) {
|
||||
int e;
|
||||
int rc;
|
||||
regex_t *r;
|
||||
r = xmalloc(sizeof(regex_t));
|
||||
r = lua_newuserdatauv(L, sizeof(regex_t), 0);
|
||||
luaL_setmetatable(L, "re.Regex");
|
||||
f &= REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB;
|
||||
f ^= REG_EXTENDED;
|
||||
if ((e = regcomp(r, p, f)) != REG_OK) {
|
||||
free(r);
|
||||
luaL_error(L, "regcomp(%s) → REG_%s", p,
|
||||
kRegCode[MAX(0, MIN(ARRAYLEN(kRegCode) - 1, e))]);
|
||||
unreachable;
|
||||
if ((rc = regcomp(r, p, f)) == REG_OK) {
|
||||
return r;
|
||||
} else {
|
||||
LuaReReturnError(L, r, rc);
|
||||
return NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int LuaReSearchImpl(lua_State *L, regex_t *r, const char *s, int f) {
|
||||
int i, n;
|
||||
int rc, i, n;
|
||||
regmatch_t *m;
|
||||
n = r->re_nsub + 1;
|
||||
m = xcalloc(n, sizeof(regmatch_t));
|
||||
if (regexec(r, s, n, m, f >> 8) == REG_OK) {
|
||||
luaL_Buffer tmp;
|
||||
n = 1 + r->re_nsub;
|
||||
m = (regmatch_t *)luaL_buffinitsize(L, &tmp, n * sizeof(regmatch_t));
|
||||
if ((rc = regexec(r, s, n, m, f >> 8)) == REG_OK) {
|
||||
for (i = 0; i < n; ++i) {
|
||||
lua_pushlstring(L, s + m[i].rm_so, m[i].rm_eo - m[i].rm_so);
|
||||
}
|
||||
return n;
|
||||
} else {
|
||||
n = 0;
|
||||
return LuaReReturnError(L, r, rc);
|
||||
}
|
||||
free(m);
|
||||
return n;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// re
|
||||
|
||||
static int LuaReSearch(lua_State *L) {
|
||||
int f;
|
||||
regex_t *r;
|
||||
int i, e, n, f;
|
||||
const char *p, *s;
|
||||
p = luaL_checkstring(L, 1);
|
||||
s = luaL_checkstring(L, 2);
|
||||
|
@ -74,56 +86,51 @@ static int LuaReSearch(lua_State *L) {
|
|||
luaL_argerror(L, 3, "invalid flags");
|
||||
unreachable;
|
||||
}
|
||||
r = LuaReCompileImpl(L, p, f);
|
||||
n = LuaReSearchImpl(L, r, s, f);
|
||||
regfree(r);
|
||||
free(r);
|
||||
return n;
|
||||
if ((r = LuaReCompileImpl(L, p, f))) {
|
||||
return LuaReSearchImpl(L, r, s, f);
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static int LuaReCompile(lua_State *L) {
|
||||
int f, e;
|
||||
int f;
|
||||
regex_t *r;
|
||||
const char *p;
|
||||
regex_t *r, **u;
|
||||
p = luaL_checkstring(L, 1);
|
||||
f = luaL_optinteger(L, 2, 0);
|
||||
if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB)) {
|
||||
luaL_argerror(L, 3, "invalid flags");
|
||||
luaL_argerror(L, 2, "invalid flags");
|
||||
unreachable;
|
||||
}
|
||||
r = LuaReCompileImpl(L, p, f);
|
||||
u = lua_newuserdata(L, sizeof(regex_t *));
|
||||
luaL_setmetatable(L, "regex_t*");
|
||||
*u = r;
|
||||
return 1;
|
||||
if ((r = LuaReCompileImpl(L, p, f))) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// re.Regex
|
||||
|
||||
static int LuaReRegexSearch(lua_State *L) {
|
||||
int f;
|
||||
regex_t **u;
|
||||
regex_t *r;
|
||||
const char *s;
|
||||
u = luaL_checkudata(L, 1, "regex_t*");
|
||||
r = luaL_checkudata(L, 1, "re.Regex");
|
||||
s = luaL_checkstring(L, 2);
|
||||
f = luaL_optinteger(L, 3, 0);
|
||||
if (!*u) {
|
||||
luaL_argerror(L, 1, "destroyed");
|
||||
unreachable;
|
||||
}
|
||||
if (f & ~(REG_NOTBOL << 8 | REG_NOTEOL << 8)) {
|
||||
luaL_argerror(L, 3, "invalid flags");
|
||||
unreachable;
|
||||
}
|
||||
return LuaReSearchImpl(L, *u, s, f);
|
||||
return LuaReSearchImpl(L, r, s, f);
|
||||
}
|
||||
|
||||
static int LuaReRegexGc(lua_State *L) {
|
||||
regex_t **u;
|
||||
u = luaL_checkudata(L, 1, "regex_t*");
|
||||
if (*u) {
|
||||
regfree(*u);
|
||||
free(*u);
|
||||
*u = NULL;
|
||||
}
|
||||
regex_t *r;
|
||||
r = luaL_checkudata(L, 1, "re.Regex");
|
||||
regfree(r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -143,8 +150,8 @@ static const luaL_Reg kLuaReRegexMeta[] = {
|
|||
{NULL, NULL}, //
|
||||
};
|
||||
|
||||
static void LuaReRegex(lua_State *L) {
|
||||
luaL_newmetatable(L, "regex_t*");
|
||||
static void LuaReRegexObj(lua_State *L) {
|
||||
luaL_newmetatable(L, "re.Regex");
|
||||
luaL_setfuncs(L, kLuaReRegexMeta, 0);
|
||||
luaL_newlibtable(L, kLuaReRegexMeth);
|
||||
luaL_setfuncs(L, kLuaReRegexMeth, 0);
|
||||
|
@ -152,14 +159,84 @@ static void LuaReRegex(lua_State *L) {
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
int LuaRe(lua_State *L) {
|
||||
luaL_newlib(L, kLuaRe);
|
||||
LuaSetIntField(L, "BASIC", REG_EXTENDED);
|
||||
LuaSetIntField(L, "ICASE", REG_ICASE);
|
||||
LuaSetIntField(L, "NEWLINE", REG_NEWLINE);
|
||||
LuaSetIntField(L, "NOSUB", REG_NOSUB);
|
||||
LuaSetIntField(L, "NOTBOL", REG_NOTBOL << 8);
|
||||
LuaSetIntField(L, "NOTEOL", REG_NOTEOL << 8);
|
||||
LuaReRegex(L);
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// re.Errno
|
||||
|
||||
static struct ReErrno *GetReErrno(lua_State *L) {
|
||||
return luaL_checkudata(L, 1, "re.Errno");
|
||||
}
|
||||
|
||||
// re.Errno:errno()
|
||||
// └─→ errno:int
|
||||
static int LuaReErrnoErrno(lua_State *L) {
|
||||
lua_pushinteger(L, GetReErrno(L)->err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// re.Errno:doc()
|
||||
// └─→ description:str
|
||||
static int LuaReErrnoDoc(lua_State *L) {
|
||||
lua_pushstring(L, GetReErrno(L)->doc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg kLuaReErrnoMeth[] = {
|
||||
{"errno", LuaReErrnoErrno}, //
|
||||
{"doc", LuaReErrnoDoc}, //
|
||||
{0}, //
|
||||
};
|
||||
|
||||
static const luaL_Reg kLuaReErrnoMeta[] = {
|
||||
{"__tostring", LuaReErrnoDoc}, //
|
||||
{0}, //
|
||||
};
|
||||
|
||||
static void LuaReErrnoObj(lua_State *L) {
|
||||
luaL_newmetatable(L, "re.Errno");
|
||||
luaL_setfuncs(L, kLuaReErrnoMeta, 0);
|
||||
luaL_newlibtable(L, kLuaReErrnoMeth);
|
||||
luaL_setfuncs(L, kLuaReErrnoMeth, 0);
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
_Alignas(1) static const struct thatispacked {
|
||||
const char s[8];
|
||||
char x;
|
||||
} kReMagnums[] = {
|
||||
{"BASIC", REG_EXTENDED}, // compile flag
|
||||
{"ICASE", REG_ICASE}, // compile flag
|
||||
{"NEWLINE", REG_NEWLINE}, // compile flag
|
||||
{"NOSUB", REG_NOSUB}, // compile flag
|
||||
{"NOMATCH", REG_NOMATCH}, // error
|
||||
{"BADPAT", REG_BADPAT}, // error
|
||||
{"ECOLLATE", REG_ECOLLATE}, // error
|
||||
{"ECTYPE", REG_ECTYPE}, // error
|
||||
{"EESCAPE", REG_EESCAPE}, // error
|
||||
{"ESUBREG", REG_ESUBREG}, // error
|
||||
{"EBRACK", REG_EBRACK}, // error
|
||||
{"EPAREN", REG_EPAREN}, // error
|
||||
{"EBRACE", REG_EBRACE}, // error
|
||||
{"BADBR", REG_BADBR}, // error
|
||||
{"ERANGE", REG_ERANGE}, // error
|
||||
{"ESPACE", REG_ESPACE}, // error
|
||||
{"BADRPT", REG_BADRPT}, // error
|
||||
};
|
||||
|
||||
int LuaRe(lua_State *L) {
|
||||
int i;
|
||||
char buf[9];
|
||||
luaL_newlib(L, kLuaRe);
|
||||
LuaSetIntField(L, "NOTBOL", REG_NOTBOL << 8); // search flag
|
||||
LuaSetIntField(L, "NOTEOL", REG_NOTEOL << 8); // search flag
|
||||
for (i = 0; i < ARRAYLEN(kReMagnums); ++i) {
|
||||
memcpy(buf, kReMagnums[i].s, 8);
|
||||
buf[8] = 0;
|
||||
LuaSetIntField(L, buf, kReMagnums[i].x);
|
||||
}
|
||||
LuaReRegexObj(L);
|
||||
LuaReErrnoObj(L);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -5398,7 +5398,11 @@ static void LuaInit(void) {
|
|||
lua_State *L = GL;
|
||||
LuaSetArgv(L);
|
||||
if (interpretermode) {
|
||||
exit(LuaInterpreter(L));
|
||||
int rc = LuaInterpreter(L);
|
||||
if (IsModeDbg()) {
|
||||
CheckForMemoryLeaks();
|
||||
}
|
||||
exit(rc);
|
||||
}
|
||||
if (LuaRunAsset("/.init.lua", true)) {
|
||||
hasonhttprequest = IsHookDefined("OnHttpRequest");
|
||||
|
|
Loading…
Reference in a new issue