From 5022f9e9207ff2b79ddd6de6d792d3280e12fb3a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 21 Mar 2022 19:31:03 -0700 Subject: [PATCH] Trim down redbean a little bit This makes redbean.c a little less long. It also reduces the size of redbean-original.com from being 333K to 213K. --- net/http/foldheader.c | 43 ++ net/http/http.h | 1 + net/https/https.h | 1 - third_party/lua/cosmo.h | 23 + third_party/lua/escapeluastring.c | 36 ++ third_party/lua/lua.mk | 1 + third_party/lua/luacallwithtrace.c | 66 +++ third_party/lua/luaencodejsondata.c | 85 +++ third_party/lua/luaencodeluadata.c | 66 +++ third_party/lua/luaencodeurl.c | 78 +++ third_party/lua/luaformatstack.c | 48 ++ third_party/lua/luaparseurl.c | 58 ++ third_party/lua/luapushheader.c | 35 ++ third_party/lua/luapushheaders.c | 44 ++ third_party/lua/luapushlatin1.c | 31 ++ third_party/lua/luapushurlparams.c | 37 ++ third_party/quickjs/cutils.h | 14 +- third_party/quickjs/quickjs.c | 8 +- tool/net/lre.c | 165 ++++++ tool/net/net.mk | 4 + tool/net/redbean.c | 824 ++++++---------------------- 21 files changed, 999 insertions(+), 669 deletions(-) create mode 100644 net/http/foldheader.c create mode 100644 third_party/lua/cosmo.h create mode 100644 third_party/lua/escapeluastring.c create mode 100644 third_party/lua/luacallwithtrace.c create mode 100644 third_party/lua/luaencodejsondata.c create mode 100644 third_party/lua/luaencodeluadata.c create mode 100644 third_party/lua/luaencodeurl.c create mode 100644 third_party/lua/luaformatstack.c create mode 100644 third_party/lua/luaparseurl.c create mode 100644 third_party/lua/luapushheader.c create mode 100644 third_party/lua/luapushheaders.c create mode 100644 third_party/lua/luapushlatin1.c create mode 100644 third_party/lua/luapushurlparams.c create mode 100644 tool/net/lre.c diff --git a/net/http/foldheader.c b/net/http/foldheader.c new file mode 100644 index 000000000..f840527dc --- /dev/null +++ b/net/http/foldheader.c @@ -0,0 +1,43 @@ +/*-*- 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/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "net/http/http.h" + +char *FoldHeader(struct HttpMessage *msg, char *b, int h, size_t *z) { + char *p; + size_t i, n, m; + struct HttpHeader *x; + n = msg->headers[h].b - msg->headers[h].a; + if ((p = malloc(n))) { + memcpy(p, b + msg->headers[h].a, n); + for (i = 0; i < msg->xheaders.n; ++i) { + x = msg->xheaders.p + i; + if (GetHttpHeader(b + x->k.a, x->k.b - x->k.a) == h) { + m = x->v.b - x->v.a; + if (!(p = realloc(p, n + 2 + m))) abort(); + memcpy(mempcpy(p + n, ", ", 2), b + x->v.a, m); + n += 2 + m; + } + } + *z = n; + } + return p; +} diff --git a/net/http/http.h b/net/http/http.h index e63e44089..d6bfc93d1 100644 --- a/net/http/http.h +++ b/net/http/http.h @@ -208,6 +208,7 @@ int ParseForwarded(const char *, size_t, uint32_t *, uint16_t *); bool IsMimeType(const char *, size_t, const char *); ssize_t Unchunk(struct HttpUnchunker *, char *, size_t, size_t *); const char *FindContentType(const char *, size_t); +char *FoldHeader(struct HttpMessage *, char *, int, size_t *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/net/https/https.h b/net/https/https.h index 98459e876..f83229875 100644 --- a/net/https/https.h +++ b/net/https/https.h @@ -29,7 +29,6 @@ void TlsDie(const char *, int) wontreturn; bool ChainCertificate(mbedtls_x509_crt *, mbedtls_x509_crt *); bool CertHasIp(const mbedtls_x509_crt *, uint32_t); bool CertHasHost(const mbedtls_x509_crt *, const void *, size_t); -bool CertHasCommonName(const mbedtls_x509_crt *, const void *, size_t); bool IsServerCert(const struct Cert *, mbedtls_pk_type_t); void TlsDebug(void *, int, const char *, int, const char *); diff --git a/third_party/lua/cosmo.h b/third_party/lua/cosmo.h new file mode 100644 index 000000000..dae4df598 --- /dev/null +++ b/third_party/lua/cosmo.h @@ -0,0 +1,23 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_LUA_COSMO_H_ +#define COSMOPOLITAN_THIRD_PARTY_LUA_COSMO_H_ +#include "net/http/http.h" +#include "net/http/url.h" +#include "third_party/lua/lauxlib.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +char *LuaFormatStack(lua_State *) nodiscard; +int LuaCallWithTrace(lua_State *, int, int, lua_State *); +int LuaEncodeJsonData(lua_State *, char **, int, char *); +int LuaEncodeLuaData(lua_State *, char **, int, char *); +int LuaEncodeUrl(lua_State *); +int LuaParseUrl(lua_State *); +void EscapeLuaString(char *, size_t, char **); +void LuaPushUrlParams(lua_State *, struct UrlParams *); +int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *); +void LuaPushLatin1(lua_State *, const char *, size_t); +int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_LUA_COSMO_H_ */ diff --git a/third_party/lua/escapeluastring.c b/third_party/lua/escapeluastring.c new file mode 100644 index 000000000..9574b80cc --- /dev/null +++ b/third_party/lua/escapeluastring.c @@ -0,0 +1,36 @@ +/*-*- 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/stdio/append.internal.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lua.h" + +void EscapeLuaString(char *s, size_t len, char **buf) { + appendw(buf, '"'); + for (size_t i = 0; i < len; i++) { + if (s[i] == '\\' || s[i] == '\"' || s[i] == '\n' || s[i] == '\0' || + s[i] == '\r') { + appendw(buf, '\\' | 'x' << 010 | + "0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 | + "0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030); + } else { + appendd(buf, s + i, 1); + } + } + appendw(buf, '"'); +} diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 681392c4d..71a7b9370 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -35,6 +35,7 @@ THIRD_PARTY_LUA_DIRECTDEPS = \ LIBC_X \ LIBC_TINYMATH \ LIBC_UNICODE \ + NET_HTTP \ THIRD_PARTY_LINENOISE \ THIRD_PARTY_GDTOA diff --git a/third_party/lua/luacallwithtrace.c b/third_party/lua/luacallwithtrace.c new file mode 100644 index 000000000..6217a120f --- /dev/null +++ b/third_party/lua/luacallwithtrace.c @@ -0,0 +1,66 @@ +/*-*- 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/macros.internal.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" + +// calling convention for lua stack of L is: +// -2 is function +// -1 is is argument (assuming nargs == 1) +// L will have this after the call +// -1 is error or result (assuming nres == 1) +// @param L is main Lua interpreter +// @note this needs to be reentrant +int LuaCallWithTrace(lua_State *L, int nargs, int nres, lua_State *C) { + int nresults, status; + bool canyield = !!C; // allow yield if coroutine is provided + if (!C) C = lua_newthread(L); // create a new coroutine if not passed + // move coroutine to the bottom of the stack (including one that is passed) + lua_insert(L, 1); + // make sure that there is enough stack space + if (!lua_checkstack(C, 1 + nargs)) { + lua_pushliteral(L, "too many arguments to resume"); + return LUA_ERRRUN; /* error flag */ + } + // move the function (and arguments) to the top of the coro stack + lua_xmove(L, C, 1 + nargs); + // resume the coroutine thus executing the function + status = lua_resume(C, L, nargs, &nresults); + lua_remove(L, 1); // remove coroutine (still) at the bottom + if (status != LUA_OK && status != LUA_YIELD) { + // move the error message + lua_xmove(C, L, 1); + // replace the error with the traceback on failure + luaL_traceback(L, C, lua_tostring(L, -1), 0); + lua_remove(L, -2); // remove the error message + } else { + if (!lua_checkstack(L, MAX(nresults, nres))) { + lua_pop(C, nresults); /* remove results anyway */ + lua_pushliteral(L, "too many results to resume"); + return LUA_ERRRUN; /* error flag */ + } + lua_xmove(C, L, nresults); // move results to the main stack + // grow the stack in case returned fewer results + // than the caller expects, as lua_resume + // doesn't adjust the stack for needed results + for (; nresults < nres; nresults++) lua_pushnil(L); + if (!canyield) status = LUA_OK; // treat LUA_YIELD the same as LUA_OK + } + return status; +} diff --git a/third_party/lua/luaencodejsondata.c b/third_party/lua/luaencodejsondata.c new file mode 100644 index 000000000..6cc64cd5a --- /dev/null +++ b/third_party/lua/luaencodejsondata.c @@ -0,0 +1,85 @@ +/*-*- 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/runtime/gc.internal.h" +#include "libc/stdio/append.internal.h" +#include "net/http/escape.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat) { + size_t idx = -1; + size_t tbllen, buflen; + bool isarray; + int t = lua_type(L, idx); + if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); + if (LUA_TSTRING == t) { + appendw(buf, '"'); + appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, idx), -1, 0))); + appendw(buf, '"'); + } else if (LUA_TNUMBER == t) { + appendf(buf, numformat, lua_tonumber(L, idx)); + } else if (LUA_TBOOLEAN == t) { + appends(buf, lua_toboolean(L, idx) ? "true" : "false"); + } else if (LUA_TTABLE == t) { + tbllen = lua_rawlen(L, idx); + // encode tables with numeric indices and empty tables as arrays + isarray = tbllen > 0 || // integer keys present + (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys + (lua_pop(L, 2), false); // pop key/value pushed by lua_next + appendw(buf, isarray ? '[' : '{'); + if (isarray) { + for (int i = 1; i <= tbllen; i++) { + if (i > 1) appendw(buf, ','); + lua_rawgeti(L, -1, i); // table/-2, value/-1 + LuaEncodeJsonData(L, buf, level - 1, numformat); + lua_pop(L, 1); + } + } else { + int i = 1; + lua_pushnil(L); // push the first key + while (lua_next(L, -2) != 0) { + if (!lua_isstring(L, -2)) + return luaL_argerror(L, 1, "expected number or string as key value"); + if (i++ > 1) appendw(buf, ','); + appendw(buf, '"'); + if (lua_type(L, -2) == LUA_TSTRING) { + appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, -2), -1, 0))); + } else { + // we'd still prefer to use lua_tostring on a numeric index, but can't + // use it in-place, as it breaks lua_next (changes numeric key to a + // string) + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + appends(buf, lua_tostring(L, idx)); // use the copy + lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 + } + appendw(buf, '"' | ':' << 010); + LuaEncodeJsonData(L, buf, level - 1, numformat); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + } + appendw(buf, isarray ? ']' : '}'); + } else if (LUA_TNIL == t) { + appendd(buf, "null", 4); + } else { + return luaL_argerror(L, 1, "can't serialize value of this type"); + } + return 0; +} diff --git a/third_party/lua/luaencodeluadata.c b/third_party/lua/luaencodeluadata.c new file mode 100644 index 000000000..fbee64e63 --- /dev/null +++ b/third_party/lua/luaencodeluadata.c @@ -0,0 +1,66 @@ +/*-*- 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/math.h" +#include "libc/stdio/append.internal.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat) { + size_t idx = -1; + size_t tbllen, buflen, slen; + char *s; + int ktype; + int t = lua_type(L, idx); + if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); + if (LUA_TSTRING == t) { + s = lua_tolstring(L, idx, &slen); + EscapeLuaString(s, slen, buf); + } else if (LUA_TNUMBER == t) { + appendf(buf, numformat, lua_tonumber(L, idx)); + } else if (LUA_TBOOLEAN == t) { + appends(buf, lua_toboolean(L, idx) ? "true" : "false"); + } else if (LUA_TTABLE == t) { + appendw(buf, '{'); + int i = 0; + lua_pushnil(L); // push the first key + while (lua_next(L, -2) != 0) { + ktype = lua_type(L, -2); + if (ktype == LUA_TTABLE) + return luaL_argerror(L, 1, "can't serialize key of this type"); + if (i++ > 0) appendd(buf, ",", 1); + if (ktype != LUA_TNUMBER || floor(lua_tonumber(L, -2)) != i) { + appendw(buf, '['); + lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 + LuaEncodeLuaData(L, buf, level, numformat); + lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 + appendw(buf, ']' | '=' << 010); + } + LuaEncodeLuaData(L, buf, level - 1, numformat); + lua_pop(L, 1); // table/-2, key/-1 + } + // stack: table/-1, as the key was popped by lua_next + appendw(buf, '}'); + } else if (LUA_TNIL == t) { + appendd(buf, "nil", 3); + } else { + return luaL_argerror(L, 1, "can't serialize value of this type"); + } + return 0; +} diff --git a/third_party/lua/luaencodeurl.c b/third_party/lua/luaencodeurl.c new file mode 100644 index 000000000..0a34eca38 --- /dev/null +++ b/third_party/lua/luaencodeurl.c @@ -0,0 +1,78 @@ +/*-*- 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/x/x.h" +#include "net/http/url.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +int LuaEncodeUrl(lua_State *L) { + size_t size; + struct Url h; + int i, j, k, n, m; + const char *data; + if (!lua_isnil(L, 1)) { + bzero(&h, sizeof(h)); + luaL_checktype(L, 1, LUA_TTABLE); + if (lua_getfield(L, 1, "scheme")) + h.scheme.p = lua_tolstring(L, -1, &h.scheme.n); + if (lua_getfield(L, 1, "fragment")) + h.fragment.p = lua_tolstring(L, -1, &h.fragment.n); + if (lua_getfield(L, 1, "user")) h.user.p = lua_tolstring(L, -1, &h.user.n); + if (lua_getfield(L, 1, "pass")) h.pass.p = lua_tolstring(L, -1, &h.pass.n); + if (lua_getfield(L, 1, "host")) h.host.p = lua_tolstring(L, -1, &h.host.n); + if (lua_getfield(L, 1, "port")) h.port.p = lua_tolstring(L, -1, &h.port.n); + if (lua_getfield(L, 1, "path")) h.path.p = lua_tolstring(L, -1, &h.path.n); + if (lua_getfield(L, 1, "params")) { + luaL_checktype(L, -1, LUA_TTABLE); + lua_len(L, -1); + n = lua_tointeger(L, -1); + for (i = -2, k = 0, j = 1; j <= n; ++j) { + if (lua_geti(L, i--, j)) { + luaL_checktype(L, -1, LUA_TTABLE); + lua_len(L, -1); + m = lua_tointeger(L, -1); + lua_pop(L, 1); // remove the table length + if (m >= 1 && lua_geti(L, -1, 1)) { + h.params.p = + xrealloc(h.params.p, ++h.params.n * sizeof(*h.params.p)); + h.params.p[h.params.n - 1].key.p = + lua_tolstring(L, -1, &h.params.p[h.params.n - 1].key.n); + if (m >= 2 && lua_geti(L, -2, 2)) { + h.params.p[h.params.n - 1].val.p = + lua_tolstring(L, -1, &h.params.p[h.params.n - 1].val.n); + } else { + h.params.p[h.params.n - 1].val.p = 0; + h.params.p[h.params.n - 1].val.n = 0; + } + } + i--; + } + i--; + } + } + data = EncodeUrl(&h, &size); + lua_pushlstring(L, data, size); + free(h.params.p); + free(data); + } else { + lua_pushnil(L); + } + return 1; +} diff --git a/third_party/lua/luaformatstack.c b/third_party/lua/luaformatstack.c new file mode 100644 index 000000000..a71a1c676 --- /dev/null +++ b/third_party/lua/luaformatstack.c @@ -0,0 +1,48 @@ +/*-*- 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/stdio/append.internal.h" +#include "third_party/lua/cosmo.h" + +nodiscard char *LuaFormatStack(lua_State *L) { + int i, top; + char *b = 0; + top = lua_gettop(L); + for (i = 1; i <= top; i++) { + if (i > 1) appendw(&b, '\n'); + appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i)); + switch (lua_type(L, i)) { + case LUA_TNUMBER: + appendf(&b, "%g", lua_tonumber(L, i)); + break; + case LUA_TSTRING: + appends(&b, lua_tostring(L, i)); + break; + case LUA_TBOOLEAN: + appends(&b, lua_toboolean(L, i) ? "true" : "false"); + break; + case LUA_TNIL: + appends(&b, "nil"); + break; + default: + appendf(&b, "%p", lua_topointer(L, i)); + break; + } + } + return b; +} diff --git a/third_party/lua/luaparseurl.c b/third_party/lua/luaparseurl.c new file mode 100644 index 000000000..a3310669d --- /dev/null +++ b/third_party/lua/luaparseurl.c @@ -0,0 +1,58 @@ +/*-*- 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/mem/mem.h" +#include "net/http/url.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +static void LuaPushUrlView(lua_State *L, struct UrlView *v) { + if (v->p) { + lua_pushlstring(L, v->p, v->n); + } else { + lua_pushnil(L); + } +} + +static void LuaSetUrlView(lua_State *L, struct UrlView *v, const char *k) { + LuaPushUrlView(L, v); + lua_setfield(L, -2, k); +} + +int LuaParseUrl(lua_State *L) { + void *m; + size_t n; + struct Url h; + const char *p; + p = luaL_checklstring(L, 1, &n); + m = ParseUrl(p, n, &h); + lua_newtable(L); + LuaSetUrlView(L, &h.scheme, "scheme"); + LuaSetUrlView(L, &h.user, "user"); + LuaSetUrlView(L, &h.pass, "pass"); + LuaSetUrlView(L, &h.host, "host"); + LuaSetUrlView(L, &h.port, "port"); + LuaSetUrlView(L, &h.path, "path"); + LuaSetUrlView(L, &h.fragment, "fragment"); + LuaPushUrlParams(L, &h.params); + lua_setfield(L, -2, "params"); + free(h.params.p); + free(m); + return 1; +} diff --git a/third_party/lua/luapushheader.c b/third_party/lua/luapushheader.c new file mode 100644 index 000000000..efaa6d6b4 --- /dev/null +++ b/third_party/lua/luapushheader.c @@ -0,0 +1,35 @@ +/*-*- 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 "net/http/http.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +int LuaPushHeader(lua_State *L, struct HttpMessage *m, char *b, int h) { + char *val; + size_t vallen; + if (!kHttpRepeatable[h]) { + LuaPushLatin1(L, b + m->headers[h].a, m->headers[h].b - m->headers[h].a); + } else { + val = FoldHeader(m, b, h, &vallen); + LuaPushLatin1(L, val, vallen); + free(val); + } + return 1; +} diff --git a/third_party/lua/luapushheaders.c b/third_party/lua/luapushheaders.c new file mode 100644 index 000000000..44ab42363 --- /dev/null +++ b/third_party/lua/luapushheaders.c @@ -0,0 +1,44 @@ +/*-*- 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 "net/http/escape.h" +#include "net/http/http.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lua.h" + +int LuaPushHeaders(lua_State *L, struct HttpMessage *m, const char *b) { + char *s; + size_t i, h; + struct HttpHeader *x; + lua_newtable(L); + for (h = 0; h < kHttpHeadersMax; ++h) { + if (m->headers[h].a) { + LuaPushHeader(L, m, b, h); + lua_setfield(L, -2, GetHttpHeaderName(h)); + } + } + for (i = 0; i < m->xheaders.n; ++i) { + x = m->xheaders.p + i; + if ((h = GetHttpHeader(b + x->v.a, x->v.b - x->v.a)) == -1) { + LuaPushLatin1(L, b + x->v.a, x->v.b - x->v.a); + lua_setfield(L, -2, (s = DecodeLatin1(b + x->k.a, x->k.b - x->k.a, 0))); + free(s); + } + } + return 1; +} diff --git a/third_party/lua/luapushlatin1.c b/third_party/lua/luapushlatin1.c new file mode 100644 index 000000000..22d8e5ee3 --- /dev/null +++ b/third_party/lua/luapushlatin1.c @@ -0,0 +1,31 @@ +/*-*- 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/mem/mem.h" +#include "net/http/escape.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +void LuaPushLatin1(lua_State *L, const char *s, size_t n) { + char *t; + size_t m; + t = DecodeLatin1(s, n, &m); + lua_pushlstring(L, t, m); + free(t); +} diff --git a/third_party/lua/luapushurlparams.c b/third_party/lua/luapushurlparams.c new file mode 100644 index 000000000..cd834bc64 --- /dev/null +++ b/third_party/lua/luapushurlparams.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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 "net/http/url.h" +#include "third_party/lua/cosmo.h" +#include "third_party/lua/lauxlib.h" +#include "third_party/lua/lua.h" + +void LuaPushUrlParams(lua_State *L, struct UrlParams *h) { + size_t i; + lua_newtable(L); + for (i = 0; i < h->n; ++i) { + lua_newtable(L); + lua_pushlstring(L, h->p[i].key.p, h->p[i].key.n); + lua_seti(L, -2, 1); + if (h->p[i].val.p) { + lua_pushlstring(L, h->p[i].val.p, h->p[i].val.n); + lua_seti(L, -2, 2); + } + lua_seti(L, -2, i + 1); + } +} diff --git a/third_party/quickjs/cutils.h b/third_party/quickjs/cutils.h index 4a8d95509..c2fd6010f 100644 --- a/third_party/quickjs/cutils.h +++ b/third_party/quickjs/cutils.h @@ -1,10 +1,9 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_QUICKJS_CUTILS_H_ #define COSMOPOLITAN_THIRD_PARTY_QUICKJS_CUTILS_H_ +#include "libc/bits/bswap.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ /* clang-format off */ -#include "libc/bits/bswap.h" -#include "libc/bits/bswap.h" /* set if CPU is big endian */ #undef WORDS_BIGENDIAN @@ -16,9 +15,6 @@ COSMOPOLITAN_C_START_ #define stringify(s) tostring(s) #define tostring(s) #s -#ifndef offsetof -#define offsetof(type, field) ((size_t) &((type *)0)->field) -#endif #ifndef countof #define countof(x) (sizeof(x) / sizeof((x)[0])) #endif @@ -32,10 +28,10 @@ enum { }; #endif -void pstrcpy(char *buf, int buf_size, const char *str); -char *pstrcat(char *buf, int buf_size, const char *s); -int strstart(const char *str, const char *val, const char **ptr); -int has_suffix(const char *str, const char *suffix); +void pstrcpy(char *, int, const char *); +char *pstrcat(char *, int, const char *); +int strstart(const char *, const char *, const char **); +int has_suffix(const char *, const char *); static inline int max_int(int a, int b) { diff --git a/third_party/quickjs/quickjs.c b/third_party/quickjs/quickjs.c index 151311542..b8d6c9c6a 100644 --- a/third_party/quickjs/quickjs.c +++ b/third_party/quickjs/quickjs.c @@ -12983,8 +12983,12 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } } else { b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); - memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); + if (fd->arg_count) { + memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); + } + if (fd->var_count) { + memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); + } } b->var_count = fd->var_count; b->arg_count = fd->arg_count; diff --git a/tool/net/lre.c b/tool/net/lre.c new file mode 100644 index 000000000..3f1eb2ddf --- /dev/null +++ b/tool/net/lre.c @@ -0,0 +1,165 @@ +/*-*- 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/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", +}; + +static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static regex_t *LuaReCompileImpl(lua_State *L, const char *p, int f) { + int e; + regex_t *r; + r = xmalloc(sizeof(regex_t)); + 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; + } + return r; +} + +static int LuaReSearchImpl(lua_State *L, regex_t *r, const char *s, int f) { + int 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) { + for (i = 0; i < n; ++i) { + lua_pushlstring(L, s + m[i].rm_so, m[i].rm_eo - m[i].rm_so); + } + } else { + n = 0; + } + free(m); + return n; +} + +static int LuaReSearch(lua_State *L) { + regex_t *r; + int i, e, n, f; + const char *p, *s; + p = luaL_checkstring(L, 1); + s = luaL_checkstring(L, 2); + f = luaL_optinteger(L, 3, 0); + if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB | + REG_NOTBOL << 8 | REG_NOTEOL << 8)) { + luaL_argerror(L, 3, "invalid flags"); + unreachable; + } + r = LuaReCompileImpl(L, p, f); + n = LuaReSearchImpl(L, r, s, f); + regfree(r); + free(r); + return n; +} + +static int LuaReCompile(lua_State *L) { + int f, e; + 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"); + unreachable; + } + r = LuaReCompileImpl(L, p, f); + u = lua_newuserdata(L, sizeof(regex_t *)); + luaL_setmetatable(L, "regex_t*"); + *u = r; + return 1; +} + +static int LuaReRegexSearch(lua_State *L) { + int f; + regex_t **u; + const char *s; + u = luaL_checkudata(L, 1, "regex_t*"); + 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); +} + +static int LuaReRegexGc(lua_State *L) { + regex_t **u; + u = luaL_checkudata(L, 1, "regex_t*"); + if (*u) { + regfree(*u); + free(*u); + *u = NULL; + } + return 0; +} + +static const luaL_Reg kLuaRe[] = { + {"compile", LuaReCompile}, // + {"search", LuaReSearch}, // + {NULL, NULL}, // +}; + +static const luaL_Reg kLuaReRegexMeth[] = { + {"search", LuaReRegexSearch}, // + {NULL, NULL}, // +}; + +static const luaL_Reg kLuaReRegexMeta[] = { + {"__gc", LuaReRegexGc}, // + {NULL, NULL}, // +}; + +static void LuaReRegex(lua_State *L) { + luaL_newmetatable(L, "regex_t*"); + luaL_setfuncs(L, kLuaReRegexMeta, 0); + luaL_newlibtable(L, kLuaReRegexMeth); + luaL_setfuncs(L, kLuaReRegexMeth, 0); + lua_setfield(L, -2, "__index"); + 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); + return 1; +} diff --git a/tool/net/net.mk b/tool/net/net.mk index ca52719d2..1872b2583 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -86,6 +86,7 @@ o/$(MODE)/tool/net/%.com.dbg: \ o/$(MODE)/tool/net/redbean.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lmaxmind.o \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/largon2.o \ @@ -148,9 +149,11 @@ o/$(MODE)/tool/net/demo/virtualbean.html.zip.o: \ o/$(MODE)/tool/net/redbean-demo.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean.o \ + o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lmaxmind.o \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/largon2.o \ + o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/net.pkg \ o/$(MODE)/tool/net/demo/sql.lua.zip.o \ o/$(MODE)/tool/net/demo/fetch.lua.zip.o \ @@ -235,6 +238,7 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \ o/$(MODE)/tool/net/redbean-unsecure.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean-unsecure.o \ + o/$(MODE)/tool/net/lre.o \ o/$(MODE)/tool/net/lmaxmind.o \ o/$(MODE)/tool/net/lsqlite3.o \ o/$(MODE)/tool/net/net.pkg \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 44b4dc9f8..d18f6f149 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -97,6 +97,7 @@ #include "net/http/url.h" #include "net/https/https.h" #include "third_party/getopt/getopt.h" +#include "third_party/lua/cosmo.h" #include "third_party/lua/lauxlib.h" #include "third_party/lua/ltests.h" #include "third_party/lua/lua.h" @@ -203,11 +204,6 @@ static const char *const kAlpn[] = { NULL, }; -static const char kRegCode[][8] = { - "OK", "NOMATCH", "BADPAT", "COLLATE", "ECTYPE", "EESCAPE", "ESUBREG", - "EBRACK", "EPAREN", "EBRACE", "BADBR", "ERANGE", "ESPACE", "BADRPT", -}; - struct Buffer { size_t n, c; char *p; @@ -1029,79 +1025,6 @@ static void Daemonize(void) { ChangeUser(); } -static nodiscard char *LuaFormatStack(lua_State *L) { - int i, top; - char *b = 0; - top = lua_gettop(L); - for (i = 1; i <= top; i++) { - if (i > 1) appendw(&b, '\n'); - appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i)); - switch (lua_type(L, i)) { - case LUA_TNUMBER: - appendf(&b, "%g", lua_tonumber(L, i)); - break; - case LUA_TSTRING: - appends(&b, lua_tostring(L, i)); - break; - case LUA_TBOOLEAN: - appends(&b, lua_toboolean(L, i) ? "true" : "false"); - break; - case LUA_TNIL: - appends(&b, "nil"); - break; - default: - appendf(&b, "%p", lua_topointer(L, i)); - break; - } - } - return b; -} - -// calling convention for lua stack of L is: -// -2 is function -// -1 is is argument (assuming nargs == 1) -// L will have this after the call -// -1 is error or result (assuming nres == 1) -// @param L is main Lua interpreter -// @note this needs to be reentrant -static int LuaCallWithTrace(lua_State *L, int nargs, int nres, lua_State *C) { - int nresults, status; - bool canyield = !!C; // allow yield if coroutine is provided - if (!C) C = lua_newthread(L); // create a new coroutine if not passed - // move coroutine to the bottom of the stack (including one that is passed) - lua_insert(L, 1); - // make sure that there is enough stack space - if (!lua_checkstack(C, 1 + nargs)) { - lua_pushliteral(L, "too many arguments to resume"); - return LUA_ERRRUN; /* error flag */ - } - // move the function (and arguments) to the top of the coro stack - lua_xmove(L, C, 1 + nargs); - // resume the coroutine thus executing the function - status = lua_resume(C, L, nargs, &nresults); - lua_remove(L, 1); // remove coroutine (still) at the bottom - if (status != LUA_OK && status != LUA_YIELD) { - // move the error message - lua_xmove(C, L, 1); - // replace the error with the traceback on failure - luaL_traceback(L, C, lua_tostring(L, -1), 0); - lua_remove(L, -2); // remove the error message - } else { - if (!lua_checkstack(L, MAX(nresults, nres))) { - lua_pop(C, nresults); /* remove results anyway */ - lua_pushliteral(L, "too many results to resume"); - return LUA_ERRRUN; /* error flag */ - } - lua_xmove(C, L, nresults); // move results to the main stack - // grow the stack in case returned fewer results - // than the caller expects, as lua_resume - // doesn't adjust the stack for needed results - for (; nresults < nres; nresults++) lua_pushnil(L); - if (!canyield) status = LUA_OK; // treat LUA_YIELD the same as LUA_OK - } - return status; -} - static void LogLuaError(char *hook, char *err) { ERRORF("(lua) failed to run %s: %s", hook, err); } @@ -1119,6 +1042,7 @@ static bool LuaRunCode(const char *code) { } static bool LuaOnClientConnection(void) { +#ifndef STATIC bool dropit; uint32_t ip, serverip; uint16_t port, serverport; @@ -1139,9 +1063,13 @@ static bool LuaOnClientConnection(void) { } AssertLuaStackIsEmpty(L); return dropit; +#else + return false; +#endif } static void LuaOnProcessCreate(int pid) { +#ifndef STATIC uint32_t ip, serverip; uint16_t port, serverport; lua_State *L = GL; @@ -1158,9 +1086,11 @@ static void LuaOnProcessCreate(int pid) { lua_pop(L, 1); // pop error } AssertLuaStackIsEmpty(L); +#endif } static void LuaOnProcessDestroy(int pid) { +#ifndef STATIC lua_State *L = GL; lua_getglobal(L, "OnProcessDestroy"); lua_pushinteger(L, pid); @@ -1169,6 +1099,7 @@ static void LuaOnProcessDestroy(int pid) { lua_pop(L, 1); // pop error } AssertLuaStackIsEmpty(L); +#endif } static inline bool IsHookDefined(const char *s) { @@ -1183,6 +1114,7 @@ static inline bool IsHookDefined(const char *s) { } static void CallSimpleHook(const char *s) { +#ifndef STATIC lua_State *L = GL; lua_getglobal(L, s); if (LuaCallWithTrace(L, 0, 0, NULL) != LUA_OK) { @@ -1190,6 +1122,7 @@ static void CallSimpleHook(const char *s) { lua_pop(L, 1); // pop error } AssertLuaStackIsEmpty(L); +#endif } static void CallSimpleHookIfDefined(const char *s) { @@ -1528,7 +1461,8 @@ static void WipeServingKeys(void) { PsksDestroy(); } -bool CertHasCommonName(const mbedtls_x509_crt *cert, const void *s, size_t n) { +static bool CertHasCommonName(const mbedtls_x509_crt *cert, const void *s, + size_t n) { const mbedtls_x509_name *name; for (name = &cert->subject; name; name = name->next) { if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid)) { @@ -3639,69 +3573,6 @@ static wontreturn void LuaThrowTlsError(lua_State *L, const char *s, int r) { unreachable; } -static char *FoldHeader(struct HttpMessage *msg, char *b, int h, size_t *z) { - char *p; - size_t i, n, m; - struct HttpHeader *x; - n = msg->headers[h].b - msg->headers[h].a; - p = xmalloc(n); - memcpy(p, b + msg->headers[h].a, n); - for (i = 0; i < msg->xheaders.n; ++i) { - x = msg->xheaders.p + i; - if (GetHttpHeader(b + x->k.a, x->k.b - x->k.a) == h) { - m = x->v.b - x->v.a; - p = xrealloc(p, n + 2 + m); - memcpy(mempcpy(p + n, ", ", 2), b + x->v.a, m); - n += 2 + m; - } - } - *z = n; - return p; -} - -static void LuaPushLatin1(lua_State *L, const char *s, size_t n) { - char *t; - size_t m; - t = DecodeLatin1(s, n, &m); - lua_pushlstring(L, t, m); - free(t); -} - -static int LuaPushHeader(lua_State *L, struct HttpMessage *m, char *b, int h) { - char *val; - size_t vallen; - if (!kHttpRepeatable[h]) { - LuaPushLatin1(L, b + m->headers[h].a, m->headers[h].b - m->headers[h].a); - } else { - val = FoldHeader(m, b, h, &vallen); - LuaPushLatin1(L, val, vallen); - free(val); - } - return 1; -} - -static int LuaPushHeaders(lua_State *L, struct HttpMessage *m, const char *b) { - char *s; - size_t i, h; - struct HttpHeader *x; - lua_newtable(L); - for (h = 0; h < kHttpHeadersMax; ++h) { - if (m->headers[h].a) { - LuaPushHeader(L, m, b, h); - lua_setfield(L, -2, GetHttpHeaderName(h)); - } - } - for (i = 0; i < m->xheaders.n; ++i) { - x = m->xheaders.p + i; - if ((h = GetHttpHeader(b + x->v.a, x->v.b - x->v.a)) == -1) { - LuaPushLatin1(L, b + x->v.a, x->v.b - x->v.a); - lua_setfield(L, -2, (s = DecodeLatin1(b + x->k.a, x->k.b - x->k.a, 0))); - free(s); - } - } - return 1; -} - static void LogMessage(const char *d, const char *s, size_t n) { size_t n2, n3; char *s2, *s3; @@ -4418,7 +4289,6 @@ static int LuaGetCookie(lua_State *L) { char *cookie = 0, *cookietmpl, *cookieval; OnlyCallDuringRequest(L, "GetCookie"); cookietmpl = gc(xasprintf(" %s=", luaL_checkstring(L, 1))); - if (HasHeader(kHttpCookie)) { appends(&cookie, " "); // prepend space to simplify cookie search appendd(&cookie, HeaderData(kHttpCookie), HeaderLength(kHttpCookie)); @@ -4573,21 +4443,6 @@ static int LuaGetParam(lua_State *L) { return 1; } -static void LuaPushUrlParams(lua_State *L, struct UrlParams *h) { - size_t i; - lua_newtable(L); - for (i = 0; i < h->n; ++i) { - lua_newtable(L); - lua_pushlstring(L, h->p[i].key.p, h->p[i].key.n); - lua_seti(L, -2, 1); - if (h->p[i].val.p) { - lua_pushlstring(L, h->p[i].val.p, h->p[i].val.n); - lua_seti(L, -2, 2); - } - lua_seti(L, -2, i + 1); - } -} - static int LuaGetParams(lua_State *L) { OnlyCallDuringRequest(L, "GetParams"); LuaPushUrlParams(L, &url.params); @@ -4608,33 +4463,6 @@ static int LuaParseParams(lua_State *L) { return 1; } -static void LuaSetUrlView(lua_State *L, struct UrlView *v, const char *k) { - LuaPushUrlView(L, v); - lua_setfield(L, -2, k); -} - -static int LuaParseUrl(lua_State *L) { - void *m; - size_t n; - struct Url h; - const char *p; - p = luaL_checklstring(L, 1, &n); - m = ParseUrl(p, n, &h); - lua_newtable(L); - LuaSetUrlView(L, &h.scheme, "scheme"); - LuaSetUrlView(L, &h.user, "user"); - LuaSetUrlView(L, &h.pass, "pass"); - LuaSetUrlView(L, &h.host, "host"); - LuaSetUrlView(L, &h.port, "port"); - LuaSetUrlView(L, &h.path, "path"); - LuaSetUrlView(L, &h.fragment, "fragment"); - LuaPushUrlParams(L, &h.params); - lua_setfield(L, -2, "params"); - free(h.params.p); - free(m); - return 1; -} - static int LuaParseHost(lua_State *L) { void *m; size_t n; @@ -4650,61 +4478,6 @@ static int LuaParseHost(lua_State *L) { return 1; } -static int LuaEncodeUrl(lua_State *L) { - size_t size; - struct Url h; - int i, j, k, n, m; - const char *data; - if (!lua_isnil(L, 1)) { - bzero(&h, sizeof(h)); - luaL_checktype(L, 1, LUA_TTABLE); - if (lua_getfield(L, 1, "scheme")) - h.scheme.p = lua_tolstring(L, -1, &h.scheme.n); - if (lua_getfield(L, 1, "fragment")) - h.fragment.p = lua_tolstring(L, -1, &h.fragment.n); - if (lua_getfield(L, 1, "user")) h.user.p = lua_tolstring(L, -1, &h.user.n); - if (lua_getfield(L, 1, "pass")) h.pass.p = lua_tolstring(L, -1, &h.pass.n); - if (lua_getfield(L, 1, "host")) h.host.p = lua_tolstring(L, -1, &h.host.n); - if (lua_getfield(L, 1, "port")) h.port.p = lua_tolstring(L, -1, &h.port.n); - if (lua_getfield(L, 1, "path")) h.path.p = lua_tolstring(L, -1, &h.path.n); - if (lua_getfield(L, 1, "params")) { - luaL_checktype(L, -1, LUA_TTABLE); - lua_len(L, -1); - n = lua_tointeger(L, -1); - for (i = -2, k = 0, j = 1; j <= n; ++j) { - if (lua_geti(L, i--, j)) { - luaL_checktype(L, -1, LUA_TTABLE); - lua_len(L, -1); - m = lua_tointeger(L, -1); - lua_pop(L, 1); // remove the table length - if (m >= 1 && lua_geti(L, -1, 1)) { - h.params.p = - xrealloc(h.params.p, ++h.params.n * sizeof(*h.params.p)); - h.params.p[h.params.n - 1].key.p = - lua_tolstring(L, -1, &h.params.p[h.params.n - 1].key.n); - if (m >= 2 && lua_geti(L, -2, 2)) { - h.params.p[h.params.n - 1].val.p = - lua_tolstring(L, -1, &h.params.p[h.params.n - 1].val.n); - } else { - h.params.p[h.params.n - 1].val.p = 0; - h.params.p[h.params.n - 1].val.n = 0; - } - } - i--; - } - i--; - } - } - data = EncodeUrl(&h, &size); - lua_pushlstring(L, data, size); - free(h.params.p); - free(data); - } else { - lua_pushnil(L); - } - return 1; -} - static int LuaWrite(lua_State *L) { size_t size; const char *data; @@ -4923,126 +4696,6 @@ static int LuaGetHttpReason(lua_State *L) { return 1; } -static int EncodeJsonData(lua_State *L, char **buf, int level, - char *numformat) { - size_t idx = -1; - size_t tbllen, buflen; - bool isarray; - int t = lua_type(L, idx); - if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); - if (LUA_TSTRING == t) { - appendw(buf, '"'); - appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, idx), -1, 0))); - appendw(buf, '"'); - } else if (LUA_TNUMBER == t) { - appendf(buf, numformat, lua_tonumber(L, idx)); - } else if (LUA_TBOOLEAN == t) { - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - } else if (LUA_TTABLE == t) { - tbllen = lua_rawlen(L, idx); - // encode tables with numeric indices and empty tables as arrays - isarray = tbllen > 0 || // integer keys present - (lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys - (lua_pop(L, 2), false); // pop key/value pushed by lua_next - appendw(buf, isarray ? '[' : '{'); - if (isarray) { - for (int i = 1; i <= tbllen; i++) { - if (i > 1) appendw(buf, ','); - lua_rawgeti(L, -1, i); // table/-2, value/-1 - EncodeJsonData(L, buf, level - 1, numformat); - lua_pop(L, 1); - } - } else { - int i = 1; - lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { - if (!lua_isstring(L, -2)) - return luaL_argerror(L, 1, "expected number or string as key value"); - if (i++ > 1) appendw(buf, ','); - appendw(buf, '"'); - if (lua_type(L, -2) == LUA_TSTRING) { - appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, -2), -1, 0))); - } else { - // we'd still prefer to use lua_tostring on a numeric index, but can't - // use it in-place, as it breaks lua_next (changes numeric key to a - // string) - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - appends(buf, lua_tostring(L, idx)); // use the copy - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 - } - appendw(buf, '"' | ':' << 010); - EncodeJsonData(L, buf, level - 1, numformat); - lua_pop(L, 1); // table/-2, key/-1 - } - // stack: table/-1, as the key was popped by lua_next - } - appendw(buf, isarray ? ']' : '}'); - } else if (LUA_TNIL == t) { - appendd(buf, "null", 4); - } else { - return luaL_argerror(L, 1, "can't serialize value of this type"); - } - return 0; -} - -static void EscapeLuaString(char *s, size_t len, char **buf) { - appendw(buf, '"'); - for (size_t i = 0; i < len; i++) { - if (s[i] == '\\' || s[i] == '\"' || s[i] == '\n' || s[i] == '\0' || - s[i] == '\r') { - appendw(buf, '\\' | 'x' << 010 | - "0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 | - "0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030); - } else { - appendd(buf, s + i, 1); - } - } - appendw(buf, '"'); -} - -static int EncodeLuaData(lua_State *L, char **buf, int level, char *numformat) { - size_t idx = -1; - size_t tbllen, buflen, slen; - char *s; - int ktype; - int t = lua_type(L, idx); - if (level < 0) return luaL_argerror(L, 1, "too many nested tables"); - if (LUA_TSTRING == t) { - s = lua_tolstring(L, idx, &slen); - EscapeLuaString(s, slen, buf); - } else if (LUA_TNUMBER == t) { - appendf(buf, numformat, lua_tonumber(L, idx)); - } else if (LUA_TBOOLEAN == t) { - appends(buf, lua_toboolean(L, idx) ? "true" : "false"); - } else if (LUA_TTABLE == t) { - appendw(buf, '{'); - int i = 0; - lua_pushnil(L); // push the first key - while (lua_next(L, -2) != 0) { - ktype = lua_type(L, -2); - if (ktype == LUA_TTABLE) - return luaL_argerror(L, 1, "can't serialize key of this type"); - if (i++ > 0) appendd(buf, ",", 1); - if (ktype != LUA_TNUMBER || floor(lua_tonumber(L, -2)) != i) { - appendw(buf, '['); - lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1 - EncodeLuaData(L, buf, level, numformat); - lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1 - appendw(buf, ']' | '=' << 010); - } - EncodeLuaData(L, buf, level - 1, numformat); - lua_pop(L, 1); // table/-2, key/-1 - } - // stack: table/-1, as the key was popped by lua_next - appendw(buf, '}'); - } else if (LUA_TNIL == t) - appendd(buf, "nil", 3); - else { - return luaL_argerror(L, 1, "can't serialize value of this type"); - } - return 0; -} - static int LuaEncodeSmth(lua_State *L, int Encoder(lua_State *, char **, int, char *)) { int useoutput = false; @@ -5072,11 +4725,11 @@ static int LuaEncodeSmth(lua_State *L, } static int LuaEncodeJson(lua_State *L) { - return LuaEncodeSmth(L, EncodeJsonData); + return LuaEncodeSmth(L, LuaEncodeJsonData); } static int LuaEncodeLua(lua_State *L) { - return LuaEncodeSmth(L, EncodeLuaData); + return LuaEncodeSmth(L, LuaEncodeLuaData); } static int LuaEncodeLatin1(lua_State *L) { @@ -5255,7 +4908,6 @@ static int LuaProgramPidPath(lua_State *L) { } static int LuaProgramSslPresharedKey(lua_State *L) { -#ifndef UNSECURE struct Psk psk; size_t n1, n2, i; const char *p1, *p2; @@ -5282,7 +4934,6 @@ static int LuaProgramSslPresharedKey(lua_State *L) { } psks.p = realloc(psks.p, ++psks.n * sizeof(*psks.p)); psks.p[psks.n - 1] = psk; -#endif return 0; } @@ -5299,24 +4950,20 @@ static int LuaProgramSslCiphersuite(lua_State *L) { } static int LuaProgramPrivateKey(lua_State *L) { -#ifndef UNSECURE size_t n; const char *p; OnlyCallFromInitLua(L, "ProgramPrivateKey"); p = luaL_checklstring(L, 1, &n); ProgramPrivateKey(p, n); -#endif return 0; } static int LuaProgramCertificate(lua_State *L) { -#ifndef UNSECURE size_t n; const char *p; OnlyCallFromInitLua(L, "ProgramCertificate"); p = luaL_checklstring(L, 1, &n); ProgramCertificate(p, n); -#endif return 0; } @@ -5577,150 +5224,12 @@ static int LuaGetHostOs(lua_State *L) { return 1; } -static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { - lua_pushinteger(L, v); - lua_setfield(L, -2, k); -} - static int LuaLaunchBrowser(lua_State *L) { OnlyCallFromInitLua(L, "LaunchBrowser"); launchbrowser = strdup(luaL_optstring(L, 1, "/")); return 0; } -static regex_t *LuaReCompileImpl(lua_State *L, const char *p, int f) { - int e; - regex_t *r; - r = xmalloc(sizeof(regex_t)); - 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; - } - return r; -} - -static int LuaReSearchImpl(lua_State *L, regex_t *r, const char *s, int f) { - int 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) { - for (i = 0; i < n; ++i) { - lua_pushlstring(L, s + m[i].rm_so, m[i].rm_eo - m[i].rm_so); - } - } else { - n = 0; - } - free(m); - return n; -} - -static int LuaReSearch(lua_State *L) { - regex_t *r; - int i, e, n, f; - const char *p, *s; - p = luaL_checkstring(L, 1); - s = luaL_checkstring(L, 2); - f = luaL_optinteger(L, 3, 0); - if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB | - REG_NOTBOL << 8 | REG_NOTEOL << 8)) { - luaL_argerror(L, 3, "invalid flags"); - unreachable; - } - r = LuaReCompileImpl(L, p, f); - n = LuaReSearchImpl(L, r, s, f); - regfree(r); - free(r); - return n; -} - -static int LuaReCompile(lua_State *L) { - int f, e; - 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"); - unreachable; - } - r = LuaReCompileImpl(L, p, f); - u = lua_newuserdata(L, sizeof(regex_t *)); - luaL_setmetatable(L, "regex_t*"); - *u = r; - return 1; -} - -static int LuaReRegexSearch(lua_State *L) { - int f; - regex_t **u; - const char *s; - u = luaL_checkudata(L, 1, "regex_t*"); - 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); -} - -static int LuaReRegexGc(lua_State *L) { - regex_t **u; - u = luaL_checkudata(L, 1, "regex_t*"); - if (*u) { - regfree(*u); - free(*u); - *u = NULL; - } - return 0; -} - -static const luaL_Reg kLuaRe[] = { - {"compile", LuaReCompile}, // - {"search", LuaReSearch}, // - {NULL, NULL}, // -}; - -static const luaL_Reg kLuaReRegexMeth[] = { - {"search", LuaReRegexSearch}, // - {NULL, NULL}, // -}; - -static const luaL_Reg kLuaReRegexMeta[] = { - {"__gc", LuaReRegexGc}, // - {NULL, NULL}, // -}; - -static void LuaReRegex(lua_State *L) { - luaL_newmetatable(L, "regex_t*"); - luaL_setfuncs(L, kLuaReRegexMeta, 0); - luaL_newlibtable(L, kLuaReRegexMeth); - luaL_setfuncs(L, kLuaReRegexMeth, 0); - lua_setfield(L, -2, "__index"); - lua_pop(L, 1); -} - -static 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); - return 1; -} - static bool LuaRunAsset(const char *path, bool mandatory) { struct Asset *a; const char *code; @@ -5746,159 +5255,159 @@ static bool LuaRunAsset(const char *path, bool mandatory) { } static const luaL_Reg kLuaFuncs[] = { - {"Bsf", LuaBsf}, // - {"Bsr", LuaBsr}, // - {"CategorizeIp", LuaCategorizeIp}, // - {"Crc32", LuaCrc32}, // - {"Crc32c", LuaCrc32c}, // - {"DecodeBase64", LuaDecodeBase64}, // - {"DecodeLatin1", LuaDecodeLatin1}, // - {"EncodeBase64", LuaEncodeBase64}, // - {"EncodeJson", LuaEncodeJson}, // - {"EncodeLatin1", LuaEncodeLatin1}, // - {"EncodeLua", LuaEncodeLua}, // - {"EncodeUrl", LuaEncodeUrl}, // - {"EscapeFragment", LuaEscapeFragment}, // - {"EscapeHost", LuaEscapeHost}, // - {"EscapeHtml", LuaEscapeHtml}, // - {"EscapeIp", LuaEscapeIp}, // - {"EscapeLiteral", LuaEscapeLiteral}, // - {"EscapeParam", LuaEscapeParam}, // - {"EscapePass", LuaEscapePass}, // - {"EscapePath", LuaEscapePath}, // - {"EscapeSegment", LuaEscapeSegment}, // - {"EscapeUser", LuaEscapeUser}, // - {"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, // + {"Bsf", LuaBsf}, // + {"Bsr", LuaBsr}, // + {"CategorizeIp", LuaCategorizeIp}, // + {"Crc32", LuaCrc32}, // + {"Crc32c", LuaCrc32c}, // + {"DecodeBase64", LuaDecodeBase64}, // + {"DecodeLatin1", LuaDecodeLatin1}, // + {"EncodeBase64", LuaEncodeBase64}, // + {"EncodeJson", LuaEncodeJson}, // + {"EncodeLatin1", LuaEncodeLatin1}, // + {"EncodeLua", LuaEncodeLua}, // + {"EncodeUrl", LuaEncodeUrl}, // + {"EscapeFragment", LuaEscapeFragment}, // + {"EscapeHost", LuaEscapeHost}, // + {"EscapeHtml", LuaEscapeHtml}, // + {"EscapeIp", LuaEscapeIp}, // + {"EscapeLiteral", LuaEscapeLiteral}, // + {"EscapeParam", LuaEscapeParam}, // + {"EscapePass", LuaEscapePass}, // + {"EscapePath", LuaEscapePath}, // + {"EscapeSegment", LuaEscapeSegment}, // + {"EscapeUser", LuaEscapeUser}, // + {"FormatHttpDateTime", LuaFormatHttpDateTime}, // + {"FormatIp", LuaFormatIp}, // + {"GetAssetComment", LuaGetAssetComment}, // + {"GetAssetMode", LuaGetAssetMode}, // + {"GetAssetSize", LuaGetAssetSize}, // + {"GetBody", LuaGetBody}, // + {"GetClientAddr", LuaGetClientAddr}, // + {"GetComment", LuaGetAssetComment}, // + {"GetCookie", LuaGetCookie}, // + {"GetCryptoHash", LuaGetCryptoHash}, // + {"GetDate", LuaGetDate}, // + {"GetEffectivePath", LuaGetEffectivePath}, // + {"GetFragment", LuaGetFragment}, // + {"GetHeader", LuaGetHeader}, // + {"GetHeaders", LuaGetHeaders}, // + {"GetHost", LuaGetHost}, // + {"GetHostOs", LuaGetHostOs}, // + {"GetHttpReason", LuaGetHttpReason}, // + {"GetHttpVersion", LuaGetHttpVersion}, // + {"GetLastModifiedTime", LuaGetLastModifiedTime}, // + {"GetLogLevel", LuaGetLogLevel}, // + {"GetMethod", LuaGetMethod}, // + {"GetMonospaceWidth", LuaGetMonospaceWidth}, // + {"GetParam", LuaGetParam}, // + {"GetParams", LuaGetParams}, // + {"GetPass", LuaGetPass}, // + {"GetPath", LuaGetPath}, // + {"GetPayload", LuaGetBody}, // + {"GetPort", LuaGetPort}, // + {"GetRandomBytes", LuaGetRandomBytes}, // + {"GetRedbeanVersion", LuaGetRedbeanVersion}, // + {"GetRemoteAddr", LuaGetRemoteAddr}, // + {"GetScheme", LuaGetScheme}, // + {"GetServerAddr", LuaGetServerAddr}, // + {"GetStatus", LuaGetStatus}, // + {"GetTime", LuaGetTime}, // + {"GetUrl", LuaGetUrl}, // + {"GetUser", LuaGetUser}, // + {"GetVersion", LuaGetHttpVersion}, // + {"GetZipPaths", LuaGetZipPaths}, // + {"HasControlCodes", LuaHasControlCodes}, // + {"HasParam", LuaHasParam}, // + {"HidePath", LuaHidePath}, // + {"IndentLines", LuaIndentLines}, // + {"IsAcceptableHost", LuaIsAcceptableHost}, // + {"IsAcceptablePath", LuaIsAcceptablePath}, // + {"IsAcceptablePort", LuaIsAcceptablePort}, // + {"IsCompressed", LuaIsCompressed}, // + {"IsDaemon", LuaIsDaemon}, // + {"IsHeaderRepeatable", LuaIsHeaderRepeatable}, // + {"IsHiddenPath", LuaIsHiddenPath}, // + {"IsLoopbackClient", LuaIsLoopbackClient}, // + {"IsLoopbackIp", LuaIsLoopbackIp}, // + {"IsPrivateIp", LuaIsPrivateIp}, // + {"IsPublicIp", LuaIsPublicIp}, // + {"IsReasonablePath", LuaIsReasonablePath}, // + {"IsValidHttpToken", LuaIsValidHttpToken}, // + {"LaunchBrowser", LuaLaunchBrowser}, // + {"LoadAsset", LuaLoadAsset}, // + {"Log", LuaLog}, // + {"Md5", LuaMd5}, // + {"ParseHost", LuaParseHost}, // + {"ParseHttpDateTime", LuaParseHttpDateTime}, // + {"ParseIp", LuaParseIp}, // + {"ParseParams", LuaParseParams}, // + {"ParseUrl", LuaParseUrl}, // + {"Popcnt", LuaPopcnt}, // + {"ProgramAddr", LuaProgramAddr}, // + {"ProgramBrand", LuaProgramBrand}, // + {"ProgramCache", LuaProgramCache}, // + {"ProgramCertificate", LuaProgramCertificate}, // + {"ProgramDirectory", LuaProgramDirectory}, // + {"ProgramGid", LuaProgramGid}, // + {"ProgramHeader", LuaProgramHeader}, // + {"ProgramLogBodies", LuaProgramLogBodies}, // + {"ProgramLogMessages", LuaProgramLogMessages}, // + {"ProgramLogPath", LuaProgramLogPath}, // + {"ProgramPidPath", LuaProgramPidPath}, // + {"ProgramPort", LuaProgramPort}, // + {"ProgramPrivateKey", LuaProgramPrivateKey}, // + {"ProgramRedirect", LuaProgramRedirect}, // + {"ProgramTimeout", LuaProgramTimeout}, // + {"ProgramUid", LuaProgramUid}, // + {"ProgramUniprocess", LuaProgramUniprocess}, // + {"Route", LuaRoute}, // + {"RouteHost", LuaRouteHost}, // + {"RoutePath", LuaRoutePath}, // + {"ServeAsset", LuaServeAsset}, // + {"ServeError", LuaServeError}, // + {"ServeIndex", LuaServeIndex}, // + {"ServeListing", LuaServeListing}, // + {"ServeRedirect", LuaServeRedirect}, // + {"ServeStatusz", LuaServeStatusz}, // + {"SetCookie", LuaSetCookie}, // + {"SetHeader", LuaSetHeader}, // + {"SetLogLevel", LuaSetLogLevel}, // + {"SetStatus", LuaSetStatus}, // + {"Sha1", LuaSha1}, // + {"Sha224", LuaSha224}, // + {"Sha256", LuaSha256}, // + {"Sha384", LuaSha384}, // + {"Sha512", LuaSha512}, // + {"Sleep", LuaSleep}, // + {"Slurp", LuaSlurp}, // + {"StoreAsset", LuaStoreAsset}, // + {"Underlong", LuaUnderlong}, // + {"VisualizeControlCodes", LuaVisualizeControlCodes}, // + {"Write", LuaWrite}, // + {"bsf", LuaBsf}, // + {"bsr", LuaBsr}, // + {"crc32", LuaCrc32}, // + {"crc32c", LuaCrc32c}, // + {"popcnt", LuaPopcnt}, // +#ifndef UNSECURE {"Fetch", LuaFetch}, // - {"FormatHttpDateTime", LuaFormatHttpDateTime}, // - {"FormatIp", LuaFormatIp}, // - {"GetAssetComment", LuaGetAssetComment}, // - {"GetAssetMode", LuaGetAssetMode}, // - {"GetAssetSize", LuaGetAssetSize}, // - {"GetBody", LuaGetBody}, // - {"GetClientAddr", LuaGetClientAddr}, // - {"GetComment", LuaGetAssetComment}, // - {"GetCookie", LuaGetCookie}, // - {"GetCryptoHash", LuaGetCryptoHash}, // - {"GetDate", LuaGetDate}, // - {"GetEffectivePath", LuaGetEffectivePath}, // - {"GetFragment", LuaGetFragment}, // - {"GetHeader", LuaGetHeader}, // - {"GetHeaders", LuaGetHeaders}, // - {"GetHost", LuaGetHost}, // - {"GetHostOs", LuaGetHostOs}, // - {"GetHttpReason", LuaGetHttpReason}, // - {"GetHttpVersion", LuaGetHttpVersion}, // - {"GetLastModifiedTime", LuaGetLastModifiedTime}, // - {"GetLogLevel", LuaGetLogLevel}, // - {"GetMethod", LuaGetMethod}, // - {"GetMonospaceWidth", LuaGetMonospaceWidth}, // - {"GetParam", LuaGetParam}, // - {"GetParams", LuaGetParams}, // - {"GetPass", LuaGetPass}, // - {"GetPath", LuaGetPath}, // - {"GetPayload", LuaGetBody}, // - {"GetPort", LuaGetPort}, // - {"GetRandomBytes", LuaGetRandomBytes}, // - {"GetRedbeanVersion", LuaGetRedbeanVersion}, // - {"GetRemoteAddr", LuaGetRemoteAddr}, // - {"GetScheme", LuaGetScheme}, // - {"GetServerAddr", LuaGetServerAddr}, // + {"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, // {"GetSslIdentity", LuaGetSslIdentity}, // - {"GetStatus", LuaGetStatus}, // - {"GetTime", LuaGetTime}, // - {"GetUrl", LuaGetUrl}, // - {"GetUser", LuaGetUser}, // - {"GetVersion", LuaGetHttpVersion}, // - {"GetZipPaths", LuaGetZipPaths}, // - {"HasControlCodes", LuaHasControlCodes}, // - {"HasParam", LuaHasParam}, // - {"HidePath", LuaHidePath}, // - {"IndentLines", LuaIndentLines}, // - {"IsAcceptableHost", LuaIsAcceptableHost}, // - {"IsAcceptablePath", LuaIsAcceptablePath}, // - {"IsAcceptablePort", LuaIsAcceptablePort}, // - {"IsCompressed", LuaIsCompressed}, // - {"IsDaemon", LuaIsDaemon}, // - {"IsHeaderRepeatable", LuaIsHeaderRepeatable}, // - {"IsHiddenPath", LuaIsHiddenPath}, // - {"IsLoopbackClient", LuaIsLoopbackClient}, // - {"IsLoopbackIp", LuaIsLoopbackIp}, // - {"IsPrivateIp", LuaIsPrivateIp}, // - {"IsPublicIp", LuaIsPublicIp}, // - {"IsReasonablePath", LuaIsReasonablePath}, // - {"IsValidHttpToken", LuaIsValidHttpToken}, // - {"LaunchBrowser", LuaLaunchBrowser}, // - {"LoadAsset", LuaLoadAsset}, // - {"Log", LuaLog}, // - {"Md5", LuaMd5}, // - {"ParseHost", LuaParseHost}, // - {"ParseHttpDateTime", LuaParseHttpDateTime}, // - {"ParseIp", LuaParseIp}, // - {"ParseParams", LuaParseParams}, // - {"ParseUrl", LuaParseUrl}, // - {"Popcnt", LuaPopcnt}, // - {"ProgramAddr", LuaProgramAddr}, // - {"ProgramBrand", LuaProgramBrand}, // - {"ProgramCache", LuaProgramCache}, // - {"ProgramCertificate", LuaProgramCertificate}, // - {"ProgramDirectory", LuaProgramDirectory}, // - {"ProgramGid", LuaProgramGid}, // - {"ProgramHeader", LuaProgramHeader}, // - {"ProgramLogBodies", LuaProgramLogBodies}, // - {"ProgramLogMessages", LuaProgramLogMessages}, // - {"ProgramLogPath", LuaProgramLogPath}, // - {"ProgramPidPath", LuaProgramPidPath}, // - {"ProgramPort", LuaProgramPort}, // - {"ProgramPrivateKey", LuaProgramPrivateKey}, // - {"ProgramRedirect", LuaProgramRedirect}, // {"ProgramSslCiphersuite", LuaProgramSslCiphersuite}, // {"ProgramSslClientVerify", LuaProgramSslClientVerify}, // {"ProgramSslCompression", LuaProgramSslCompression}, // - {"ProgramSslInit", LuaProgramSslInit}, // {"ProgramSslFetchVerify", LuaProgramSslFetchVerify}, // + {"ProgramSslInit", LuaProgramSslInit}, // {"ProgramSslPresharedKey", LuaProgramSslPresharedKey}, // {"ProgramSslTicketLifetime", LuaProgramSslTicketLifetime}, // - {"ProgramTimeout", LuaProgramTimeout}, // - {"ProgramUid", LuaProgramUid}, // - {"ProgramUniprocess", LuaProgramUniprocess}, // - {"Route", LuaRoute}, // - {"RouteHost", LuaRouteHost}, // - {"RoutePath", LuaRoutePath}, // - {"ServeAsset", LuaServeAsset}, // - {"ServeError", LuaServeError}, // - {"ServeIndex", LuaServeIndex}, // - {"ServeListing", LuaServeListing}, // - {"ServeRedirect", LuaServeRedirect}, // - {"ServeStatusz", LuaServeStatusz}, // - {"SetCookie", LuaSetCookie}, // - {"SetHeader", LuaSetHeader}, // - {"SetLogLevel", LuaSetLogLevel}, // - {"SetStatus", LuaSetStatus}, // - {"Sha1", LuaSha1}, // - {"Sha224", LuaSha224}, // - {"Sha256", LuaSha256}, // - {"Sha384", LuaSha384}, // - {"Sha512", LuaSha512}, // - {"Sleep", LuaSleep}, // - {"Slurp", LuaSlurp}, // - {"StoreAsset", LuaStoreAsset}, // - {"Underlong", LuaUnderlong}, // - {"VisualizeControlCodes", LuaVisualizeControlCodes}, // - {"Write", LuaWrite}, // - {"bsf", LuaBsf}, // - {"bsr", LuaBsr}, // - {"crc32", LuaCrc32}, // - {"crc32c", LuaCrc32c}, // - {"popcnt", LuaPopcnt}, // +#endif }; -extern int luaopen_lsqlite3(lua_State *); -extern int LuaMaxmind(lua_State *); - -#ifndef UNSECURE -extern int luaopen_argon2(lua_State *); -#endif +int LuaMaxmind(lua_State *); +int LuaRe(lua_State *); +int luaopen_argon2(lua_State *); +int luaopen_lsqlite3(lua_State *); static const luaL_Reg kLuaLibs[] = { {"re", LuaRe}, // @@ -7315,8 +6824,6 @@ static void GetOpts(int argc, char *argv[]) { switch (opt) { CASE('v', ++__log_level); CASE('s', --__log_level); - CASE('V', ++mbedtls_debug_threshold); - CASE('B', suiteb = true); CASE('f', funtrace = true); CASE('b', logbodies = true); CASE('z', printport = true); @@ -7325,10 +6832,6 @@ static void GetOpts(int argc, char *argv[]) { CASE('u', uniprocess = true); CASE('g', loglatency = true); CASE('m', logmessages = true); - CASE('k', sslfetchverify = false); - CASE('j', sslclientverify = true); - CASE('e', LuaRunCode(optarg)); - CASE('A', storeasset = true; StorePath(optarg)); CASE('l', ProgramAddr(optarg)); CASE('H', ProgramHeader(optarg)); CASE('L', ProgramLogPath(optarg)); @@ -7342,9 +6845,17 @@ static void GetOpts(int argc, char *argv[]) { CASE('r', ProgramRedirectArg(307, optarg)); CASE('t', ProgramTimeout(ParseInt(optarg))); CASE('h', PrintUsage(stdout, EXIT_SUCCESS)); - CASE('T', ProgramSslTicketLifetime(ParseInt(optarg))); CASE('M', ProgramMaxPayloadSize(ParseInt(optarg))); +#ifndef STATIC + CASE('e', LuaRunCode(optarg)); + CASE('A', storeasset = true; StorePath(optarg)); +#endif #ifndef UNSECURE + CASE('B', suiteb = true); + CASE('V', ++mbedtls_debug_threshold); + CASE('k', sslfetchverify = false); + CASE('j', sslclientverify = true); + CASE('T', ProgramSslTicketLifetime(ParseInt(optarg))); CASE('C', ProgramFile(optarg, ProgramCertificate)); CASE('K', ProgramFile(optarg, ProgramPrivateKey)); #endif @@ -7413,7 +6924,6 @@ void RedBean(int argc, char *argv[]) { } int main(int argc, char *argv[]) { - int fd; if (!IsTiny()) { setenv("GDB", "", true); ShowCrashReports();