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:
Justine Tunney 2022-07-08 23:06:46 -07:00
parent c5b9902ac9
commit 1c83670229
20 changed files with 738 additions and 204 deletions

View file

@ -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};

View file

@ -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)

View file

@ -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;

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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);
}

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/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));
}

View 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"))

View file

@ -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&amp;there&lt;&gt;")
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")

View 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 ']'")

View 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")

View file

@ -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()

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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.

View file

@ -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;

View file

@ -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;
}

View file

@ -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");