mirror of
				https://github.com/jart/cosmopolitan.git
				synced 2025-10-26 11:10:58 +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…
	
	Add table
		Add a link
		
	
		Reference in a new issue