From bb815eafafe89ecc0133fe85f9f51ba820467c51 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 30 Jul 2024 09:14:57 -0700 Subject: [PATCH] Update Musl Libc code We now have implement all of Musl's localization code, the same way that Musl implements localization. You may need setlocale(LC_ALL, "C.UTF-8"), just in case anything stops working as expected. --- ape/BUILD.mk | 2 - libc/calls/finddebugbinary.c | 2 + libc/isystem/langinfo.h | 2 - libc/log/addr2linepath.c | 2 +- libc/runtime/enable_tls.c | 3 +- libc/runtime/straceinit.greg.c | 6 +- libc/runtime/zipos-get.c | 2 +- libc/sock/BUILD.mk | 1 - libc/str/c32rtomb.c | 23 - libc/str/freelocale.c | 23 - libc/str/langinfo.h | 5 +- libc/str/locale.c | 13 +- libc/str/locale.h | 16 - libc/str/locale.internal.h | 52 + libc/str/mb.internal.h | 17 - libc/str/mblen.c | 23 - libc/str/mbrlen.c | 26 - libc/str/mbsinit.c | 23 - libc/str/mbsrtowcs.c | 147 - libc/str/mbstowcs.c | 23 - libc/str/newlocale.c | 25 - libc/str/setlocale.c | 43 - libc/str/str.h | 51 +- libc/str/wcsrtombs.c | 90 - libc/str/wcstombs.c | 23 - libc/str/wctomb.c | 26 - libc/thread/mktls.c | 1 - libc/thread/posixthread.internal.h | 3 + libc/thread/pthread_create.c | 2 + libc/thread/tls.h | 2 +- libc/x/BUILD.mk | 5 +- libc/{sock => x}/syslog.c | 0 test/libc/str/BUILD.mk | 11 +- test/libc/str/regex_test.c | 5 + test/libc/str/setlocale_test.c | 30 - test/libc/time/BUILD.mk | 3 +- test/libc/tinymath/BUILD.mk | 11 +- test/posix/BUILD.mk | 3 +- test/posix/iconv_utf8_utf16_test.c | 173 + test/posix/iconv_utf8_utf32_test.c | 172 + third_party/awk/BUILD.mk | 3 +- third_party/chibicc/BUILD.mk | 1 + third_party/less/BUILD.mk | 3 +- third_party/lua/BUILD.mk | 3 +- third_party/musl/BUILD.mk | 3 +- third_party/musl/__mo_lookup.c | 72 + third_party/musl/__month_to_secs.c | 10 + third_party/musl/__secs_to_tm.c | 82 + third_party/musl/__tm_to_secs.c | 24 + third_party/musl/__year_to_secs.c | 47 + third_party/musl/asctime.c | 10 + third_party/musl/asctime_r.c | 52 + {libc/str => third_party/musl}/btowc.c | 19 +- {libc/str => third_party/musl}/c16rtomb.c | 71 +- third_party/musl/c32rtomb.c | 35 + third_party/musl/catclose.c | 1 + third_party/musl/catgets.c | 1 + third_party/musl/catopen.c | 1 + third_party/{tz => musl}/ctime.c | 0 third_party/{tz => musl}/ctime_r.c | 0 .../musl/duplocale.c | 54 +- third_party/musl/fnmatch.c | 567 ++- .../musl/freelocale.c | 43 +- third_party/musl/glob.c | 528 ++- {libc/stdio => third_party/musl}/iconv.c | 26 +- {libc/str => third_party/musl}/langinfo.c | 17 +- third_party/musl/lctrans.c | 46 + third_party/musl/locale_map.c | 137 + third_party/musl/mblen.c | 34 + third_party/musl/mbrlen.c | 35 + third_party/musl/mbrtoc16.c | 58 + {libc/str => third_party/musl}/mbrtoc32.c | 30 +- third_party/musl/mbrtowc.c | 81 + third_party/musl/mbsinit.c | 34 + third_party/musl/mbsnrtowcs.c | 83 + third_party/musl/mbsrtowcs.c | 150 + third_party/musl/mbstowcs.c | 35 + {libc/str => third_party/musl}/mbtowc.c | 96 +- third_party/musl/multibyte.c | 53 + third_party/musl/multibyte.h | 26 + third_party/musl/newlocale.c | 94 + .../musl/setlocale.c | 136 +- third_party/musl/strfmon.c | 21 +- third_party/musl/strftime.c | 313 ++ third_party/musl/strptime.c | 453 +- third_party/musl/time_impl.h | 19 + third_party/musl/uselocale.c | 39 + {libc/str => third_party/musl}/wcrtomb.c | 74 +- .../mbrtowc.c => third_party/musl/wcsftime.c | 126 +- third_party/musl/wcsnrtombs.c | 63 + libc/str/mb.c => third_party/musl/wcsrtombs.c | 81 +- third_party/musl/wcstombs.c | 35 + {libc/str => third_party/musl}/wctob.c | 23 +- third_party/musl/wctomb.c | 36 + third_party/pcre/BUILD.mk | 3 +- third_party/python/BUILD.mk | 8 +- third_party/python/Include/pyatomic.h | 1 + third_party/python/Lib/test/test_re.py | 4 +- third_party/python/Modules/socketmodule.c | 24 +- third_party/python/Modules/socketmodule.h | 1 + .../python/Objects/unicodeobject-deadcode.c | 430 -- third_party/python/Objects/unicodeobject.c | 31 + third_party/python/pyconfig.h | 12 +- third_party/regex/BUILD.mk | 3 +- third_party/regex/regcomp.c | 3992 +++++++++-------- third_party/regex/regerror.c | 88 +- third_party/regex/regexec.c | 1151 ++--- third_party/regex/tre-mem.c | 147 +- third_party/regex/tre.inc | 189 +- third_party/sed/BUILD.mk | 1 + third_party/tz/asctime.c | 135 - third_party/tz/strftime.c | 646 --- third_party/unzip/BUILD.mk | 3 +- third_party/zip/BUILD.mk | 3 +- tool/decode/BUILD.mk | 1 + tool/lambda/BUILD.mk | 1 + 116 files changed, 6525 insertions(+), 5523 deletions(-) delete mode 100644 libc/str/c32rtomb.c delete mode 100644 libc/str/freelocale.c create mode 100644 libc/str/locale.internal.h delete mode 100644 libc/str/mb.internal.h delete mode 100644 libc/str/mblen.c delete mode 100644 libc/str/mbrlen.c delete mode 100644 libc/str/mbsinit.c delete mode 100644 libc/str/mbsrtowcs.c delete mode 100644 libc/str/mbstowcs.c delete mode 100644 libc/str/newlocale.c delete mode 100644 libc/str/setlocale.c delete mode 100644 libc/str/wcsrtombs.c delete mode 100644 libc/str/wcstombs.c delete mode 100644 libc/str/wctomb.c rename libc/{sock => x}/syslog.c (100%) delete mode 100644 test/libc/str/setlocale_test.c create mode 100644 test/posix/iconv_utf8_utf16_test.c create mode 100644 test/posix/iconv_utf8_utf32_test.c create mode 100644 third_party/musl/__mo_lookup.c create mode 100644 third_party/musl/__month_to_secs.c create mode 100644 third_party/musl/__secs_to_tm.c create mode 100644 third_party/musl/__tm_to_secs.c create mode 100644 third_party/musl/__year_to_secs.c create mode 100644 third_party/musl/asctime.c create mode 100644 third_party/musl/asctime_r.c rename {libc/str => third_party/musl}/btowc.c (86%) rename {libc/str => third_party/musl}/c16rtomb.c (76%) create mode 100644 third_party/musl/c32rtomb.c rename third_party/{tz => musl}/ctime.c (100%) rename third_party/{tz => musl}/ctime_r.c (100%) rename libc/str/wcsnrtombs.c => third_party/musl/duplocale.c (74%) rename libc/str/mbrtoc16.c => third_party/musl/freelocale.c (72%) rename {libc/stdio => third_party/musl}/iconv.c (97%) rename {libc/str => third_party/musl}/langinfo.c (93%) create mode 100644 third_party/musl/lctrans.c create mode 100644 third_party/musl/locale_map.c create mode 100644 third_party/musl/mblen.c create mode 100644 third_party/musl/mbrlen.c create mode 100644 third_party/musl/mbrtoc16.c rename {libc/str => third_party/musl}/mbrtoc32.c (81%) create mode 100644 third_party/musl/mbrtowc.c create mode 100644 third_party/musl/mbsinit.c create mode 100644 third_party/musl/mbsnrtowcs.c create mode 100644 third_party/musl/mbsrtowcs.c create mode 100644 third_party/musl/mbstowcs.c rename {libc/str => third_party/musl}/mbtowc.c (68%) create mode 100644 third_party/musl/multibyte.c create mode 100644 third_party/musl/multibyte.h create mode 100644 third_party/musl/newlocale.c rename libc/str/mbsnrtowcs.c => third_party/musl/setlocale.c (56%) create mode 100644 third_party/musl/strftime.c create mode 100644 third_party/musl/time_impl.h create mode 100644 third_party/musl/uselocale.c rename {libc/str => third_party/musl}/wcrtomb.c (70%) rename libc/str/mbrtowc.c => third_party/musl/wcsftime.c (60%) create mode 100644 third_party/musl/wcsnrtombs.c rename libc/str/mb.c => third_party/musl/wcsrtombs.c (69%) create mode 100644 third_party/musl/wcstombs.c rename {libc/str => third_party/musl}/wctob.c (86%) create mode 100644 third_party/musl/wctomb.c delete mode 100644 third_party/python/Objects/unicodeobject-deadcode.c delete mode 100644 third_party/tz/asctime.c delete mode 100644 third_party/tz/strftime.c diff --git a/ape/BUILD.mk b/ape/BUILD.mk index 25542e0d5..7265b222a 100644 --- a/ape/BUILD.mk +++ b/ape/BUILD.mk @@ -246,8 +246,6 @@ o/$(MODE)/ape: $(APE_CHECKS) \ o/$(MODE)/ape/ape.lds \ o/$(MODE)/ape/ape.elf \ o/$(MODE)/ape/ape.macho \ - o/$(MODE)/ape/ape-copy-self.o \ - o/$(MODE)/ape/ape-no-modify-self.o endif diff --git a/libc/calls/finddebugbinary.c b/libc/calls/finddebugbinary.c index e8afd3935..e52e464eb 100644 --- a/libc/calls/finddebugbinary.c +++ b/libc/calls/finddebugbinary.c @@ -104,6 +104,8 @@ static bool IsMyDebugBinary(const char *path) { static void FindDebugBinaryInit(void) { const char *comdbg; + if (issetugid()) + return; if ((comdbg = getenv("COMDBG")) && IsMyDebugBinary(comdbg)) { g_comdbg.res = comdbg; return; diff --git a/libc/isystem/langinfo.h b/libc/isystem/langinfo.h index 6be085243..fbc4c3d46 100644 --- a/libc/isystem/langinfo.h +++ b/libc/isystem/langinfo.h @@ -1,6 +1,4 @@ #ifndef _LANGINFO_H #define _LANGINFO_H #include "libc/str/langinfo.h" -#include "libc/str/locale.h" -#include "libc/str/nltypes.h" #endif /* _LANGINFO_H */ diff --git a/libc/log/addr2linepath.c b/libc/log/addr2linepath.c index eb656175e..058b52d80 100644 --- a/libc/log/addr2linepath.c +++ b/libc/log/addr2linepath.c @@ -40,7 +40,7 @@ void GetAddr2linePathInit(void) { char *res; int e = errno; const char *env, *cmd, *path; - if ((env = getenv("ADDR2LINE"))) { + if ((env = secure_getenv("ADDR2LINE"))) { cmd = env; path = env; } else { diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index faaa704c3..fb94950ff 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -37,6 +37,7 @@ #include "libc/runtime/syslib.internal.h" #include "libc/stdalign.internal.h" #include "libc/str/locale.h" +#include "libc/str/locale.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" @@ -213,7 +214,6 @@ textstartup void __enable_tls(void) { tib->tib_errno = __errno; tib->tib_strace = __strace; tib->tib_ftrace = __ftrace; - tib->tib_locale = (intptr_t)&__c_dot_utf8_locale; tib->tib_pthread = (pthread_t)&_pthread_static; if (IsWindows()) { intptr_t hThread; @@ -246,6 +246,7 @@ textstartup void __enable_tls(void) { // initialize posix threads _pthread_static.tib = tib; _pthread_static.pt_flags = PT_STATIC; + _pthread_static.pt_locale = &__global_locale; dll_init(&_pthread_static.list); _pthread_list = &_pthread_static.list; atomic_store_explicit(&_pthread_static.ptid, tid, memory_order_release); diff --git a/libc/runtime/straceinit.greg.c b/libc/runtime/straceinit.greg.c index 151e3449a..7817b547a 100644 --- a/libc/runtime/straceinit.greg.c +++ b/libc/runtime/straceinit.greg.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/intrin/getenv.h" #include "libc/intrin/safemacros.h" #include "libc/log/libfatal.internal.h" @@ -27,8 +28,9 @@ */ textstartup int __strace_init(int argc, char **argv, char **envp, long *auxv) { /* asan isn't initialized yet at runlevel 300 */ - if (__intercept_flag(&argc, argv, "--strace") || - __atoul(nulltoempty(__getenv(envp, "STRACE").s))) { + if ((__intercept_flag(&argc, argv, "--strace") || + __atoul(nulltoempty(__getenv(envp, "STRACE").s))) && + !issetugid()) { strace_enabled(+1); } return (__argc = argc); diff --git a/libc/runtime/zipos-get.c b/libc/runtime/zipos-get.c index e3615c5f4..c9b39737f 100644 --- a/libc/runtime/zipos-get.c +++ b/libc/runtime/zipos-get.c @@ -112,7 +112,7 @@ static void __zipos_init(void) { const char *progpath; if (!(s = getenv("COSMOPOLITAN_DISABLE_ZIPOS"))) { // this environment variable may be a filename or file descriptor - if ((progpath = getenv("COSMOPOLITAN_INIT_ZIPOS")) && + if ((progpath = secure_getenv("COSMOPOLITAN_INIT_ZIPOS")) && (x = strtol(progpath, &endptr, 10)) >= 0 && !*endptr) { fd = x; } else { diff --git a/libc/sock/BUILD.mk b/libc/sock/BUILD.mk index bd74fe141..faf19f971 100644 --- a/libc/sock/BUILD.mk +++ b/libc/sock/BUILD.mk @@ -32,7 +32,6 @@ LIBC_SOCK_A_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_NT_ADVAPI32 \ LIBC_NT_IPHLPAPI \ - LIBC_NT_IPHLPAPI \ LIBC_NT_KERNEL32 \ LIBC_NT_NTDLL \ LIBC_NT_WS2_32 \ diff --git a/libc/str/c32rtomb.c b/libc/str/c32rtomb.c deleted file mode 100644 index 4d258deda..000000000 --- a/libc/str/c32rtomb.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" - -size_t c32rtomb(char *s, char32_t c, mbstate_t *t) { - return wcrtomb(s, c, t); -} diff --git a/libc/str/freelocale.c b/libc/str/freelocale.c deleted file mode 100644 index eba6fad5c..000000000 --- a/libc/str/freelocale.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et 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/str/locale.h" - -void freelocale(locale_t l) { - // TODO: implement me -} diff --git a/libc/str/langinfo.h b/libc/str/langinfo.h index edb8ce75e..5427efa4d 100644 --- a/libc/str/langinfo.h +++ b/libc/str/langinfo.h @@ -1,5 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_STR_LANGINFO_H_ #define COSMOPOLITAN_LIBC_STR_LANGINFO_H_ +#include "libc/str/locale.h" +#include "libc/str/nltypes.h" COSMOPOLITAN_C_START_ #define ABDAY_1 0x20000 @@ -78,7 +80,8 @@ COSMOPOLITAN_C_START_ #define NOSTR 0x50003 #endif -char *nl_langinfo(int); +char *nl_langinfo(nl_item); +char *nl_langinfo_l(nl_item, locale_t); COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_LIBC_STR_LANGINFO_H_ */ diff --git a/libc/str/locale.c b/libc/str/locale.c index 8db705895..d818e76d4 100644 --- a/libc/str/locale.c +++ b/libc/str/locale.c @@ -16,10 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/locale.h" +#include "libc/str/locale.internal.h" #include "libc/str/str.h" -static const uint32_t empty_mo[] = {0x950412de, 0, -1, -1, -1}; +static const uint32_t empty_mo[] = { + 0x950412de, 0, -1, -1, -1, +}; const struct __locale_map __c_dot_utf8 = { .map = empty_mo, @@ -27,8 +29,11 @@ const struct __locale_map __c_dot_utf8 = { .name = "C.UTF-8", }; -const struct __locale_struct __c_locale; - +const struct __locale_struct __c_locale = {0}; const struct __locale_struct __c_dot_utf8_locale = { .cat[LC_CTYPE] = &__c_dot_utf8, }; + +struct __locale_struct __global_locale; + +pthread_mutex_t __locale_lock = PTHREAD_MUTEX_INITIALIZER; diff --git a/libc/str/locale.h b/libc/str/locale.h index ec66e45dd..2b772639f 100644 --- a/libc/str/locale.h +++ b/libc/str/locale.h @@ -17,29 +17,13 @@ #define LC_MONETARY_MASK 16 #define LC_MESSAGES_MASK 32 #define LC_ALL_MASK 0x1fbf -#define LOCALE_NAME_MAX 23 COSMOPOLITAN_C_START_ #define LC_GLOBAL_LOCALE ((locale_t) - 1) -struct __locale_map { - const void *map; - size_t map_size; - char name[LOCALE_NAME_MAX + 1]; - const struct __locale_map *next; -}; - -struct __locale_struct { - const struct __locale_map *cat[6]; -}; - typedef struct __locale_struct *locale_t; -extern const struct __locale_map __c_dot_utf8; -extern const struct __locale_struct __c_locale; -extern const struct __locale_struct __c_dot_utf8_locale; - char *nl_langinfo_l(int, locale_t) libcesque; char *setlocale(int, const char *) libcesque; double strtod_l(const char *, char **, locale_t) libcesque; diff --git a/libc/str/locale.internal.h b/libc/str/locale.internal.h new file mode 100644 index 000000000..e6dad969c --- /dev/null +++ b/libc/str/locale.internal.h @@ -0,0 +1,52 @@ +#ifndef COSMOPOLITAN_LIBC_STR_LOCALE_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_STR_LOCALE_INTERNAL_H_ +#include "libc/limits.h" +#include "libc/str/locale.h" +#include "libc/thread/posixthread.internal.h" +COSMOPOLITAN_C_START_ + +#define LOCALE_NAME_MAX 23 + +struct __locale_map { + const void *map; + size_t map_size; + char name[LOCALE_NAME_MAX + 1]; + const struct __locale_map *next; +}; + +struct __locale_struct { + const struct __locale_map *cat[6]; +}; + +extern pthread_mutex_t __locale_lock; + +extern struct __locale_struct __global_locale; +extern const struct __locale_map __c_dot_utf8; +extern const struct __locale_struct __c_locale; +extern const struct __locale_struct __c_dot_utf8_locale; + +const struct __locale_map *__get_locale(int, const char *); +const char *__mo_lookup(const void *, size_t, const char *); +const char *__lctrans(const char *, const struct __locale_map *); +const char *__lctrans_cur(const char *); +const char *__lctrans_impl(const char *, const struct __locale_map *); +int __loc_is_allocated(locale_t); +char *__gettextdomain(void); + +#define LOC_MAP_FAILED ((const struct __locale_map *)-1) + +#define LCTRANS(msg, lc, loc) __lctrans(msg, (loc)->cat[(lc)]) +#define LCTRANS_CUR(msg) __lctrans_cur(msg) + +#define C_LOCALE ((locale_t) & __c_locale) +#define UTF8_LOCALE ((locale_t) & __c_dot_utf8_locale) + +#define CURRENT_LOCALE _pthread_self()->pt_locale + +#define CURRENT_UTF8 (!!_pthread_self()->pt_locale->cat[LC_CTYPE]) + +#undef MB_CUR_MAX +#define MB_CUR_MAX (CURRENT_UTF8 ? 4 : 1) + +COSMOPOLITAN_C_END_ +#endif /* COSMOPOLITAN_LIBC_STR_LOCALE_INTERNAL_H_ */ diff --git a/libc/str/mb.internal.h b/libc/str/mb.internal.h deleted file mode 100644 index d5f2748c6..000000000 --- a/libc/str/mb.internal.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_STR_MB_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_STR_MB_INTERNAL_H_ -COSMOPOLITAN_C_START_ - -#define SA 0xc2u -#define SB 0xf4u -#define CODEUNIT(c) (0xdfff & (signed char)(c)) -#define IS_CODEUNIT(c) ((unsigned)(c) - 0xdf80 < 0x80) -#define R(a, b) ((uint32_t)((a == 0x80 ? 0x40u - b : 0u - a) << 23)) -#define FAILSTATE R(0x80, 0x80) -#define OOB(c, b) \ - (((((b) >> 3) - 0x10) | (((b) >> 3) + ((int32_t)(c) >> 26))) & ~7) - -extern const uint32_t kMbBittab[51]; - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_STR_MB_INTERNAL_H_ */ diff --git a/libc/str/mblen.c b/libc/str/mblen.c deleted file mode 100644 index 807cfbd51..000000000 --- a/libc/str/mblen.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" - -int mblen(const char *s, size_t n) { - return mbtowc(0, s, n); -} diff --git a/libc/str/mbrlen.c b/libc/str/mbrlen.c deleted file mode 100644 index 4f20fe1a0..000000000 --- a/libc/str/mbrlen.c +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" - -size_t mbrlen(const char *s, size_t n, mbstate_t *t) { - static mbstate_t ss; - if (!t) - t = &ss; - return mbrtowc(0, s, n, t); -} diff --git a/libc/str/mbsinit.c b/libc/str/mbsinit.c deleted file mode 100644 index e7bac0d7f..000000000 --- a/libc/str/mbsinit.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" - -int mbsinit(const mbstate_t *t) { - return !t || !*t; -} diff --git a/libc/str/mbsrtowcs.c b/libc/str/mbsrtowcs.c deleted file mode 100644 index eaebe234c..000000000 --- a/libc/str/mbsrtowcs.c +++ /dev/null @@ -1,147 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╚──────────────────────────────────────────────────────────────────────────────╝ -│ │ -│ Musl Libc │ -│ Copyright © 2005-2014 Rich Felker, et al. │ -│ │ -│ Permission is hereby granted, free of charge, to any person obtaining │ -│ a copy of this software and associated documentation files (the │ -│ "Software"), to deal in the Software without restriction, including │ -│ without limitation the rights to use, copy, modify, merge, publish, │ -│ distribute, sublicense, and/or sell copies of the Software, and to │ -│ permit persons to whom the Software is furnished to do so, subject to │ -│ the following conditions: │ -│ │ -│ The above copyright notice and this permission notice shall be │ -│ included in all copies or substantial portions of the Software. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ -│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ -│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ -│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ -│ │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/macros.internal.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" -__static_yoink("musl_libc_notice"); - -size_t mbsrtowcs(wchar_t *ws, const char **src, size_t wn, mbstate_t *st) { - const unsigned char *s = (const void *)*src; - size_t wn0 = wn; - unsigned c = 0; - if (st && (c = *(unsigned *)st)) { - if (ws) { - *(unsigned *)st = 0; - goto resume; - } else { - goto resume0; - } - } - if (MB_CUR_MAX == 1) { - if (!ws) - return strlen((const char *)s); - for (;;) { - if (!wn) { - *src = (const void *)s; - return wn0; - } - if (!*s) - break; - c = *s++; - *ws++ = CODEUNIT(c); - wn--; - } - *ws = 0; - *src = 0; - return wn0 - wn; - } - if (!ws) - for (;;) { - if (*s - 1u < 0x7f) { - s++; - wn--; - continue; - } - if (*s - SA > SB - SA) - break; - c = kMbBittab[*s++ - SA]; - resume0: - if (OOB(c, *s)) { - s--; - break; - } - s++; - if (c & (1U << 25)) { - if (*s - 0x80u >= 0x40) { - s -= 2; - break; - } - s++; - if (c & (1U << 19)) { - if (*s - 0x80u >= 0x40) { - s -= 3; - break; - } - s++; - } - } - wn--; - c = 0; - } - else - for (;;) { - if (!wn) { - *src = (const void *)s; - return wn0; - } - if (*s - 1u < 0x7f) { - *ws++ = *s++; - wn--; - continue; - } - if (*s - SA > SB - SA) - break; - c = kMbBittab[*s++ - SA]; - resume: - if (OOB(c, *s)) { - s--; - break; - } - c = (c << 6) | (*s++ - 0x80); - if (c & (1U << 31)) { - if (*s - 0x80u >= 0x40) { - s -= 2; - break; - } - c = (c << 6) | (*s++ - 0x80); - if (c & (1U << 31)) { - if (*s - 0x80u >= 0x40) { - s -= 3; - break; - } - c = (c << 6) | (*s++ - 0x80); - } - } - *ws++ = c; - wn--; - c = 0; - } - if (!c && !*s) { - if (ws) { - *ws = 0; - *src = 0; - } - return wn0 - wn; - } - errno = EILSEQ; - if (ws) - *src = (const void *)s; - return -1; -} diff --git a/libc/str/mbstowcs.c b/libc/str/mbstowcs.c deleted file mode 100644 index 936499e03..000000000 --- a/libc/str/mbstowcs.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/str/str.h" - -size_t mbstowcs(wchar_t *pwc, const char *s, size_t wn) { - return mbsrtowcs(pwc, (void *)&s, wn, 0); -} diff --git a/libc/str/newlocale.c b/libc/str/newlocale.c deleted file mode 100644 index ad0d5ff2e..000000000 --- a/libc/str/newlocale.c +++ /dev/null @@ -1,25 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et 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/str/locale.h" -#include "libc/sysv/errfuns.h" - -locale_t newlocale(int catmask, const char *locale, locale_t base) { - // TODO: implement me - return 0; -} diff --git a/libc/str/setlocale.c b/libc/str/setlocale.c deleted file mode 100644 index 9e4ecbeed..000000000 --- a/libc/str/setlocale.c +++ /dev/null @@ -1,43 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/safemacros.h" -#include "libc/intrin/strace.h" -#include "libc/str/locale.h" -#include "libc/str/str.h" - -/** - * Sets program locale. - * - * Cosmopolitan only supports the C or POSIX locale with UTF-8. - */ -char *setlocale(int category, const char *locale) { - char *res; - if (!locale || (*locale == '\0')) { - res = "C"; - } else if (!strcmp(locale, "C") || // - !strcmp(locale, "POSIX") || // - !strcmp(locale, "C.UTF-8") || // - !strcmp(locale, "en_US.UTF-8")) { - res = (char *)locale; - } else { - res = NULL; - } - STRACE("setlocale(%d, %#s) → %s", category, locale, res); - return res; -} diff --git a/libc/str/str.h b/libc/str/str.h index 538acdeaa..b7e65ba14 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -27,10 +27,7 @@ COSMOPOLITAN_C_START_ void *memset(void *, int, size_t) memcpyesque; void *memmove(void *, const void *, size_t) memcpyesque; void *memcpy(void *, const void *, size_t) memcpyesque; -void *mempcpy(void *, const void *, size_t) memcpyesque; char *hexpcpy(char *, const void *, size_t) memcpyesque; -void *memccpy(void *, const void *, int, size_t) memcpyesque; -void explicit_bzero(void *, size_t); int memcmp(const void *, const void *, size_t) strlenesque; int timingsafe_bcmp(const void *, const void *, size_t) libcesque; @@ -41,7 +38,6 @@ size_t strnlen(const char *, size_t) strlenesque; size_t strnlen_s(const char *, size_t) libcesque; char *strchr(const char *, int) strlenesque; void *memchr(const void *, int, size_t) strlenesque; -char *strchrnul(const char *, int) strlenesque returnsnonnull; void *rawmemchr(const void *, int) strlenesque returnsnonnull; size_t wcslen(const wchar_t *) strlenesque; size_t wcsnlen(const wchar_t *, size_t) strlenesque; @@ -51,7 +47,6 @@ wchar_t *wmemchr(const wchar_t *, wchar_t, size_t) strlenesque; wchar_t *wcschrnul(const wchar_t *, wchar_t) strlenesque returnsnonnull; char *strstr(const char *, const char *) strlenesque; -char *strcasestr(const char *, const char *) strlenesque; wchar_t *wcsstr(const wchar_t *, const wchar_t *) strlenesque; int strcmp(const char *, const char *) strlenesque; int strncmp(const char *, const char *, size_t) strlenesque; @@ -59,14 +54,11 @@ int wcscmp(const wchar_t *, const wchar_t *) strlenesque; int wcsncmp(const wchar_t *, const wchar_t *, size_t) strlenesque; int wmemcmp(const wchar_t *, const wchar_t *, size_t) strlenesque; int strcasecmp(const char *, const char *) strlenesque; -int memcasecmp(const void *, const void *, size_t) strlenesque; int wcscasecmp(const wchar_t *, const wchar_t *) strlenesque; int strncasecmp(const char *, const char *, size_t) strlenesque; int wcsncasecmp(const wchar_t *, const wchar_t *, size_t) strlenesque; char *strrchr(const char *, int) strlenesque; -void *memrchr(const void *, int, size_t) strlenesque; wchar_t *wcsrchr(const wchar_t *, wchar_t) strlenesque; -void *wmemrchr(const wchar_t *, wchar_t, size_t) strlenesque; char *strpbrk(const char *, const char *) strlenesque; wchar_t *wcspbrk(const wchar_t *, const wchar_t *) strlenesque; size_t strspn(const char *, const char *) strlenesque; @@ -75,13 +67,10 @@ size_t strcspn(const char *, const char *) strlenesque; size_t wcscspn(const wchar_t *, const wchar_t *) strlenesque; void *memfrob(void *, size_t) memcpyesque; int strcoll(const char *, const char *) strlenesque; -char *strsep(char **, const char *) libcesque paramsnonnull(); char *stpcpy(char *, const char *) memcpyesque; char *stpncpy(char *, const char *, size_t) memcpyesque; char *strcat(char *, const char *) memcpyesque; wchar_t *wcscat(wchar_t *, const wchar_t *) memcpyesque; -size_t strlcpy(char *, const char *, size_t) libcesque; -size_t strlcat(char *, const char *, size_t) libcesque; size_t strxfrm(char *, const char *, size_t) libcesque; char *strcpy(char *, const char *) memcpyesque; wchar_t *wcscpy(wchar_t *, const wchar_t *) memcpyesque; @@ -91,13 +80,9 @@ char *strncpy(char *, const char *, size_t) memcpyesque; char *strtok(char *, const char *) paramsnonnull((2)) libcesque; char *strtok_r(char *, const char *, char **) paramsnonnull((2, 3)); wchar_t *wcstok(wchar_t *, const wchar_t *, wchar_t **) paramsnonnull((2, 3)); -int strverscmp(const char *, const char *) libcesque; wchar_t *wmemset(wchar_t *, wchar_t, size_t) memcpyesque; wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t) memcpyesque; -wchar_t *wmempcpy(wchar_t *, const wchar_t *, size_t) memcpyesque; wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t) memcpyesque; -void *memmem(const void *, size_t, const void *, size_t) -libcesque nosideeffect; ssize_t strfmon(char *, size_t, const char *, ...) libcesque; long a64l(const char *) libcesque; char *l64a(long) libcesque; @@ -131,6 +116,33 @@ char *strerror(int) returnsnonnull dontthrow dontcallback; errno_t strerror_r(int, char *, size_t) libcesque; char *__xpg_strerror_r(int, char *, size_t) libcesque; +int bcmp(const void *, const void *, size_t) strlenesque; +void bcopy(const void *, void *, size_t) memcpyesque; +void bzero(void *, size_t) memcpyesque; +char *index(const char *, int) strlenesque; +char *rindex(const char *, int) strlenesque; + +#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) || \ + defined(_XOPEN_SOURCE) +void *memccpy(void *, const void *, int, size_t) memcpyesque; +#endif + +#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +char *strsep(char **, const char *) libcesque paramsnonnull(); +void explicit_bzero(void *, size_t); +size_t strlcpy(char *, const char *, size_t) libcesque; +size_t strlcat(char *, const char *, size_t) libcesque; +#endif + +#if defined(_COSMO_SOURCE) || defined(_GNU_SOURCE) +int strverscmp(const char *, const char *) libcesque; +char *strchrnul(const char *, int) strlenesque returnsnonnull; +char *strcasestr(const char *, const char *) strlenesque; +void *memmem(const void *, size_t, const void *, size_t) libcesque; +void *memrchr(const void *, int, size_t) strlenesque; +void *mempcpy(void *, const void *, size_t) memcpyesque; +#endif + #ifdef _COSMO_SOURCE pureconst uint64_t tpenc(uint32_t) libcesque; char *chomp(char *) libcesque; @@ -141,6 +153,7 @@ bool32 startswithi(const char *, const char *) strlenesque; bool32 endswith(const char *, const char *) strlenesque; bool32 istext(const void *, size_t) libcesque; bool32 isutf8(const void *, size_t) libcesque; +void *wmemrchr(const wchar_t *, wchar_t, size_t) strlenesque; const char *strsignal_r(int, char[21]) returnsnonnull libcesque __wur; char16_t *chomp16(char16_t *) libcesque; size_t strlen16(const char16_t *) strlenesque; @@ -150,6 +163,7 @@ void *memchr16(const void *, int, size_t) strlenesque; char16_t *strchrnul16(const char16_t *, int) strlenesque returnsnonnull; void *rawmemchr16(const void *, int) strlenesque returnsnonnull; char16_t *strstr16(const char16_t *, const char16_t *) strlenesque; +int memcasecmp(const void *, const void *, size_t) strlenesque; int strcmp16(const char16_t *, const char16_t *) strlenesque; int strncmp16(const char16_t *, const char16_t *, size_t) strlenesque; int strcasecmp16(const char16_t *, const char16_t *) strlenesque; @@ -171,14 +185,9 @@ bool32 wcsstartswith(const wchar_t *, const wchar_t *) strlenesque; bool32 wcsendswith(const wchar_t *, const wchar_t *) strlenesque; char *__join_paths(char *, size_t, const char *, const char *) libcesque __wur; int __mkntpathat(int, const char *, int, char16_t[hasatleast 1024]); +wchar_t *wmempcpy(wchar_t *, const wchar_t *, size_t) memcpyesque; #endif /* _COSMO_SOURCE */ -int bcmp(const void *, const void *, size_t) strlenesque; -void bcopy(const void *, void *, size_t) memcpyesque; -void bzero(void *, size_t) memcpyesque; -char *index(const char *, int) strlenesque; -char *rindex(const char *, int) strlenesque; - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_STR_STR_H_ */ diff --git a/libc/str/wcsrtombs.c b/libc/str/wcsrtombs.c deleted file mode 100644 index 70d115684..000000000 --- a/libc/str/wcsrtombs.c +++ /dev/null @@ -1,90 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╚──────────────────────────────────────────────────────────────────────────────╝ -│ │ -│ Musl Libc │ -│ Copyright © 2005-2014 Rich Felker, et al. │ -│ │ -│ Permission is hereby granted, free of charge, to any person obtaining │ -│ a copy of this software and associated documentation files (the │ -│ "Software"), to deal in the Software without restriction, including │ -│ without limitation the rights to use, copy, modify, merge, publish, │ -│ distribute, sublicense, and/or sell copies of the Software, and to │ -│ permit persons to whom the Software is furnished to do so, subject to │ -│ the following conditions: │ -│ │ -│ The above copyright notice and this permission notice shall be │ -│ included in all copies or substantial portions of the Software. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ -│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ -│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ -│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ -│ │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" -__static_yoink("musl_libc_notice"); - -size_t wcsrtombs(char *s, const wchar_t **ws, size_t n, mbstate_t *st) { - const wchar_t *ws2; - char buf[4]; - size_t N = n, l; - if (!s) { - for (n = 0, ws2 = *ws; *ws2; ws2++) { - if (*ws2 >= 0x80u) { - l = wcrtomb(buf, *ws2, 0); - if (!(l + 1)) - return -1; - n += l; - } else - n++; - } - return n; - } - while (n >= 4) { - if (**ws - 1u >= 0x7fu) { - if (!**ws) { - *s = 0; - *ws = 0; - return N - n; - } - l = wcrtomb(s, **ws, 0); - if (!(l + 1)) - return -1; - s += l; - n -= l; - } else { - *s++ = **ws; - n--; - } - (*ws)++; - } - while (n) { - if (**ws - 1u >= 0x7fu) { - if (!**ws) { - *s = 0; - *ws = 0; - return N - n; - } - l = wcrtomb(buf, **ws, 0); - if (!(l + 1)) - return -1; - if (l > n) - return N - n; - wcrtomb(s, **ws, 0); - s += l; - n -= l; - } else { - *s++ = **ws; - n--; - } - (*ws)++; - } - return N; -} diff --git a/libc/str/wcstombs.c b/libc/str/wcstombs.c deleted file mode 100644 index b269fce52..000000000 --- a/libc/str/wcstombs.c +++ /dev/null @@ -1,23 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" - -size_t wcstombs(char *s, const wchar_t *ws, size_t n) { - return wcsrtombs(s, &(const wchar_t *){ws}, n, 0); -} diff --git a/libc/str/wctomb.c b/libc/str/wctomb.c deleted file mode 100644 index e2ca942d1..000000000 --- a/libc/str/wctomb.c +++ /dev/null @@ -1,26 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/limits.h" -#include "libc/str/str.h" - -int wctomb(char *s, wchar_t wc) { - if (!s) - return 0; - return wcrtomb(s, wc, 0); -} diff --git a/libc/thread/mktls.c b/libc/thread/mktls.c index 2f7129a5c..3e461fe17 100644 --- a/libc/thread/mktls.c +++ b/libc/thread/mktls.c @@ -40,7 +40,6 @@ static char *_mktls_finish(struct CosmoTib **out_tib, char *mem, tib->tib_ftrace = old->tib_ftrace; tib->tib_strace = old->tib_strace; tib->tib_sigmask = old->tib_sigmask; - tib->tib_locale = (intptr_t)&__c_dot_utf8_locale; atomic_store_explicit(&tib->tib_tid, -1, memory_order_relaxed); if (out_tib) { *out_tib = tib; diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 4afebc85d..938a73baf 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -69,6 +69,8 @@ enum PosixThreadStatus { #define POSIXTHREAD_CONTAINER(e) DLL_CONTAINER(struct PosixThread, list, e) +typedef struct __locale_struct *locale_t; + struct PosixThread { int pt_flags; // 0x00: see PT_* constants atomic_int pt_canceled; // 0x04: thread has bad beliefs @@ -86,6 +88,7 @@ struct PosixThread { uint64_t pt_blkmask; int64_t pt_semaphore; intptr_t pt_iohandle; + locale_t pt_locale; void *pt_ioverlap; jmp_buf pt_exiter; pthread_attr_t pt_attr; diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 6f9c86469..f44163c85 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -44,6 +44,7 @@ #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/syslib.internal.h" +#include "libc/str/locale.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/clone.h" @@ -235,6 +236,7 @@ static errno_t pthread_create_impl(pthread_t *thread, return EAGAIN; } dll_init(&pt->list); + pt->pt_locale = &__global_locale; pt->pt_start = start_routine; pt->pt_arg = arg; diff --git a/libc/thread/tls.h b/libc/thread/tls.h index df4e742d3..6c8be5747 100644 --- a/libc/thread/tls.h +++ b/libc/thread/tls.h @@ -23,7 +23,7 @@ struct CosmoTib { struct CosmoTib *tib_self; /* 0x00 */ struct CosmoFtrace tib_ftracer; /* 0x08 */ void *tib_garbages; /* 0x18 */ - intptr_t tib_locale; /* 0x20 */ + intptr_t __unused; /* 0x20 */ intptr_t tib_pthread; /* 0x28 */ struct CosmoTib *tib_self2; /* 0x30 */ _Atomic(int32_t) tib_tid; /* 0x38 transitions -1 → tid → 0 */ diff --git a/libc/x/BUILD.mk b/libc/x/BUILD.mk index dc1de97a8..343fcb5a1 100644 --- a/libc/x/BUILD.mk +++ b/libc/x/BUILD.mk @@ -33,11 +33,14 @@ LIBC_X_A_DIRECTDEPS = \ LIBC_PROC \ LIBC_RUNTIME \ LIBC_NT_KERNEL32 \ + LIBC_NT_ADVAPI32 \ LIBC_STDIO \ + LIBC_SOCK \ LIBC_STR \ LIBC_SYSV \ THIRD_PARTY_GDTOA \ - THIRD_PARTY_MUSL + THIRD_PARTY_MUSL \ + THIRD_PARTY_TZ \ LIBC_X_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_X_A_DIRECTDEPS),$($(x)))) diff --git a/libc/sock/syslog.c b/libc/x/syslog.c similarity index 100% rename from libc/sock/syslog.c rename to libc/x/syslog.c diff --git a/test/libc/str/BUILD.mk b/test/libc/str/BUILD.mk index cae03bb64..7468b3f05 100644 --- a/test/libc/str/BUILD.mk +++ b/test/libc/str/BUILD.mk @@ -36,7 +36,6 @@ TEST_LIBC_STR_DIRECTDEPS = \ LIBC_FMT \ LIBC_INTRIN \ LIBC_LOG \ - LIBC_TINYMATH \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ @@ -45,14 +44,16 @@ TEST_LIBC_STR_DIRECTDEPS = \ LIBC_SYSV \ LIBC_SYSV_CALLS \ LIBC_TESTLIB \ + LIBC_TINYMATH \ LIBC_X \ THIRD_PARTY_COMPILER_RT \ - THIRD_PARTY_MBEDTLS \ - THIRD_PARTY_REGEX \ - THIRD_PARTY_ZLIB \ THIRD_PARTY_LIBCXX \ + THIRD_PARTY_MBEDTLS \ + THIRD_PARTY_MUSL \ + THIRD_PARTY_REGEX \ THIRD_PARTY_SMALLZ4 \ - THIRD_PARTY_VQSORT + THIRD_PARTY_VQSORT \ + THIRD_PARTY_ZLIB \ TEST_LIBC_STR_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_STR_DIRECTDEPS),$($(x)))) diff --git a/test/libc/str/regex_test.c b/test/libc/str/regex_test.c index f51b7f557..5c23594d3 100644 --- a/test/libc/str/regex_test.c +++ b/test/libc/str/regex_test.c @@ -19,10 +19,15 @@ #include "third_party/regex/regex.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" +#include "libc/str/locale.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" +void SetUpOnce(void) { + setlocale(LC_ALL, "C.UTF-8"); +} + TEST(regex, test) { regex_t rx; EXPECT_EQ(REG_OK, regcomp(&rx, "^[A-Za-z\x7f-\uffff]{2}$", REG_EXTENDED)); diff --git a/test/libc/str/setlocale_test.c b/test/libc/str/setlocale_test.c deleted file mode 100644 index cc2669066..000000000 --- a/test/libc/str/setlocale_test.c +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Gavin Arthur Hayes │ -│ │ -│ 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/str/locale.h" -#include "libc/testlib/testlib.h" - -TEST(setlocale, test) { - EXPECT_STREQ("C", setlocale(LC_ALL, NULL)); - EXPECT_STREQ("C", setlocale(LC_ALL, "C")); - EXPECT_STREQ("C", setlocale(LC_ALL, NULL)); - EXPECT_STREQ("POSIX", setlocale(LC_ALL, "POSIX")); - EXPECT_STREQ("C", setlocale(LC_ALL, "")); - EXPECT_EQ(0, setlocale(LC_ALL, "ja_JP.PCK")); - EXPECT_STREQ("C", setlocale(LC_ALL, NULL)); -} diff --git a/test/libc/time/BUILD.mk b/test/libc/time/BUILD.mk index 937b2d09e..092444a95 100644 --- a/test/libc/time/BUILD.mk +++ b/test/libc/time/BUILD.mk @@ -28,7 +28,8 @@ TEST_LIBC_TIME_DIRECTDEPS = \ LIBC_SYSV \ LIBC_TESTLIB \ LIBC_X \ - THIRD_PARTY_TZ + THIRD_PARTY_MUSL \ + THIRD_PARTY_TZ \ TEST_LIBC_TIME_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_TIME_DIRECTDEPS),$($(x)))) diff --git a/test/libc/tinymath/BUILD.mk b/test/libc/tinymath/BUILD.mk index 5193d086b..7431bb4eb 100644 --- a/test/libc/tinymath/BUILD.mk +++ b/test/libc/tinymath/BUILD.mk @@ -8,15 +8,16 @@ TEST_LIBC_TINYMATH_SRCS_CC := $(wildcard test/libc/tinymath/*.cc) TEST_LIBC_TINYMATH_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_TINYMATH_SRCS)) TEST_LIBC_TINYMATH_SRCS = \ - $(TEST_LIBC_TINYMATH_SRCS_C:%.c=o/$(MODE)/%.o) \ - $(TEST_LIBC_TINYMATH_SRCS_CC:%.cc=o/$(MODE)/%.o) + $(TEST_LIBC_TINYMATH_SRCS_C) \ + $(TEST_LIBC_TINYMATH_SRCS_CC) TEST_LIBC_TINYMATH_OBJS = \ $(TEST_LIBC_TINYMATH_SRCS_C:%.c=o/$(MODE)/%.o) \ $(TEST_LIBC_TINYMATH_SRCS_CC:%.cc=o/$(MODE)/%.o) TEST_LIBC_TINYMATH_COMS = \ - $(TEST_LIBC_TINYMATH_SRCS:%.c=o/$(MODE)/%) + $(TEST_LIBC_TINYMATH_SRCS_C:%.c=o/$(MODE)/%) \ + $(TEST_LIBC_TINYMATH_SRCS_CC:%.cc=o/$(MODE)/%) TEST_LIBC_TINYMATH_BINS = \ $(TEST_LIBC_TINYMATH_COMS) \ @@ -68,10 +69,6 @@ $(TEST_LIBC_TINYMATH_OBJS): private \ CFLAGS += \ -fno-builtin -$(TEST_LIBC_TINYMATH_OBJS): private \ - CXXFLAGS += \ - #-ffast-math - .PHONY: o/$(MODE)/test/libc/tinymath o/$(MODE)/test/libc/tinymath: \ $(TEST_LIBC_TINYMATH_BINS) \ diff --git a/test/posix/BUILD.mk b/test/posix/BUILD.mk index dafa1d9c1..0c1209cf5 100644 --- a/test/posix/BUILD.mk +++ b/test/posix/BUILD.mk @@ -35,7 +35,8 @@ TEST_POSIX_DIRECTDEPS = \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ - LIBC_THREAD + LIBC_THREAD \ + THIRD_PARTY_MUSL \ TEST_POSIX_DEPS := \ $(call uniq,$(foreach x,$(TEST_POSIX_DIRECTDEPS),$($(x)))) diff --git a/test/posix/iconv_utf8_utf16_test.c b/test/posix/iconv_utf8_utf16_test.c new file mode 100644 index 000000000..f582749b9 --- /dev/null +++ b/test/posix/iconv_utf8_utf16_test.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include + +#define INBUF_SIZE 1024 +#define OUTBUF_SIZE 2048 + +int g_count; + +int check_conversion(const char* input, size_t input_len, + const char16_t* expected_output, size_t expected_len) { + iconv_t cd; + char inbuf[INBUF_SIZE]; + char outbuf[OUTBUF_SIZE]; + char* inptr = inbuf; + char* outptr = outbuf; + size_t inbytesleft = input_len; + size_t outbytesleft = OUTBUF_SIZE; + size_t result; + + ++g_count; + + memcpy(inbuf, input, input_len); + + cd = iconv_open("UTF-16LE", "UTF-8"); + if (cd == (iconv_t)-1) { + return 10 + g_count; // iconv_open failed + } + + result = iconv(cd, &inptr, &inbytesleft, &outptr, &outbytesleft); + if (result == (size_t)-1) { + iconv_close(cd); + return 20 + g_count; // iconv failed, return 20 + specific errno + } + + if (inbytesleft != 0) { + iconv_close(cd); + return 40 + g_count; // Not all input was converted + } + + size_t output_len = OUTBUF_SIZE - outbytesleft; + if (output_len != expected_len) { + iconv_close(cd); + return 50 + g_count; // Output length mismatch + } + + if (memcmp(outbuf, expected_output, output_len) != 0) { + iconv_close(cd); + return 60 + g_count; // Output content mismatch + } + + if (iconv_close(cd) == -1) + return 70 + g_count; // iconv_close failed + + // Reverse direction check: UTF-16LE back to UTF-8 + cd = iconv_open("UTF-8", "UTF-16LE"); + if (cd == (iconv_t)-1) { + return 80 + g_count; // iconv_open failed for reverse direction + } + + char reverse_inbuf[OUTBUF_SIZE]; + char reverse_outbuf[INBUF_SIZE]; + char* reverse_inptr = reverse_inbuf; + char* reverse_outptr = reverse_outbuf; + size_t reverse_inbytesleft = output_len; + size_t reverse_outbytesleft = INBUF_SIZE; + + memcpy(reverse_inbuf, outbuf, output_len); + + result = iconv(cd, &reverse_inptr, &reverse_inbytesleft, &reverse_outptr, + &reverse_outbytesleft); + if (result == (size_t)-1) { + iconv_close(cd); + return 90 + g_count; // iconv failed for reverse direction + } + + if (reverse_inbytesleft != 0) { + iconv_close(cd); + return 100 + g_count; // Not all input was converted in reverse direction + } + + size_t reverse_output_len = INBUF_SIZE - reverse_outbytesleft; + if (reverse_output_len != input_len) { + iconv_close(cd); + return 110 + g_count; // Reverse output length mismatch + } + + if (memcmp(reverse_outbuf, input, input_len) != 0) { + iconv_close(cd); + return 120 + g_count; // Reverse output content mismatch + } + + if (iconv_close(cd) == -1) + return 130 + g_count; // iconv_close failed for reverse direction + + return 0; // Success +} + +int main() { + // Test case 1: Basic ASCII + const char input1[] = "Hello, world!"; + const char16_t expected1[] = u"Hello, world!"; + int result = check_conversion(input1, sizeof(input1) - 1, expected1, + sizeof(expected1) - 2); + if (result != 0) + return result; + + // Test case 2: Non-ASCII characters and newline + const char input2[] = "こんにちは\nWorld! ☺"; + const char16_t expected2[] = u"こんにちは\nWorld! ☺"; + result = check_conversion(input2, sizeof(input2) - 1, expected2, + sizeof(expected2) - 2); + if (result != 0) + return result; + + // Test case 3: Empty string + const char input3[] = ""; + const char16_t expected3[] = u""; + result = check_conversion(input3, 0, expected3, 0); + if (result != 0) + return result; + + // Test case 4: String with null characters + const char input4[] = "Hello\0World"; + const char16_t expected4[] = u"Hello\0World"; + result = check_conversion(input4, sizeof(input4) - 1, expected4, + sizeof(expected4) - 2); + if (result != 0) + return result; + + // Test case 5: Long string to test buffer handling + char input5[INBUF_SIZE]; + char16_t expected5[INBUF_SIZE]; + memset(input5, 'A', INBUF_SIZE - 1); + input5[INBUF_SIZE - 1] = '\0'; + for (int i = 0; i < INBUF_SIZE - 1; i++) { + expected5[i] = u'A'; + } + result = + check_conversion(input5, INBUF_SIZE - 1, expected5, (INBUF_SIZE - 1) * 2); + if (result != 0) + return result; + + // Test case 6: Invalid UTF-8 sequence + const char input6[] = {0xC0, 0x80}; + result = check_conversion(input6, sizeof(input6), NULL, 0); + if (result != 26) { + if (errno != EILSEQ) + return 201; + return 200; + } + + // Test case 7: Mixing ASCII and non-ASCII + const char input7[] = "Hello, 世界!"; + const char16_t expected7[] = u"Hello, 世界!"; + result = check_conversion(input7, sizeof(input7) - 1, expected7, + sizeof(expected7) - 2); + if (result != 0) + return result; + + // Test case 8: Surrogate pairs + const char input8[] = "𐐷"; // U+10437 + const char16_t expected8[] = + u"𐐷"; // This will be encoded as a surrogate pair + result = check_conversion(input8, sizeof(input8) - 1, expected8, + sizeof(expected8) - 2); + if (result != 0) + return result; + + return 0; // All tests passed +} diff --git a/test/posix/iconv_utf8_utf32_test.c b/test/posix/iconv_utf8_utf32_test.c new file mode 100644 index 000000000..2486bdb22 --- /dev/null +++ b/test/posix/iconv_utf8_utf32_test.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + +#define INBUF_SIZE 1024 +#define OUTBUF_SIZE 4096 + +int g_count; + +int check_conversion(const char* input, size_t input_len, + const wchar_t* expected_output, size_t expected_len) { + iconv_t cd; + char inbuf[INBUF_SIZE]; + char outbuf[OUTBUF_SIZE]; + char* inptr = inbuf; + char* outptr = outbuf; + size_t inbytesleft = input_len; + size_t outbytesleft = OUTBUF_SIZE; + size_t result; + + ++g_count; + + memcpy(inbuf, input, input_len); + + cd = iconv_open("UTF-32LE", "UTF-8"); + if (cd == (iconv_t)-1) { + return 10 + g_count; // iconv_open failed + } + + result = iconv(cd, &inptr, &inbytesleft, &outptr, &outbytesleft); + if (result == (size_t)-1) { + iconv_close(cd); + return 20 + g_count; // iconv failed, return 20 + specific errno + } + + if (inbytesleft != 0) { + iconv_close(cd); + return 40 + g_count; // Not all input was converted + } + + size_t output_len = OUTBUF_SIZE - outbytesleft; + if (output_len != expected_len) { + iconv_close(cd); + return 50 + g_count; // Output length mismatch + } + + if (memcmp(outbuf, expected_output, output_len) != 0) { + iconv_close(cd); + return 60 + g_count; // Output content mismatch + } + + if (iconv_close(cd) == -1) + return 70 + g_count; // iconv_close failed + + // Reverse direction check: UTF-32LE back to UTF-8 + cd = iconv_open("UTF-8", "UTF-32LE"); + if (cd == (iconv_t)-1) { + return 80 + g_count; // iconv_open failed for reverse direction + } + + char reverse_inbuf[OUTBUF_SIZE]; + char reverse_outbuf[INBUF_SIZE]; + char* reverse_inptr = reverse_inbuf; + char* reverse_outptr = reverse_outbuf; + size_t reverse_inbytesleft = output_len; + size_t reverse_outbytesleft = INBUF_SIZE; + + memcpy(reverse_inbuf, outbuf, output_len); + + result = iconv(cd, &reverse_inptr, &reverse_inbytesleft, &reverse_outptr, + &reverse_outbytesleft); + if (result == (size_t)-1) { + iconv_close(cd); + return 90 + g_count; // iconv failed for reverse direction + } + + if (reverse_inbytesleft != 0) { + iconv_close(cd); + return 100 + g_count; // Not all input was converted in reverse direction + } + + size_t reverse_output_len = INBUF_SIZE - reverse_outbytesleft; + if (reverse_output_len != input_len) { + iconv_close(cd); + return 110 + g_count; // Reverse output length mismatch + } + + if (memcmp(reverse_outbuf, input, input_len) != 0) { + iconv_close(cd); + return 120 + g_count; // Reverse output content mismatch + } + + if (iconv_close(cd) == -1) + return 130 + g_count; // iconv_close failed for reverse direction + + return 0; // Success +} + +int main() { + // Test case 1: Basic ASCII + const char input1[] = "Hello, world!"; + const wchar_t expected1[] = L"Hello, world!"; + int result = check_conversion(input1, sizeof(input1) - 1, expected1, + sizeof(expected1) - 4); + if (result != 0) + return result; + + // Test case 2: Non-ASCII characters and newline + const char input2[] = "こんにちは\nWorld! ☺"; + const wchar_t expected2[] = L"こんにちは\nWorld! ☺"; + result = check_conversion(input2, sizeof(input2) - 1, expected2, + sizeof(expected2) - 4); + if (result != 0) + return result; + + // Test case 3: Empty string + const char input3[] = ""; + const wchar_t expected3[] = L""; + result = check_conversion(input3, 0, expected3, 0); + if (result != 0) + return result; + + // Test case 4: String with null characters + const char input4[] = "Hello\0World"; + const wchar_t expected4[] = L"Hello\0World"; + result = check_conversion(input4, sizeof(input4) - 1, expected4, + sizeof(expected4) - 4); + if (result != 0) + return result; + + // Test case 5: Long string to test buffer handling + char input5[INBUF_SIZE]; + wchar_t expected5[INBUF_SIZE]; + memset(input5, 'A', INBUF_SIZE - 1); + input5[INBUF_SIZE - 1] = '\0'; + for (int i = 0; i < INBUF_SIZE - 1; i++) { + expected5[i] = u'A'; + } + result = + check_conversion(input5, INBUF_SIZE - 1, expected5, (INBUF_SIZE - 1) * 4); + if (result != 0) + return result; + + // Test case 6: Invalid UTF-8 sequence + const char input6[] = {0xC0, 0x80}; + result = check_conversion(input6, sizeof(input6), NULL, 0); + if (result != 26) { + if (errno != EILSEQ) + return 201; + return 200; + } + + // Test case 7: Mixing ASCII and non-ASCII + const char input7[] = "Hello, 世界!"; + const wchar_t expected7[] = L"Hello, 世界!"; + result = check_conversion(input7, sizeof(input7) - 1, expected7, + sizeof(expected7) - 4); + if (result != 0) + return result; + + // Test case 8: Surrogate pairs + const char input8[] = "𐐷"; // U+10437 + const wchar_t expected8[] = L"𐐷"; // This will be encoded as a surrogate pair + result = check_conversion(input8, sizeof(input8) - 1, expected8, + sizeof(expected8) - 4); + if (result != 0) + return result; + + return 0; // All tests passed +} diff --git a/third_party/awk/BUILD.mk b/third_party/awk/BUILD.mk index 42ea4aad8..591a6f41d 100644 --- a/third_party/awk/BUILD.mk +++ b/third_party/awk/BUILD.mk @@ -25,7 +25,8 @@ THIRD_PARTY_AWK_A_DIRECTDEPS = \ LIBC_SYSV \ LIBC_TINYMATH \ TOOL_ARGS \ - THIRD_PARTY_GDTOA + THIRD_PARTY_GDTOA \ + THIRD_PARTY_MUSL \ THIRD_PARTY_AWK_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_AWK_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/chibicc/BUILD.mk b/third_party/chibicc/BUILD.mk index 8e3d9bb6d..30d9019f2 100644 --- a/third_party/chibicc/BUILD.mk +++ b/third_party/chibicc/BUILD.mk @@ -63,6 +63,7 @@ THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GDTOA \ + THIRD_PARTY_MUSL \ THIRD_PARTY_TZ \ TOOL_BUILD_LIB diff --git a/third_party/less/BUILD.mk b/third_party/less/BUILD.mk index 2a0ddd0ff..b183fad6c 100644 --- a/third_party/less/BUILD.mk +++ b/third_party/less/BUILD.mk @@ -27,8 +27,9 @@ THIRD_PARTY_LESS_DIRECTDEPS = \ LIBC_STDIO \ LIBC_STR \ LIBC_SYSV \ + THIRD_PARTY_MUSL \ THIRD_PARTY_NCURSES \ - THIRD_PARTY_PCRE + THIRD_PARTY_PCRE \ THIRD_PARTY_LESS_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_LESS_DIRECTDEPS),$($(x)))) diff --git a/third_party/lua/BUILD.mk b/third_party/lua/BUILD.mk index 6fe58a0c9..805eacb3f 100644 --- a/third_party/lua/BUILD.mk +++ b/third_party/lua/BUILD.mk @@ -139,7 +139,8 @@ THIRD_PARTY_LUA_A_DIRECTDEPS = \ THIRD_PARTY_DOUBLECONVERSION \ THIRD_PARTY_GDTOA \ THIRD_PARTY_LINENOISE \ - THIRD_PARTY_TZ + THIRD_PARTY_MUSL \ + THIRD_PARTY_TZ \ THIRD_PARTY_LUA_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_LUA_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/musl/BUILD.mk b/third_party/musl/BUILD.mk index b8b2c98fd..8a0dd5488 100644 --- a/third_party/musl/BUILD.mk +++ b/third_party/musl/BUILD.mk @@ -30,7 +30,8 @@ THIRD_PARTY_MUSL_A_DIRECTDEPS = \ LIBC_STR \ LIBC_SYSV \ LIBC_THREAD \ - THIRD_PARTY_ZLIB + THIRD_PARTY_TZ \ + THIRD_PARTY_ZLIB \ THIRD_PARTY_MUSL_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_MUSL_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/musl/__mo_lookup.c b/third_party/musl/__mo_lookup.c new file mode 100644 index 000000000..1ef350aec --- /dev/null +++ b/third_party/musl/__mo_lookup.c @@ -0,0 +1,72 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "libc/str/locale.internal.h" +__static_yoink("musl_libc_notice"); + +#pragma GCC diagnostic ignored "-Wparentheses" + +static inline uint32_t swapc(uint32_t x, int c) +{ + return c ? x>>24 | x>>8&0xff00 | x<<8&0xff0000 | x<<24 : x; +} + +const char *__mo_lookup(const void *p, size_t size, const char *s) +{ + const uint32_t *mo = p; + int sw = *mo - 0x950412de; + uint32_t b = 0, n = swapc(mo[2], sw); + uint32_t o = swapc(mo[3], sw); + uint32_t t = swapc(mo[4], sw); + if (n>=size/4 || o>=size-4*n || t>=size-4*n || ((o|t)%4)) + return 0; + o/=4; + t/=4; + for (;;) { + uint32_t ol = swapc(mo[o+2*(b+n/2)], sw); + uint32_t os = swapc(mo[o+2*(b+n/2)+1], sw); + if (os >= size || ol >= size-os || ((char *)p)[os+ol]) + return 0; + int sign = strcmp(s, (char *)p + os); + if (!sign) { + uint32_t tl = swapc(mo[t+2*(b+n/2)], sw); + uint32_t ts = swapc(mo[t+2*(b+n/2)+1], sw); + if (ts >= size || tl >= size-ts || ((char *)p)[ts+tl]) + return 0; + return (char *)p + ts; + } + else if (n == 1) return 0; + else if (sign < 0) + n /= 2; + else { + b += n/2; + n -= n/2; + } + } + return 0; +} diff --git a/third_party/musl/__month_to_secs.c b/third_party/musl/__month_to_secs.c new file mode 100644 index 000000000..43248fb3c --- /dev/null +++ b/third_party/musl/__month_to_secs.c @@ -0,0 +1,10 @@ +int __month_to_secs(int month, int is_leap) +{ + static const int secs_through_month[] = { + 0, 31*86400, 59*86400, 90*86400, + 120*86400, 151*86400, 181*86400, 212*86400, + 243*86400, 273*86400, 304*86400, 334*86400 }; + int t = secs_through_month[month]; + if (is_leap && month >= 2) t+=86400; + return t; +} diff --git a/third_party/musl/__secs_to_tm.c b/third_party/musl/__secs_to_tm.c new file mode 100644 index 000000000..093d9021a --- /dev/null +++ b/third_party/musl/__secs_to_tm.c @@ -0,0 +1,82 @@ +#include "time_impl.h" +#include + +/* 2000-03-01 (mod 400 year, immediately after feb29 */ +#define LEAPOCH (946684800LL + 86400*(31+29)) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + +int __secs_to_tm(long long t, struct tm *tm) +{ + long long days, secs, years; + int remdays, remsecs, remyears; + int qc_cycles, c_cycles, q_cycles; + int months; + int wday, yday, leap; + static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; + + /* Reject time_t values whose year would overflow int */ + if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) + return -1; + + secs = t - LEAPOCH; + days = secs / 86400; + remsecs = secs % 86400; + if (remsecs < 0) { + remsecs += 86400; + days--; + } + + wday = (3+days)%7; + if (wday < 0) wday += 7; + + qc_cycles = days / DAYS_PER_400Y; + remdays = days % DAYS_PER_400Y; + if (remdays < 0) { + remdays += DAYS_PER_400Y; + qc_cycles--; + } + + c_cycles = remdays / DAYS_PER_100Y; + if (c_cycles == 4) c_cycles--; + remdays -= c_cycles * DAYS_PER_100Y; + + q_cycles = remdays / DAYS_PER_4Y; + if (q_cycles == 25) q_cycles--; + remdays -= q_cycles * DAYS_PER_4Y; + + remyears = remdays / 365; + if (remyears == 4) remyears--; + remdays -= remyears * 365; + + leap = !remyears && (q_cycles || !c_cycles); + yday = remdays + 31 + 28 + leap; + if (yday >= 365+leap) yday -= 365+leap; + + years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles; + + for (months=0; days_in_month[months] <= remdays; months++) + remdays -= days_in_month[months]; + + if (months >= 10) { + months -= 12; + years++; + } + + if (years+100 > INT_MAX || years+100 < INT_MIN) + return -1; + + tm->tm_year = years + 100; + tm->tm_mon = months + 2; + tm->tm_mday = remdays + 1; + tm->tm_wday = wday; + tm->tm_yday = yday; + + tm->tm_hour = remsecs / 3600; + tm->tm_min = remsecs / 60 % 60; + tm->tm_sec = remsecs % 60; + + return 0; +} diff --git a/third_party/musl/__tm_to_secs.c b/third_party/musl/__tm_to_secs.c new file mode 100644 index 000000000..c29fa985a --- /dev/null +++ b/third_party/musl/__tm_to_secs.c @@ -0,0 +1,24 @@ +#include "time_impl.h" + +long long __tm_to_secs(const struct tm *tm) +{ + int is_leap; + long long year = tm->tm_year; + int month = tm->tm_mon; + if (month >= 12 || month < 0) { + int adj = month / 12; + month %= 12; + if (month < 0) { + adj--; + month += 12; + } + year += adj; + } + long long t = __year_to_secs(year, &is_leap); + t += __month_to_secs(month, is_leap); + t += 86400LL * (tm->tm_mday-1); + t += 3600LL * tm->tm_hour; + t += 60LL * tm->tm_min; + t += tm->tm_sec; + return t; +} diff --git a/third_party/musl/__year_to_secs.c b/third_party/musl/__year_to_secs.c new file mode 100644 index 000000000..b42f5a6d2 --- /dev/null +++ b/third_party/musl/__year_to_secs.c @@ -0,0 +1,47 @@ +long long __year_to_secs(long long year, int *is_leap) +{ + if (year-2ULL <= 136) { + int y = year; + int leaps = (y-68)>>2; + if (!((y-68)&3)) { + leaps--; + if (is_leap) *is_leap = 1; + } else if (is_leap) *is_leap = 0; + return 31536000*(y-70) + 86400*leaps; + } + + int cycles, centuries, leaps, rem, dummy; + + if (!is_leap) is_leap = &dummy; + cycles = (year-100) / 400; + rem = (year-100) % 400; + if (rem < 0) { + cycles--; + rem += 400; + } + if (!rem) { + *is_leap = 1; + centuries = 0; + leaps = 0; + } else { + if (rem >= 200) { + if (rem >= 300) centuries = 3, rem -= 300; + else centuries = 2, rem -= 200; + } else { + if (rem >= 100) centuries = 1, rem -= 100; + else centuries = 0; + } + if (!rem) { + *is_leap = 0; + leaps = 0; + } else { + leaps = rem / 4U; + rem %= 4U; + *is_leap = !rem; + } + } + + leaps += 97*cycles + 24*centuries - *is_leap; + + return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; +} diff --git a/third_party/musl/asctime.c b/third_party/musl/asctime.c new file mode 100644 index 000000000..7ba0e5581 --- /dev/null +++ b/third_party/musl/asctime.c @@ -0,0 +1,10 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/time.h" + +char *asctime(const struct tm *tm) +{ + static char buf[26]; + return asctime_r(tm, buf); +} diff --git a/third_party/musl/asctime_r.c b/third_party/musl/asctime_r.c new file mode 100644 index 000000000..6f62b1242 --- /dev/null +++ b/third_party/musl/asctime_r.c @@ -0,0 +1,52 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/stdio/stdio.h" +#include "libc/str/langinfo.h" +#include "libc/str/locale.internal.h" +__static_yoink("musl_libc_notice"); + +char *asctime_r(const struct tm *tm, char *buf) +{ + if (snprintf(buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", + nl_langinfo_l(ABDAY_1+tm->tm_wday, C_LOCALE), + nl_langinfo_l(ABMON_1+tm->tm_mon, C_LOCALE), + tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_sec, + 1900 + tm->tm_year) >= 26) + { + /* ISO C requires us to use the above format string, + * even if it will not fit in the buffer. Thus asctime_r + * is _supposed_ to crash if the fields in tm are too large. + * We follow this behavior and crash "gracefully" to warn + * application developers that they may not be so lucky + * on other implementations (e.g. stack smashing..). + */ + __builtin_trap(); + } + return buf; +} diff --git a/libc/str/btowc.c b/third_party/musl/btowc.c similarity index 86% rename from libc/str/btowc.c rename to third_party/musl/btowc.c index 4e3cb74ab..557146e94 100644 --- a/libc/str/btowc.c +++ b/third_party/musl/btowc.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,13 +25,14 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/limits.h" -#include "libc/stdio/stdio.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" +#include +#include +#include +#include "multibyte.h" __static_yoink("musl_libc_notice"); -wint_t btowc(int c) { - int b = (unsigned char)c; - return b < 128U ? b : (MB_CUR_MAX == 1 && c != EOF) ? CODEUNIT(c) : WEOF; +wint_t btowc(int c) +{ + int b = (unsigned char)c; + return b<128U ? b : (MB_CUR_MAX==1 && c!=EOF) ? CODEUNIT(c) : WEOF; } diff --git a/libc/str/c16rtomb.c b/third_party/musl/c16rtomb.c similarity index 76% rename from libc/str/c16rtomb.c rename to third_party/musl/c16rtomb.c index 546f40741..10fbcfa89 100644 --- a/libc/str/c16rtomb.c +++ b/third_party/musl/c16rtomb.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,40 +25,41 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" +#include +#include +#include __static_yoink("musl_libc_notice"); -size_t c16rtomb(char *restrict s, char16_t c16, mbstate_t *restrict ps) { - static unsigned internal_state; - if (!ps) - ps = (void *)&internal_state; - unsigned *x = (unsigned *)ps; - wchar_t wc; - if (!s) { - if (*x) - goto ilseq; - return 1; - } - if (!*x && c16 - 0xd800u < 0x400) { - *x = (c16 - 0xd7c0) << 10; - return 0; - } - if (*x) { - if (c16 - 0xdc00u >= 0x400) - goto ilseq; - else - wc = *x + c16 - 0xdc00; - *x = 0; - } else { - wc = c16; - } - return wcrtomb(s, wc, 0); +#pragma GCC diagnostic ignored "-Wparentheses" + +size_t c16rtomb(char *restrict s, char16_t c16, mbstate_t *restrict ps) +{ + static unsigned internal_state; + if (!ps) ps = (void *)&internal_state; + unsigned *x = (unsigned *)ps; + wchar_t wc; + + if (!s) { + if (*x) goto ilseq; + return 1; + } + + if (!*x && c16 - 0xd800u < 0x400) { + *x = c16 - 0xd7c0 << 10; + return 0; + } + + if (*x) { + if (c16 - 0xdc00u >= 0x400) goto ilseq; + else wc = *x + c16 - 0xdc00; + *x = 0; + } else { + wc = c16; + } + return wcrtomb(s, wc, 0); + ilseq: - *x = 0; - errno = EILSEQ; - return -1; + *x = 0; + errno = EILSEQ; + return -1; } diff --git a/third_party/musl/c32rtomb.c b/third_party/musl/c32rtomb.c new file mode 100644 index 000000000..d23efca16 --- /dev/null +++ b/third_party/musl/c32rtomb.c @@ -0,0 +1,35 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +__static_yoink("musl_libc_notice"); + +size_t c32rtomb(char *restrict s, char32_t c32, mbstate_t *restrict ps) +{ + return wcrtomb(s, c32, ps); +} diff --git a/third_party/musl/catclose.c b/third_party/musl/catclose.c index f3f3b73f1..76140f687 100644 --- a/third_party/musl/catclose.c +++ b/third_party/musl/catclose.c @@ -30,6 +30,7 @@ #include #include #include +__static_yoink("musl_libc_notice"); #define V(p) be32toh(*(uint32_t *)(p)) diff --git a/third_party/musl/catgets.c b/third_party/musl/catgets.c index 8921ffc4c..cca0b16e8 100644 --- a/third_party/musl/catgets.c +++ b/third_party/musl/catgets.c @@ -31,6 +31,7 @@ #include #include #include +__static_yoink("musl_libc_notice"); #define V(p) be32toh(*(uint32_t *)(p)) diff --git a/third_party/musl/catopen.c b/third_party/musl/catopen.c index 8bcaff432..fcc09b126 100644 --- a/third_party/musl/catopen.c +++ b/third_party/musl/catopen.c @@ -35,6 +35,7 @@ #include #include "third_party/musl/mapfile.internal.h" #include +__static_yoink("musl_libc_notice"); #define V(p) be32toh(*(uint32_t *)(p)) diff --git a/third_party/tz/ctime.c b/third_party/musl/ctime.c similarity index 100% rename from third_party/tz/ctime.c rename to third_party/musl/ctime.c diff --git a/third_party/tz/ctime_r.c b/third_party/musl/ctime_r.c similarity index 100% rename from third_party/tz/ctime_r.c rename to third_party/musl/ctime_r.c diff --git a/libc/str/wcsnrtombs.c b/third_party/musl/duplocale.c similarity index 74% rename from libc/str/wcsnrtombs.c rename to third_party/musl/duplocale.c index e300ad202..7633f6e4b 100644 --- a/libc/str/wcsnrtombs.c +++ b/third_party/musl/duplocale.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,43 +25,21 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/str/mb.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/str/locale.internal.h" #include "libc/str/str.h" __static_yoink("musl_libc_notice"); -size_t wcsnrtombs(char *dst, const wchar_t **wcs, size_t wn, size_t n, - mbstate_t *st) { - const wchar_t *ws = *wcs; - size_t cnt = 0; - if (!dst) - n = 0; - while (ws && wn) { - char tmp[MB_LEN_MAX] = {0}; - size_t l = wcrtomb(n < MB_LEN_MAX ? tmp : dst, *ws, 0); - if (l == -1) { - cnt = -1; - break; - } - if (dst) { - if (n < MB_LEN_MAX) { - if (l > n) - break; - memcpy(dst, tmp, l); - } - dst += l; - n -= l; - } - if (!*ws) { - ws = 0; - break; - } - ws++; - wn--; - cnt += l; - } - if (dst) - *wcs = ws; - return cnt; +#define malloc _mapanon +#define calloc undef +#define realloc undef +#define free undef + +locale_t duplocale(locale_t old) +{ + locale_t new = malloc(sizeof *new); + if (!new) return 0; + if (old == LC_GLOBAL_LOCALE) old = &__global_locale; + *new = *old; + return new; } diff --git a/third_party/musl/fnmatch.c b/third_party/musl/fnmatch.c index 6ccf0a2e7..e48d7b998 100644 --- a/third_party/musl/fnmatch.c +++ b/third_party/musl/fnmatch.c @@ -25,10 +25,12 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/limits.h" -#include "libc/str/str.h" -#include "libc/wctype.h" -#include "third_party/musl/fnmatch.h" +#include +#include +#include +#include +#include +#include "libc/str/locale.internal.h" __static_yoink("musl_libc_notice"); /* @@ -46,284 +48,279 @@ __static_yoink("musl_libc_notice"); * - Rich Felker, April 2012 */ -#define END 0 +#define END 0 #define UNMATCHABLE -2 -#define BRACKET -3 -#define QUESTION -4 -#define STAR -5 +#define BRACKET -3 +#define QUESTION -4 +#define STAR -5 -static int FnmatchNextString(const char *str, size_t n, size_t *step) { - if (!n) { - *step = 0; - return 0; - } - if (str[0] >= 128U) { - wchar_t wc; - int k = mbtowc(&wc, str, n); - if (k < 0) { - *step = 1; - return -1; - } - *step = k; - return wc; - } - *step = 1; - return str[0]; +static int str_next(const char *str, size_t n, size_t *step) +{ + if (!n) { + *step = 0; + return 0; + } + if (str[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, str, n); + if (k<0) { + *step = 1; + return -1; + } + *step = k; + return wc; + } + *step = 1; + return str[0]; } -static int FnmatchNextPattern(const char *pat, size_t m, size_t *step, - int flags) { - int esc = 0; - if (!m || !*pat) { - *step = 0; - return END; - } - *step = 1; - if (pat[0] == '\\' && pat[1] && !(flags & FNM_NOESCAPE)) { - *step = 2; - pat++; - esc = 1; - goto escaped; - } - if (pat[0] == '[') { - size_t k = 1; - if (k < m) - if (pat[k] == '^' || pat[k] == '!') k++; - if (k < m) - if (pat[k] == ']') k++; - for (; k < m && pat[k] && pat[k] != ']'; k++) { - if (k + 1 < m && pat[k + 1] && pat[k] == '[' && - (pat[k + 1] == ':' || pat[k + 1] == '.' || pat[k + 1] == '=')) { - int z = pat[k + 1]; - k += 2; - if (k < m && pat[k]) k++; - while (k < m && pat[k] && (pat[k - 1] != z || pat[k] != ']')) k++; - if (k == m || !pat[k]) break; - } - } - if (k == m || !pat[k]) { - *step = 1; - return '['; - } - *step = k + 1; - return BRACKET; - } - if (pat[0] == '*') return STAR; - if (pat[0] == '?') return QUESTION; +static int pat_next(const char *pat, size_t m, size_t *step, int flags) +{ + int esc = 0; + if (!m || !*pat) { + *step = 0; + return END; + } + *step = 1; + if (pat[0]=='\\' && pat[1] && !(flags & FNM_NOESCAPE)) { + *step = 2; + pat++; + esc = 1; + goto escaped; + } + if (pat[0]=='[') { + size_t k = 1; + if (k= 128U) { - wchar_t wc; - int k = mbtowc(&wc, pat, m); - if (k < 0) { - *step = 0; - return UNMATCHABLE; - } - *step = k + esc; - return wc; - } - return pat[0]; + if (pat[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, pat, m); + if (k<0) { + *step = 0; + return UNMATCHABLE; + } + *step = k + esc; + return wc; + } + return pat[0]; } -static int FnmatchCaseFold(int k) { - int c = towupper(k); - return c == k ? towlower(k) : c; +static int casefold(int k) +{ + int c = towupper(k); + return c == k ? towlower(k) : c; } -static int FnmatchBracket(const char *p, int k, int kfold) { - wchar_t wc; - int inv = 0; - p++; - if (*p == '^' || *p == '!') { - inv = 1; - p++; - } - if (*p == ']') { - if (k == ']') return !inv; - p++; - } else if (*p == '-') { - if (k == '-') return !inv; - p++; - } - wc = p[-1]; - for (; *p != ']'; p++) { - if (p[0] == '-' && p[1] != ']') { - wchar_t wc2; - int l = mbtowc(&wc2, p + 1, 4); - if (l < 0) return 0; - if (wc <= wc2) - if ((unsigned)k - wc <= wc2 - wc || (unsigned)kfold - wc <= wc2 - wc) - return !inv; - p += l - 1; - continue; - } - if (p[0] == '[' && (p[1] == ':' || p[1] == '.' || p[1] == '=')) { - const char *p0 = p + 2; - int z = p[1]; - p += 3; - while (p[-1] != z || p[0] != ']') p++; - if (z == ':' && p - 1 - p0 < 16) { - char buf[16]; - memcpy(buf, p0, p - 1 - p0); - buf[p - 1 - p0] = 0; - if (iswctype(k, wctype(buf)) || iswctype(kfold, wctype(buf))) - return !inv; - } - continue; - } - if (*p < 128U) { - wc = (unsigned char)*p; - } else { - int l = mbtowc(&wc, p, 4); - if (l < 0) return 0; - p += l - 1; - } - if (wc == k || wc == kfold) return !inv; - } - return inv; +static int match_bracket(const char *p, int k, int kfold) +{ + wchar_t wc; + int inv = 0; + p++; + if (*p=='^' || *p=='!') { + inv = 1; + p++; + } + if (*p==']') { + if (k==']') return !inv; + p++; + } else if (*p=='-') { + if (k=='-') return !inv; + p++; + } + wc = p[-1]; + for (; *p != ']'; p++) { + if (p[0]=='-' && p[1]!=']') { + wchar_t wc2; + int l = mbtowc(&wc2, p+1, 4); + if (l < 0) return 0; + if (wc <= wc2) + if ((unsigned)k-wc <= wc2-wc || + (unsigned)kfold-wc <= wc2-wc) + return !inv; + p += l-1; + continue; + } + if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) { + const char *p0 = p+2; + int z = p[1]; + p+=3; + while (p[-1]!=z || p[0]!=']') p++; + if (z == ':' && p-1-p0 < 16) { + char buf[16]; + memcpy(buf, p0, p-1-p0); + buf[p-1-p0] = 0; + if (iswctype(k, wctype(buf)) || + iswctype(kfold, wctype(buf))) + return !inv; + } + continue; + } + if (*p < 128U) { + wc = (unsigned char)*p; + } else { + int l = mbtowc(&wc, p, 4); + if (l < 0) return 0; + p += l-1; + } + if (wc==k || wc==kfold) return !inv; + } + return inv; } -static int FnmatchPerform(const char *pat, size_t m, const char *str, size_t n, - int flags) { - const char *p, *ptail, *endpat; - const char *s, *stail, *endstr; - size_t pinc, sinc, tailcnt = 0; - int c, k, kfold; +static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n, int flags) +{ + const char *p, *ptail, *endpat; + const char *s, *stail, *endstr; + size_t pinc, sinc, tailcnt=0; + int c, k, kfold; - if (flags & FNM_PERIOD) { - if (*str == '.' && *pat != '.') { - return FNM_NOMATCH; - } - } + if (flags & FNM_PERIOD) { + if (*str == '.' && *pat != '.') + return FNM_NOMATCH; + } + for (;;) { + switch ((c = pat_next(pat, m, &pinc, flags))) { + case UNMATCHABLE: + return FNM_NOMATCH; + case STAR: + pat++; + m--; + break; + default: + k = str_next(str, n, &sinc); + if (k <= 0) + return (c==END) ? 0 : FNM_NOMATCH; + str += sinc; + n -= sinc; + kfold = flags & FNM_CASEFOLD ? casefold(k) : k; + if (c == BRACKET) { + if (!match_bracket(pat, k, kfold)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c && kfold != c) { + return FNM_NOMATCH; + } + pat+=pinc; + m-=pinc; + continue; + } + break; + } - for (;;) { - switch ((c = FnmatchNextPattern(pat, m, &pinc, flags))) { - case UNMATCHABLE: - return FNM_NOMATCH; - case STAR: - pat++; - m--; - break; - default: - k = FnmatchNextString(str, n, &sinc); - if (k <= 0) return (c == END) ? 0 : FNM_NOMATCH; - str += sinc; - n -= sinc; - kfold = flags & FNM_CASEFOLD ? FnmatchCaseFold(k) : k; - if (c == BRACKET) { - if (!FnmatchBracket(pat, k, kfold)) return FNM_NOMATCH; - } else if (c != QUESTION && k != c && kfold != c) { - return FNM_NOMATCH; - } - pat += pinc; - m -= pinc; - continue; - } - break; - } + /* Compute real pat length if it was initially unknown/-1 */ + m = strnlen(pat, m); + endpat = pat + m; - /* Compute real pat length if it was initially unknown/-1 */ - m = strnlen(pat, m); - endpat = pat + m; + /* Find the last * in pat and count chars needed after it */ + for (p=ptail=pat; pstr && tailcnt; tailcnt--) { + if (s[-1] < 128U || MB_CUR_MAX==1) s--; + else while ((unsigned char)*--s-0x80U<0x40 && s>str); + } + if (tailcnt) return FNM_NOMATCH; + stail = s; - /* Find the final tailcnt chars of str, accounting for UTF-8. - * On illegal sequences we may get it wrong, but in that case - * we necessarily have a matching failure anyway. */ - for (s = endstr; s > str && tailcnt; tailcnt--) { - if (s[-1] < 128U || MB_CUR_MAX == 1) { - s--; - } else { - while ((unsigned char)*--s - 0x80U < 0x40 && s > str) - ; - } - } - if (tailcnt) return FNM_NOMATCH; - stail = s; + /* Check that the pat and str tails match */ + p = ptail; + for (;;) { + c = pat_next(p, endpat-p, &pinc, flags); + p += pinc; + if ((k = str_next(s, endstr-s, &sinc)) <= 0) { + if (c != END) return FNM_NOMATCH; + break; + } + s += sinc; + kfold = flags & FNM_CASEFOLD ? casefold(k) : k; + if (c == BRACKET) { + if (!match_bracket(p-pinc, k, kfold)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c && kfold != c) { + return FNM_NOMATCH; + } + } - /* Check that the pat and str tails match */ - p = ptail; - for (;;) { - c = FnmatchNextPattern(p, endpat - p, &pinc, flags); - p += pinc; - if ((k = FnmatchNextString(s, endstr - s, &sinc)) <= 0) { - if (c != END) return FNM_NOMATCH; - break; - } - s += sinc; - kfold = flags & FNM_CASEFOLD ? FnmatchCaseFold(k) : k; - if (c == BRACKET) { - if (!FnmatchBracket(p - pinc, k, kfold)) return FNM_NOMATCH; - } else if (c != QUESTION && k != c && kfold != c) { - return FNM_NOMATCH; - } - } + /* We're all done with the tails now, so throw them out */ + endstr = stail; + endpat = ptail; - /* We're all done with the tails now, so throw them out */ - endstr = stail; - endpat = ptail; + /* Match pattern components until there are none left */ + while (pat 0) str += sinc; + else for (str++; str_next(str, endstr-str, &sinc)<0; str++); + } - /* Match pattern components until there are none left */ - while (pat < endpat) { - p = pat; - s = str; - for (;;) { - c = FnmatchNextPattern(p, endpat - p, &pinc, flags); - p += pinc; - /* Encountering * completes/commits a component */ - if (c == STAR) { - pat = p; - str = s; - break; - } - k = FnmatchNextString(s, endstr - s, &sinc); - if (!k) return FNM_NOMATCH; - kfold = flags & FNM_CASEFOLD ? FnmatchCaseFold(k) : k; - if (c == BRACKET) { - if (!FnmatchBracket(p - pinc, k, kfold)) break; - } else if (c != QUESTION && k != c && kfold != c) { - break; - } - s += sinc; - } - if (c == STAR) continue; - /* If we failed, advance str, by 1 char if it's a valid - * char, or past all invalid bytes otherwise. */ - k = FnmatchNextString(str, endstr - str, &sinc); - if (k > 0) { - str += sinc; - } else { - str++; - while (FnmatchNextString(str, endstr - str, &sinc) < 0) { - str++; - } - } - } - - return 0; + return 0; } /** @@ -337,29 +334,27 @@ static int FnmatchPerform(const char *pat, size_t m, const char *str, size_t n, * * @see glob() */ -int fnmatch(const char *pat, const char *str, int flags) { - const char *s, *p; - size_t inc; - int c; - if (flags & FNM_PATHNAME) { - for (;;) { - for (s = str; *s && *s != '/'; s++) - ; - for (p = pat; - (c = FnmatchNextPattern(p, -1, &inc, flags)) != END && c != '/'; - p += inc) - ; - if (c != *s && (!*s || !(flags & FNM_LEADING_DIR))) return FNM_NOMATCH; - if (FnmatchPerform(pat, p - pat, str, s - str, flags)) return FNM_NOMATCH; - if (!c) return 0; - str = s + 1; - pat = p + inc; - } - } else if (flags & FNM_LEADING_DIR) { - for (s = str; *s; s++) { - if (*s != '/') continue; - if (!FnmatchPerform(pat, -1, str, s - str, flags)) return 0; - } - } - return FnmatchPerform(pat, -1, str, -1, flags); +int fnmatch(const char *pat, const char *str, int flags) +{ + const char *s, *p; + size_t inc; + int c; + if (flags & FNM_PATHNAME) for (;;) { + for (s=str; *s && *s!='/'; s++); + for (p=pat; (c=pat_next(p, -1, &inc, flags))!=END && c!='/'; p+=inc); + if (c!=*s && (!*s || !(flags & FNM_LEADING_DIR))) + return FNM_NOMATCH; + if (fnmatch_internal(pat, p-pat, str, s-str, flags)) + return FNM_NOMATCH; + if (!c) return 0; + str = s+1; + pat = p+inc; + } else if (flags & FNM_LEADING_DIR) { + for (s=str; *s; s++) { + if (*s != '/') continue; + if (!fnmatch_internal(pat, -1, str, s-str, flags)) + return 0; + } + } + return fnmatch_internal(pat, -1, str, -1, flags); } diff --git a/libc/str/mbrtoc16.c b/third_party/musl/freelocale.c similarity index 72% rename from libc/str/mbrtoc16.c rename to third_party/musl/freelocale.c index 492ae68b6..341afa33b 100644 --- a/libc/str/mbrtoc16.c +++ b/third_party/musl/freelocale.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,36 +25,17 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/limits.h" -#include "libc/str/mb.internal.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/str/locale.internal.h" __static_yoink("musl_libc_notice"); -size_t mbrtoc16(char16_t *pc16, const char *s, size_t n, mbstate_t *ps) { - static unsigned internal_state; - if (!ps) - ps = (void *)&internal_state; - unsigned *pending = (unsigned *)ps; - if (!s) - return mbrtoc16(0, "", 1, ps); - /* mbrtowc states for partial UTF-8 characters have the high bit set; - * we use nonzero states without high bit for pending surrogates. */ - if ((int)*pending > 0) { - if (pc16) - *pc16 = *pending; - *pending = 0; - return -3; - } - wchar_t wc; - size_t ret = mbrtowc(&wc, s, n, ps); - if (ret <= 4) { - if (wc >= 0x10000) { - *pending = (wc & 0x3ff) + 0xdc00; - wc = 0xd7c0 + (wc >> 10); - } - if (pc16) - *pc16 = wc; - } - return ret; +#define malloc undef +#define calloc undef +#define realloc undef +#define free(p) munmap(p, sizeof(struct __locale_struct)) + +void freelocale(locale_t l) +{ + if (__loc_is_allocated(l)) free(l); } diff --git a/third_party/musl/glob.c b/third_party/musl/glob.c index 655532d9a..e8a5314b7 100644 --- a/third_party/musl/glob.c +++ b/third_party/musl/glob.c @@ -35,190 +35,227 @@ #include "libc/str/str.h" #include "libc/sysv/consts/dt.h" #include "libc/sysv/consts/s.h" +#include "libc/limits.h" +#include "libc/str/str.h" +#include "libc/runtime/runtime.h" +#include "third_party/musl/passwd.h" #include "third_party/musl/fnmatch.h" __static_yoink("musl_libc_notice"); -#define MAXPATH 1024 +#pragma GCC diagnostic ignored "-Wparentheses" -struct GlobList { - struct GlobList *next; - char name[]; +struct match +{ + struct match *next; + char name[]; }; -static int AppendGlob(struct GlobList **tail, const char *name, size_t len, - int mark) { - struct GlobList *new; - if ((new = malloc(sizeof(struct GlobList) + len + 2))) { - (*tail)->next = new; - new->next = NULL; - memcpy(new->name, name, len + 1); - if (mark && len && name[len - 1] != '/') { - new->name[len] = '/'; - new->name[len + 1] = 0; - } - *tail = new; - return 0; - } else { - return -1; - } +static int append(struct match **tail, const char *name, size_t len, int mark) +{ + struct match *new = malloc(sizeof(struct match) + len + 2); + if (!new) return -1; + (*tail)->next = new; + new->next = NULL; + memcpy(new->name, name, len+1); + if (mark && len && name[len-1]!='/') { + new->name[len] = '/'; + new->name[len+1] = 0; + } + *tail = new; + return 0; } -static int PerformGlob(char *buf, size_t pos, int type, char *pat, int flags, - int (*errfunc)(const char *path, int err), - struct GlobList **tail) { - DIR *dir; - size_t l; - char *p, *p2; - char saved_sep; - ptrdiff_t i, j; - struct stat st; - struct dirent *de; - int r, readerr, in_bracket, overflow, old_errno, fnm_flags; - /* If GLOB_MARK is unused, we don't care about type. */ - if (!type && !(flags & GLOB_MARK)) type = DT_REG; - /* Special-case the remaining pattern being all slashes, in - * which case we can use caller-passed type if it's a dir. */ - if (*pat && type != DT_DIR) type = 0; - while (pos + 1 < MAXPATH && *pat == '/') { - buf[pos++] = *pat++; - } - /* Consume maximal [escaped-]literal prefix of pattern, copying - * and un-escaping it to the running buffer as we go. */ - i = 0; - j = 0; - overflow = 0; - in_bracket = 0; - for (; pat[i] != '*' && pat[i] != '?' && (!in_bracket || pat[i] != ']'); - i++) { - if (!pat[i]) { - if (overflow) return 0; - pat += i; - pos += j; - i = j = 0; - break; - } else if (pat[i] == '[') { - in_bracket = 1; - } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) { - /* Backslashes inside a bracket are (at least by - * our interpretation) non-special, so if next - * char is ']' we have a complete expression. */ - if (in_bracket && pat[i + 1] == ']') break; - /* Unpaired final backslash never matches. */ - if (!pat[i + 1]) return 0; - i++; - } - if (pat[i] == '/') { - if (overflow) return 0; - in_bracket = 0; - pat += i + 1; - i = -1; - pos += j + 1; - j = -1; - } - /* Only store a character if it fits in the buffer, but if - * a potential bracket expression is open, the overflow - * must be remembered and handled later only if the bracket - * is unterminated (and thereby a literal), so as not to - * disallow long bracket expressions with short matches. */ - if (pos + (j + 1) < MAXPATH) { - buf[pos + j++] = pat[i]; - } else if (in_bracket) { - overflow = 1; - } else { - return 0; - } - /* If we consume any new components, the caller-passed type - * or dummy type from above is no longer valid. */ - type = 0; - } - buf[pos] = 0; - if (!*pat) { - /* If we consumed any components above, or if GLOB_MARK is - * requested and we don't yet know if the match is a dir, - * we must call stat to confirm the file exists and/or - * determine its type. */ - if ((flags & GLOB_MARK) && type == DT_LNK) type = 0; - if (!type && stat(buf, &st)) { - if (errno != ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) { - return GLOB_ABORTED; - } - return 0; - } - if (!type && S_ISDIR(st.st_mode)) type = DT_DIR; - if (AppendGlob(tail, buf, pos, (flags & GLOB_MARK) && type == DT_DIR)) { - return GLOB_NOSPACE; - } - return 0; - } - p2 = strchr(pat, '/'); - saved_sep = '/'; - /* Check if the '/' was escaped and, if so, remove the escape char - * so that it will not be unpaired when passed to fnmatch. */ - if (p2 && !(flags & GLOB_NOESCAPE)) { - for (p = p2; p > pat && p[-1] == '\\'; p--) - ; - if ((p2 - p) % 2) { - p2--; - saved_sep = '\\'; - } - } - dir = opendir(pos ? buf : "."); - if (!dir) { - if (errfunc(buf, errno) || (flags & GLOB_ERR)) return GLOB_ABORTED; - return 0; - } - old_errno = errno; - while (errno = 0, de = readdir(dir)) { - /* Quickly skip non-directories when there's pattern left. */ - if (p2 && de->d_type && de->d_type != DT_DIR && de->d_type != DT_LNK) { - continue; - } - l = strlen(de->d_name); - if (l >= MAXPATH - pos) continue; - if (p2) *p2 = 0; - fnm_flags = ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) | - ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); - if (fnmatch(pat, de->d_name, fnm_flags)) continue; - /* With GLOB_PERIOD don't allow matching . or .. unless fnmatch() - * would match them with FNM_PERIOD rules in effect. */ - if (p2 && (flags & GLOB_PERIOD) && de->d_name[0] == '.' && - (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])) && - fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD)) { - continue; - } - memcpy(buf + pos, de->d_name, l + 1); - if (p2) *p2 = saved_sep; - r = PerformGlob(buf, pos + l, de->d_type, p2 ? p2 : "", flags, errfunc, - tail); - if (r) { - closedir(dir); - return r; - } - } - readerr = errno; - if (p2) *p2 = saved_sep; - closedir(dir); - if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) { - return GLOB_ABORTED; - } - errno = old_errno; - return 0; +static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (*errfunc)(const char *path, int err), struct match **tail) +{ + /* If GLOB_MARK is unused, we don't care about type. */ + if (!type && !(flags & GLOB_MARK)) type = DT_REG; + + /* Special-case the remaining pattern being all slashes, in + * which case we can use caller-passed type if it's a dir. */ + if (*pat && type!=DT_DIR) type = 0; + while (pos+1 < PATH_MAX && *pat=='/') buf[pos++] = *pat++; + + /* Consume maximal [escaped-]literal prefix of pattern, copying + * and un-escaping it to the running buffer as we go. */ + ptrdiff_t i=0, j=0; + int in_bracket = 0, overflow = 0; + for (; pat[i]!='*' && pat[i]!='?' && (!in_bracket || pat[i]!=']'); i++) { + if (!pat[i]) { + if (overflow) return 0; + pat += i; + pos += j; + i = j = 0; + break; + } else if (pat[i] == '[') { + in_bracket = 1; + } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) { + /* Backslashes inside a bracket are (at least by + * our interpretation) non-special, so if next + * char is ']' we have a complete expression. */ + if (in_bracket && pat[i+1]==']') break; + /* Unpaired final backslash never matches. */ + if (!pat[i+1]) return 0; + i++; + } + if (pat[i] == '/') { + if (overflow) return 0; + in_bracket = 0; + pat += i+1; + i = -1; + pos += j+1; + j = -1; + } + /* Only store a character if it fits in the buffer, but if + * a potential bracket expression is open, the overflow + * must be remembered and handled later only if the bracket + * is unterminated (and thereby a literal), so as not to + * disallow long bracket expressions with short matches. */ + if (pos+(j+1) < PATH_MAX) { + buf[pos+j++] = pat[i]; + } else if (in_bracket) { + overflow = 1; + } else { + return 0; + } + /* If we consume any new components, the caller-passed type + * or dummy type from above is no longer valid. */ + type = 0; + } + buf[pos] = 0; + if (!*pat) { + /* If we consumed any components above, or if GLOB_MARK is + * requested and we don't yet know if the match is a dir, + * we must confirm the file exists and/or determine its type. + * + * If marking dirs, symlink type is inconclusive; we need the + * type for the symlink target, and therefore must try stat + * first unless type is known not to be a symlink. Otherwise, + * or if that fails, use lstat for determining existence to + * avoid false negatives in the case of broken symlinks. */ + struct stat st; + if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) { + if (S_ISDIR(st.st_mode)) type = DT_DIR; + else type = DT_REG; + } + if (!type && lstat(buf, &st)) { + if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) + return GLOB_ABORTED; + return 0; + } + if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR)) + return GLOB_NOSPACE; + return 0; + } + char *p2 = strchr(pat, '/'), saved_sep = '/'; + /* Check if the '/' was escaped and, if so, remove the escape char + * so that it will not be unpaired when passed to fnmatch. */ + if (p2 && !(flags & GLOB_NOESCAPE)) { + char *p; + for (p=p2; p>pat && p[-1]=='\\'; p--); + if ((p2-p)%2) { + p2--; + saved_sep = '\\'; + } + } + DIR *dir = opendir(pos ? buf : "."); + if (!dir) { + if (errfunc(buf, errno) || (flags & GLOB_ERR)) + return GLOB_ABORTED; + return 0; + } + int old_errno = errno; + struct dirent *de; + while (errno=0, de=readdir(dir)) { + /* Quickly skip non-directories when there's pattern left. */ + if (p2 && de->d_type && de->d_type!=DT_DIR && de->d_type!=DT_LNK) + continue; + + size_t l = strlen(de->d_name); + if (l >= PATH_MAX-pos) continue; + + if (p2) *p2 = 0; + + int fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) + | ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); + + if (fnmatch(pat, de->d_name, fnm_flags)) + continue; + + /* With GLOB_PERIOD, don't allow matching . or .. unless + * fnmatch would match them with FNM_PERIOD rules in effect. */ + if (p2 && (flags & GLOB_PERIOD) && de->d_name[0]=='.' + && (!de->d_name[1] || de->d_name[1]=='.' && !de->d_name[2]) + && fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD)) + continue; + + memcpy(buf+pos, de->d_name, l+1); + if (p2) *p2 = saved_sep; + int r = do_glob(buf, pos+l, de->d_type, p2 ? p2 : "", flags, errfunc, tail); + if (r) { + closedir(dir); + return r; + } + } + int readerr = errno; + if (p2) *p2 = saved_sep; + closedir(dir); + if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) + return GLOB_ABORTED; + errno = old_errno; + return 0; } -static int IgnoreGlobError(const char *path, int err) { - return 0; +static int ignore_err(const char *path, int err) +{ + return 0; } -static void FreeGlobList(struct GlobList *head) { - struct GlobList *match, *next; - for (match = head->next; match; match = next) { - next = match->next; - free(match); - } +static void freelist(struct match *head) +{ + struct match *match, *next; + for (match=head->next; match; match=next) { + next = match->next; + free(match); + } } -static int GlobPredicate(const void *a, const void *b) { - return strcmp(*(const char **)a, *(const char **)b); +static int sort(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +static int expand_tilde(char **pat, char *buf, size_t *pos) +{ + char *p = *pat + 1; + size_t i = 0; + + char delim, *name_end = strchrnul(p, '/'); + if ((delim = *name_end)) *name_end++ = 0; + *pat = name_end; + + char *home = *p ? NULL : getenv("HOME"); + if (!home) { + struct passwd pw, *res; + int e = *p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res) + : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res); + if (e == ENOMEM) { + return GLOB_NOSPACE; + } else if (e == 0) { + if (!res) + return GLOB_NOMATCH; + } else { + return GLOB_NOMATCH; + } + home = pw.pw_dir; + } + while (i < PATH_MAX - 2 && *home) + buf[i++] = *home++; + if (*home) + return GLOB_NOMATCH; + if ((buf[i] = delim)) + buf[++i] = 0; + *pos = i; + return 0; } /** @@ -239,81 +276,88 @@ static int GlobPredicate(const void *a, const void *b) { * @return 0 on success or GLOB_NOMATCH, GLOB_NOSPACE on OOM, or * GLOB_ABORTED on read error */ -int glob(const char *pat, int flags, int errfunc(const char *path, int err), - glob_t *g) { - int error = 0; - size_t cnt, i; - char **pathv, buf[MAXPATH]; - struct GlobList head = {.next = NULL}, *tail = &head; - size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; - if (!errfunc) errfunc = IgnoreGlobError; - if (!(flags & GLOB_APPEND)) { - g->gl_offs = offs; - g->gl_pathc = 0; - g->gl_pathv = NULL; - } - if (*pat) { - char *p = strdup(pat); - if (!p) return GLOB_NOSPACE; - buf[0] = 0; - error = PerformGlob(buf, 0, 0, p, flags, errfunc, &tail); - free(p); - } - if (error == GLOB_NOSPACE) { - FreeGlobList(&head); - return error; - } - for (cnt = 0, tail = head.next; tail; tail = tail->next, cnt++) - ; - if (!cnt) { - if (flags & GLOB_NOCHECK) { - tail = &head; - if (AppendGlob(&tail, pat, strlen(pat), 0)) { - return GLOB_NOSPACE; - } - cnt++; - } else - return GLOB_NOMATCH; - } - if (flags & GLOB_APPEND) { - pathv = - realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); - if (!pathv) { - FreeGlobList(&head); - return GLOB_NOSPACE; - } - g->gl_pathv = pathv; - offs += g->gl_pathc; - } else { - g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); - if (!g->gl_pathv) { - FreeGlobList(&head); - return GLOB_NOSPACE; - } - for (i = 0; i < offs; i++) { - g->gl_pathv[i] = NULL; - } - } - for (i = 0, tail = head.next; i < cnt; tail = tail->next, i++) { - g->gl_pathv[offs + i] = tail->name; - } - g->gl_pathv[offs + i] = NULL; - g->gl_pathc += cnt; - if (!(flags & GLOB_NOSORT)) { - qsort(g->gl_pathv + offs, cnt, sizeof(char *), GlobPredicate); - } - return error; +int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) +{ + struct match head = { .next = NULL }, *tail = &head; + size_t cnt, i; + size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; + int error = 0; + char buf[PATH_MAX]; + + if (!errfunc) errfunc = ignore_err; + + if (!(flags & GLOB_APPEND)) { + g->gl_offs = offs; + g->gl_pathc = 0; + g->gl_pathv = NULL; + } + + if (*pat) { + char *p = strdup(pat); + if (!p) return GLOB_NOSPACE; + buf[0] = 0; + size_t pos = 0; + char *s = p; + if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~') + error = expand_tilde(&s, buf, &pos); + if (!error) + error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); + free(p); + } + + if (error == GLOB_NOSPACE) { + freelist(&head); + return error; + } + + for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++); + if (!cnt) { + if (flags & GLOB_NOCHECK) { + tail = &head; + if (append(&tail, pat, strlen(pat), 0)) + return GLOB_NOSPACE; + cnt++; + } else if (!error) + return GLOB_NOMATCH; + } + + if (flags & GLOB_APPEND) { + char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); + if (!pathv) { + freelist(&head); + return GLOB_NOSPACE; + } + g->gl_pathv = pathv; + offs += g->gl_pathc; + } else { + g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); + if (!g->gl_pathv) { + freelist(&head); + return GLOB_NOSPACE; + } + for (i=0; igl_pathv[i] = NULL; + } + for (i=0, tail=head.next; inext, i++) + g->gl_pathv[offs + i] = tail->name; + g->gl_pathv[offs + i] = NULL; + g->gl_pathc += cnt; + + if (!(flags & GLOB_NOSORT)) + qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); + + return error; } /** * Frees entries allocated by glob(). */ -void globfree(glob_t *g) { - size_t i; - for (i = 0; i < g->gl_pathc; i++) { - free(g->gl_pathv[g->gl_offs + i] - offsetof(struct GlobList, name)); - } - free(g->gl_pathv); - g->gl_pathc = 0; - g->gl_pathv = NULL; +void globfree(glob_t *g) +{ + size_t i; + for (i=0; igl_pathc; i++) + free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); + free(g->gl_pathv); + g->gl_pathc = 0; + g->gl_pathv = NULL; } diff --git a/libc/stdio/iconv.c b/third_party/musl/iconv.c similarity index 97% rename from libc/stdio/iconv.c rename to third_party/musl/iconv.c index d565630d3..cea641664 100644 --- a/libc/stdio/iconv.c +++ b/third_party/musl/iconv.c @@ -29,12 +29,12 @@ #include "libc/errno.h" #include "libc/mem/mem.h" #include "libc/str/locale.h" +#include "libc/str/locale.internal.h" #include "libc/str/str.h" #include "libc/thread/tls.h" // clang-format off __static_yoink("musl_libc_notice"); - #define UTF_32BE 0300 #define UTF_16LE 0301 #define UTF_16BE 0302 @@ -77,10 +77,10 @@ static const unsigned char charmaps[] = "ucs4\0utf32\0\0\313" "ucs2\0\0\314" "eucjp\0\0\320" -"shiftjis\0sjis\0\0\321" +"shiftjis\0sjis\0cp932\0\0\321" "iso2022jp\0\0\322" "gb18030\0\0\330" -"gbk\0\0\331" +"gbk\0cp936\0windows936\0\0\331" "gb2312\0\0\332" "big5\0bigfive\0cp950\0big5hkscs\0\0\340" "euckr\0ksc5601\0ksx1001\0cp949\0\0\350" @@ -88,6 +88,7 @@ static const unsigned char charmaps[] = ; #pragma GCC diagnostic ignored "-Wmissing-braces" +#pragma GCC diagnostic ignored "-Wparentheses" /* Table of characters that appear in legacy 8-bit codepages, * limited to 1024 slots (10 bit indices). The first 256 entries @@ -237,7 +238,7 @@ static unsigned legacy_map(const unsigned char *map, unsigned c) { if (c < 4*map[-1]) return c; unsigned x = c - 4*map[-1]; - x = (map[x*5/4]>>(2*x%8)) | ((map[x*5/4+1]<<(8-(2*x%8))) & 1023); + x = map[x*5/4]>>2*x%8 | map[x*5/4+1]<<8-2*x%8 & 1023; return x < 256 ? x : legacy_chars[x-256]; } @@ -279,12 +280,11 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri int err; unsigned char type = map[-1]; unsigned char totype = tomap[-1]; - locale_t *ploc = (locale_t *)&__get_tls()->tib_locale; - locale_t loc = *ploc; + locale_t *ploc = &CURRENT_LOCALE, loc = *ploc; if (!in || !*in || !*inb) return 0; - *ploc = 0; // TODO(jart): UTF8_LOCALE? + *ploc = UTF8_LOCALE; for (; *inb; *in+=l, *inb-=l) { c = *(unsigned char *)*in; @@ -334,8 +334,6 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri case UCS2: case UTF_16: l = 0; - if (!scd) - goto starved; if (!scd->state) { if (*inb < 2) goto starved; c = get_16((void *)*in, 0); @@ -349,8 +347,6 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri continue; case UTF_32: l = 0; - if (!scd) - goto starved; if (!scd->state) { if (*inb < 4) goto starved; c = get_32((void *)*in, 0); @@ -381,6 +377,7 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri c++; d -= 159; } + if (c>=84) goto ilseq; c = jis0208[c][d]; if (!c) goto ilseq; break; @@ -402,7 +399,6 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri if (!c) goto ilseq; break; case ISO2022_JP: - if (!scd) goto starved; if (c >= 128) goto ilseq; if (c == '\033') { l = 3; @@ -445,6 +441,10 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri if (c < 128) break; if (c < 0xa1) goto ilseq; case GBK: + if (c == 128) { + c = 0x20ac; + break; + } case GB18030: if (c < 128) break; c -= 0x81; @@ -537,7 +537,7 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri if (c >= 93 || d >= 94) { c += (0xa1-0x81); d += 0xa1; - if (c >= 93 || ((c>=0xc6-0x81) && d>0x52)) + if (c >= 93 || c>=0xc6-0x81 && d>0x52) goto ilseq; if (d-'A'<26) d = d-'A'; else if (d-'a'<26) d = d-'a'+26; diff --git a/libc/str/langinfo.c b/third_party/musl/langinfo.c similarity index 93% rename from libc/str/langinfo.c rename to third_party/musl/langinfo.c index fe88cd54a..5dfa38271 100644 --- a/libc/str/langinfo.c +++ b/third_party/musl/langinfo.c @@ -25,14 +25,12 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/langinfo.h" -#include "libc/str/locale.h" -#include "libc/str/nltypes.h" -#include "libc/thread/tls.h" +#include +#include +#include "libc/intrin/kprintf.h" +#include "libc/str/locale.internal.h" __static_yoink("musl_libc_notice"); -// clang-format off - static const char c_time[] = "Sun\0" "Mon\0" "Tue\0" "Wed\0" "Thu\0" "Fri\0" "Sat\0" "Sunday\0" "Monday\0" "Tuesday\0" "Wednesday\0" @@ -63,9 +61,6 @@ char *nl_langinfo_l(nl_item item, locale_t loc) int idx = item & 65535; const char *str; - if (!loc) - return ""; - if (item == CODESET) return loc->cat[LC_CTYPE] ? "UTF-8" : "ASCII"; /* _NL_LOCALE_NAME extension */ @@ -94,11 +89,11 @@ char *nl_langinfo_l(nl_item item, locale_t loc) } for (; idx; idx--, str++) for (; *str; str++); - // if (cat != LC_NUMERIC && *str) str = LCTRANS(str, cat, loc); + if (cat != LC_NUMERIC && *str) str = LCTRANS(str, cat, loc); return (char *)str; } char *nl_langinfo(nl_item item) { - return nl_langinfo_l(item, (locale_t)__get_tls()->tib_locale); + return nl_langinfo_l(item, CURRENT_LOCALE); } diff --git a/third_party/musl/lctrans.c b/third_party/musl/lctrans.c new file mode 100644 index 000000000..eb02a9e9d --- /dev/null +++ b/third_party/musl/lctrans.c @@ -0,0 +1,46 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/locale.internal.h" +__static_yoink("musl_libc_notice"); + +const char *__lctrans_dummy(const char *msg, const struct __locale_map *lm) +{ + return msg; +} + +__weak_reference(__lctrans_dummy, __lctrans_impl); + +const char *__lctrans(const char *msg, const struct __locale_map *lm) +{ + return __lctrans_impl(msg, lm); +} + +const char *__lctrans_cur(const char *msg) +{ + return __lctrans_impl(msg, CURRENT_LOCALE->cat[LC_MESSAGES]); +} diff --git a/third_party/musl/locale_map.c b/third_party/musl/locale_map.c new file mode 100644 index 000000000..4cd0082d2 --- /dev/null +++ b/third_party/musl/locale_map.c @@ -0,0 +1,137 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "libc/calls/calls.h" +#include "third_party/musl/mapfile.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/str/locale.internal.h" +__static_yoink("musl_libc_notice"); + +#define malloc _mapanon +#define calloc undef +#define realloc undef +#define free undef + +#pragma GCC diagnostic ignored "-Wparentheses" + +const char *__lctrans_impl(const char *msg, const struct __locale_map *lm) +{ + const char *trans = 0; + if (lm) trans = __mo_lookup(lm->map, lm->map_size, msg); + return trans ? trans : msg; +} + +static const char envvars[][12] = { + "LC_CTYPE", + "LC_NUMERIC", + "LC_TIME", + "LC_COLLATE", + "LC_MONETARY", + "LC_MESSAGES", +}; + +const struct __locale_map *__get_locale(int cat, const char *val) +{ + static void *volatile loc_head; + const struct __locale_map *p; + struct __locale_map *new = 0; + const char *path = 0, *z; + char buf[256]; + size_t l, n; + + if (!*val) { + (val = getenv("LC_ALL")) && *val || + (val = getenv(envvars[cat])) && *val || + (val = getenv("LANG")) && *val || + (val = "C.UTF-8"); + } + + /* Limit name length and forbid leading dot or any slashes. */ + for (n=0; nnext) + if (!strcmp(val, p->name)) return p; + + path = secure_getenv("MUSL_LOCPATH"); + /* FIXME: add a default path? */ + + if (path) for (; *path; path=z+!!*z) { + z = strchrnul(path, ':'); + l = z - path; + if (l >= sizeof buf - n - 2) continue; + memcpy(buf, path, l); + buf[l] = '/'; + memcpy(buf+l+1, val, n); + buf[l+1+n] = 0; + size_t map_size; + const void *map = __map_file(buf, &map_size); + if (map) { + new = malloc(sizeof *new); + if (!new) { + munmap((void *)map, map_size); + break; + } + new->map = map; + new->map_size = map_size; + memcpy(new->name, val, n); + new->name[n] = 0; + new->next = loc_head; + loc_head = new; + break; + } + } + + /* If no locale definition was found, make a locale map + * object anyway to store the name, which is kept for the + * sake of being able to do message translations at the + * application level. */ + if (!new && (new = malloc(sizeof *new))) { + new->map = __c_dot_utf8.map; + new->map_size = __c_dot_utf8.map_size; + memcpy(new->name, val, n); + new->name[n] = 0; + new->next = loc_head; + loc_head = new; + } + + /* For LC_CTYPE, never return a null pointer unless the + * requested name was "C" or "POSIX". */ + if (!new && cat == LC_CTYPE) new = (void *)&__c_dot_utf8; + + return new; +} diff --git a/third_party/musl/mblen.c b/third_party/musl/mblen.c new file mode 100644 index 000000000..6d88cc3e5 --- /dev/null +++ b/third_party/musl/mblen.c @@ -0,0 +1,34 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +__static_yoink("musl_libc_notice"); + +int mblen(const char *s, size_t n) +{ + return mbtowc(0, s, n); +} diff --git a/third_party/musl/mbrlen.c b/third_party/musl/mbrlen.c new file mode 100644 index 000000000..519d75ea8 --- /dev/null +++ b/third_party/musl/mbrlen.c @@ -0,0 +1,35 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +__static_yoink("musl_libc_notice"); + +size_t mbrlen(const char *restrict s, size_t n, mbstate_t *restrict st) +{ + static unsigned internal; + return mbrtowc(0, s, n, st ? st : (mbstate_t *)&internal); +} diff --git a/third_party/musl/mbrtoc16.c b/third_party/musl/mbrtoc16.c new file mode 100644 index 000000000..b484fc532 --- /dev/null +++ b/third_party/musl/mbrtoc16.c @@ -0,0 +1,58 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +__static_yoink("musl_libc_notice"); + +size_t mbrtoc16(char16_t *restrict pc16, const char *restrict s, size_t n, mbstate_t *restrict ps) +{ + static unsigned internal_state; + if (!ps) ps = (void *)&internal_state; + unsigned *pending = (unsigned *)ps; + + if (!s) return mbrtoc16(0, "", 1, ps); + + /* mbrtowc states for partial UTF-8 characters have the high bit set; + * we use nonzero states without high bit for pending surrogates. */ + if ((int)*pending > 0) { + if (pc16) *pc16 = *pending; + *pending = 0; + return -3; + } + + wchar_t wc; + size_t ret = mbrtowc(&wc, s, n, ps); + if (ret <= 4) { + if (wc >= 0x10000) { + *pending = (wc & 0x3ff) + 0xdc00; + wc = 0xd7c0 + (wc >> 10); + } + if (pc16) *pc16 = wc; + } + return ret; +} diff --git a/libc/str/mbrtoc32.c b/third_party/musl/mbrtoc32.c similarity index 81% rename from libc/str/mbrtoc32.c rename to third_party/musl/mbrtoc32.c index 535cb4f2f..df14183cd 100644 --- a/libc/str/mbrtoc32.c +++ b/third_party/musl/mbrtoc32.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,21 +25,17 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/macros.internal.h" -#include "libc/str/str.h" +#include +#include __static_yoink("musl_libc_notice"); -size_t mbrtoc32(char32_t *pc32, const char *s, size_t n, mbstate_t *ps) { - static unsigned internal_state; - if (!ps) - ps = (void *)&internal_state; - if (!s) - return mbrtoc32(0, "", 1, ps); - wchar_t wc; - size_t ret = mbrtowc(&wc, s, n, ps); - if (ret <= 4 && pc32) - *pc32 = wc; - return ret; +size_t mbrtoc32(char32_t *restrict pc32, const char *restrict s, size_t n, mbstate_t *restrict ps) +{ + static unsigned internal_state; + if (!ps) ps = (void *)&internal_state; + if (!s) return mbrtoc32(0, "", 1, ps); + wchar_t wc; + size_t ret = mbrtowc(&wc, s, n, ps); + if (ret <= 4 && pc32) *pc32 = wc; + return ret; } diff --git a/third_party/musl/mbrtowc.c b/third_party/musl/mbrtowc.c new file mode 100644 index 000000000..a221e3d7c --- /dev/null +++ b/third_party/musl/mbrtowc.c @@ -0,0 +1,81 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +#include +#include "multibyte.h" +__static_yoink("musl_libc_notice"); + +#pragma GCC diagnostic ignored "-Wparentheses" + +size_t mbrtowc(wchar_t *restrict wc, const char *restrict src, size_t n, mbstate_t *restrict st) +{ + static unsigned internal_state; + unsigned c; + const unsigned char *s = (const void *)src; + const size_t N = n; + wchar_t dummy; + + if (!st) st = (void *)&internal_state; + c = *(unsigned *)st; + + if (!s) { + if (c) goto ilseq; + return 0; + } else if (!wc) wc = &dummy; + + if (!n) return -2; + if (!c) { + if (*s < 0x80) return !!(*wc = *s); + if (MB_CUR_MAX==1) return (*wc = CODEUNIT(*s)), 1; + if (*s-SA > SB-SA) goto ilseq; + c = bittab[*s++-SA]; n--; + } + + if (n) { + if (OOB(c,*s)) goto ilseq; +loop: + c = c<<6 | *s++-0x80; n--; + if (!(c&(1U<<31))) { + *(unsigned *)st = 0; + *wc = c; + return N-n; + } + if (n) { + if (*s-0x80u >= 0x40) goto ilseq; + goto loop; + } + } + + *(unsigned *)st = c; + return -2; +ilseq: + *(unsigned *)st = 0; + errno = EILSEQ; + return -1; +} diff --git a/third_party/musl/mbsinit.c b/third_party/musl/mbsinit.c new file mode 100644 index 000000000..e6a0dbe69 --- /dev/null +++ b/third_party/musl/mbsinit.c @@ -0,0 +1,34 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +__static_yoink("musl_libc_notice"); + +int mbsinit(const mbstate_t *st) +{ + return !st || !*(unsigned *)st; +} diff --git a/third_party/musl/mbsnrtowcs.c b/third_party/musl/mbsnrtowcs.c new file mode 100644 index 000000000..ffd778bc1 --- /dev/null +++ b/third_party/musl/mbsnrtowcs.c @@ -0,0 +1,83 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +__static_yoink("musl_libc_notice"); + +size_t mbsnrtowcs(wchar_t *restrict wcs, const char **restrict src, size_t n, size_t wn, mbstate_t *restrict st) +{ + size_t l, cnt=0, n2; + wchar_t *ws, wbuf[256]; + const char *s = *src; + const char *tmp_s; + + if (!wcs) ws = wbuf, wn = sizeof wbuf / sizeof *wbuf; + else ws = wcs; + + /* making sure output buffer size is at most n/4 will ensure + * that mbsrtowcs never reads more than n input bytes. thus + * we can use mbsrtowcs as long as it's practical.. */ + + while ( s && wn && ( (n2=n/4)>=wn || n2>32 ) ) { + if (n2>=wn) n2=wn; + tmp_s = s; + l = mbsrtowcs(ws, &s, n2, st); + if (!(l+1)) { + cnt = l; + wn = 0; + break; + } + if (ws != wbuf) { + ws += l; + wn -= l; + } + n = s ? n - (s - tmp_s) : 0; + cnt += l; + } + if (s) while (wn && n) { + l = mbrtowc(ws, s, n, st); + if (l+2<=2) { + if (!(l+1)) { + cnt = l; + break; + } + if (!l) { + s = 0; + break; + } + /* have to roll back partial character */ + *(unsigned *)st = 0; + break; + } + s += l; n -= l; + /* safe - this loop runs fewer than sizeof(wbuf)/8 times */ + ws++; wn--; + cnt++; + } + if (wcs) *src = s; + return cnt; +} diff --git a/third_party/musl/mbsrtowcs.c b/third_party/musl/mbsrtowcs.c new file mode 100644 index 000000000..a51b1180e --- /dev/null +++ b/third_party/musl/mbsrtowcs.c @@ -0,0 +1,150 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +#include +#include +#include +#include "multibyte.h" +__static_yoink("musl_libc_notice"); + +#pragma GCC diagnostic ignored "-Wparentheses" + +size_t mbsrtowcs(wchar_t *restrict ws, const char **restrict src, size_t wn, mbstate_t *restrict st) +{ + const unsigned char *s = (const void *)*src; + size_t wn0 = wn; + unsigned c = 0; + + if (st && (c = *(unsigned *)st)) { + if (ws) { + *(unsigned *)st = 0; + goto resume; + } else { + goto resume0; + } + } + + if (MB_CUR_MAX==1) { + if (!ws) return strlen((const char *)s); + for (;;) { + if (!wn) { + *src = (const void *)s; + return wn0; + } + if (!*s) break; + c = *s++; + *ws++ = CODEUNIT(c); + wn--; + } + *ws = 0; + *src = 0; + return wn0-wn; + } + + if (!ws) for (;;) { +#ifdef __GNUC__ + typedef uint32_t __attribute__((__may_alias__)) w32; + if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) { + while (!(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) { + s += 4; + wn -= 4; + } + } +#endif + if (*s-1u < 0x7f) { + s++; + wn--; + continue; + } + if (*s-SA > SB-SA) break; + c = bittab[*s++-SA]; +resume0: + if (OOB(c,*s)) { s--; break; } + s++; + if (c&(1U<<25)) { + if (*s-0x80u >= 0x40) { s-=2; break; } + s++; + if (c&(1U<<19)) { + if (*s-0x80u >= 0x40) { s-=3; break; } + s++; + } + } + wn--; + c = 0; + } else for (;;) { + if (!wn) { + *src = (const void *)s; + return wn0; + } +#ifdef __GNUC__ + typedef uint32_t __attribute__((__may_alias__)) w32; + if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) { + while (wn>=5 && !(( *(w32*)s | *(w32*)s-0x01010101) & 0x80808080)) { + *ws++ = *s++; + *ws++ = *s++; + *ws++ = *s++; + *ws++ = *s++; + wn -= 4; + } + } +#endif + if (*s-1u < 0x7f) { + *ws++ = *s++; + wn--; + continue; + } + if (*s-SA > SB-SA) break; + c = bittab[*s++-SA]; +resume: + if (OOB(c,*s)) { s--; break; } + c = (c<<6) | *s++-0x80; + if (c&(1U<<31)) { + if (*s-0x80u >= 0x40) { s-=2; break; } + c = (c<<6) | *s++-0x80; + if (c&(1U<<31)) { + if (*s-0x80u >= 0x40) { s-=3; break; } + c = (c<<6) | *s++-0x80; + } + } + *ws++ = c; + wn--; + c = 0; + } + + if (!c && !*s) { + if (ws) { + *ws = 0; + *src = 0; + } + return wn0-wn; + } + errno = EILSEQ; + if (ws) *src = (const void *)s; + return -1; +} diff --git a/third_party/musl/mbstowcs.c b/third_party/musl/mbstowcs.c new file mode 100644 index 000000000..682a7db8c --- /dev/null +++ b/third_party/musl/mbstowcs.c @@ -0,0 +1,35 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +__static_yoink("musl_libc_notice"); + +size_t mbstowcs(wchar_t *restrict ws, const char *restrict s, size_t wn) +{ + return mbsrtowcs(ws, (void*)&s, wn, 0); +} diff --git a/libc/str/mbtowc.c b/third_party/musl/mbtowc.c similarity index 68% rename from libc/str/mbtowc.c rename to third_party/musl/mbtowc.c index 34b5f773d..849d40898 100644 --- a/libc/str/mbtowc.c +++ b/third_party/musl/mbtowc.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,53 +25,53 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" +#include +#include +#include +#include "multibyte.h" __static_yoink("musl_libc_notice"); -int mbtowc(wchar_t *restrict wc, const char *restrict src, size_t n) { - unsigned c; - const unsigned char *s = (const void *)src; - wchar_t dummy; - if (!s) - return 0; - if (!n) - goto ilseq; - if (!wc) - wc = &dummy; - if (*s < 0x80) - return !!(*wc = *s); - if (MB_CUR_MAX == 1) - return (*wc = CODEUNIT(*s)), 1; - if (*s - SA > SB - SA) - goto ilseq; - c = kMbBittab[*s++ - SA]; - /* Avoid excessive checks against n: If shifting the state n-1 - * times does not clear the high bit, then the value of n is - * insufficient to read a character */ - if (n < 4 && ((c << (6 * n - 6)) & (1U << 31))) - goto ilseq; - if (OOB(c, *s)) - goto ilseq; - c = c << 6 | (*s++ - 0x80); - if (!(c & (1U << 31))) { - *wc = c; - return 2; - } - if (*s - 0x80u >= 0x40) - goto ilseq; - c = c << 6 | (*s++ - 0x80); - if (!(c & (1U << 31))) { - *wc = c; - return 3; - } - if (*s - 0x80u >= 0x40) - goto ilseq; - *wc = c << 6 | (*s++ - 0x80); - return 4; +#pragma GCC diagnostic ignored "-Wparentheses" + +int mbtowc(wchar_t *restrict wc, const char *restrict src, size_t n) +{ + unsigned c; + const unsigned char *s = (const void *)src; + wchar_t dummy; + + if (!s) return 0; + if (!n) goto ilseq; + if (!wc) wc = &dummy; + + if (*s < 0x80) return !!(*wc = *s); + if (MB_CUR_MAX==1) return (*wc = CODEUNIT(*s)), 1; + if (*s-SA > SB-SA) goto ilseq; + c = bittab[*s++-SA]; + + /* Avoid excessive checks against n: If shifting the state n-1 + * times does not clear the high bit, then the value of n is + * insufficient to read a character */ + if (n<4 && ((c<<(6*n-6)) & (1U<<31))) goto ilseq; + + if (OOB(c,*s)) goto ilseq; + c = c<<6 | *s++-0x80; + if (!(c&(1U<<31))) { + *wc = c; + return 2; + } + + if (*s-0x80u >= 0x40) goto ilseq; + c = c<<6 | *s++-0x80; + if (!(c&(1U<<31))) { + *wc = c; + return 3; + } + + if (*s-0x80u >= 0x40) goto ilseq; + *wc = c<<6 | *s++-0x80; + return 4; + ilseq: - errno = EILSEQ; - return -1; + errno = EILSEQ; + return -1; } diff --git a/third_party/musl/multibyte.c b/third_party/musl/multibyte.c new file mode 100644 index 000000000..37a23683c --- /dev/null +++ b/third_party/musl/multibyte.c @@ -0,0 +1,53 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "multibyte.h" + +#define C(x) ( x<2 ? -1 : ( R(0x80,0xc0) | x ) ) +#define D(x) C((x+16)) +#define E(x) ( ( x==0 ? R(0xa0,0xc0) : \ + x==0xd ? R(0x80,0xa0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | x ) +#define F(x) ( ( x>=5 ? 0 : \ + x==0 ? R(0x90,0xc0) : \ + x==4 ? R(0x80,0x90) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | ( R(0x80,0xc0) >> 12 ) \ + | x ) + +const uint32_t bittab[] = { + C(0x2),C(0x3),C(0x4),C(0x5),C(0x6),C(0x7), + C(0x8),C(0x9),C(0xa),C(0xb),C(0xc),C(0xd),C(0xe),C(0xf), + D(0x0),D(0x1),D(0x2),D(0x3),D(0x4),D(0x5),D(0x6),D(0x7), + D(0x8),D(0x9),D(0xa),D(0xb),D(0xc),D(0xd),D(0xe),D(0xf), + E(0x0),E(0x1),E(0x2),E(0x3),E(0x4),E(0x5),E(0x6),E(0x7), + E(0x8),E(0x9),E(0xa),E(0xb),E(0xc),E(0xd),E(0xe),E(0xf), + F(0x0),F(0x1),F(0x2),F(0x3),F(0x4) +}; diff --git a/third_party/musl/multibyte.h b/third_party/musl/multibyte.h new file mode 100644 index 000000000..e55842fa0 --- /dev/null +++ b/third_party/musl/multibyte.h @@ -0,0 +1,26 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_MULTIBYTE_H_ +#define COSMOPOLITAN_THIRD_PARTY_MUSL_MULTIBYTE_H_ + +#define bittab __fsmu8 + +extern const uint32_t bittab[]; + +/* Upper 6 state bits are a negative integer offset to bound-check next byte */ +/* equivalent to: ( (b-0x80) | (b+offset) ) & ~0x3f */ +#define OOB(c,b) (((((b)>>3)-0x10)|(((b)>>3)+((int32_t)(c)>>26))) & ~7) + +/* Interval [a,b). Either a must be 80 or b must be c0, lower 3 bits clear. */ +#define R(a,b) ((uint32_t)((a==0x80 ? 0x40u-b : 0u-a) << 23)) +#define FAILSTATE R(0x80,0x80) + +#define SA 0xc2u +#define SB 0xf4u + +/* Arbitrary encoding for representing code units instead of characters. */ +#define CODEUNIT(c) (0xdfff & (signed char)(c)) +#define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80) + +/* Get inline definition of MB_CUR_MAX. */ +#include "libc/str/locale.internal.h" + +#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_MULTIBYTE_H_ */ diff --git a/third_party/musl/newlocale.c b/third_party/musl/newlocale.c new file mode 100644 index 000000000..fb63d7410 --- /dev/null +++ b/third_party/musl/newlocale.c @@ -0,0 +1,94 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/str/locale.internal.h" +__static_yoink("musl_libc_notice"); + +#define malloc _mapanon +#define calloc undef +#define realloc undef +#define free undef + +static int default_locale_init_done; +static struct __locale_struct default_locale, default_ctype_locale; + +int __loc_is_allocated(locale_t loc) +{ + return loc && loc != C_LOCALE && loc != UTF8_LOCALE + && loc != &default_locale && loc != &default_ctype_locale; +} + +static locale_t do_newlocale(int mask, const char *name, locale_t loc) +{ + struct __locale_struct tmp; + + for (int i=0; icat[i] : + __get_locale(i, (mask & (1<= wn || n2 > 32)) { - if (n2 >= wn) - n2 = wn; - tmp_s = s; - l = mbsrtowcs(ws, &s, n2, st); - if (!(l + 1)) { - cnt = l; - wn = 0; - break; - } - if (ws != wbuf) { - ws += l; - wn -= l; - } - n = s ? n - (s - tmp_s) : 0; - cnt += l; - } - if (s) - while (wn && n) { - l = mbrtowc(ws, s, n, st); - if (l + 2 <= 2) { - if (!(l + 1)) { - cnt = l; - break; - } - if (!l) { - s = 0; - break; - } - /* have to roll back partial character */ - *(unsigned *)st = 0; - break; - } - s += l; - n -= l; - /* safe - this loop runs fewer than sizeof(wbuf)/8 times */ - ws++; - wn--; - cnt++; - } - if (wcs) - *src = s; - return cnt; +static char buf[LC_ALL*(LOCALE_NAME_MAX+1)]; + +char *setlocale(int cat, const char *name) +{ + const struct __locale_map *lm; + + if ((unsigned)cat > LC_ALL) return 0; + + pthread_mutex_lock(&__locale_lock); + + /* For LC_ALL, setlocale is required to return a string which + * encodes the current setting for all categories. The format of + * this string is unspecified, and only the following code, which + * performs both the serialization and deserialization, depends + * on the format, so it can easily be changed if needed. */ + if (cat == LC_ALL) { + int i; + if (name) { + struct __locale_struct tmp_locale; + char part[LOCALE_NAME_MAX+1] = "C.UTF-8"; + const char *p = name; + for (i=0; iname : "C"; + size_t l = strlen(part); + memcpy(s, part, l); + s[l] = ';'; + s += l+1; + } + *--s = 0; + pthread_mutex_unlock(&__locale_lock); + return same==LC_ALL ? (char *)part : buf; + } + + if (name) { + lm = __get_locale(cat, name); + if (lm == LOC_MAP_FAILED) { + pthread_mutex_unlock(&__locale_lock); + return 0; + } + __global_locale.cat[cat] = lm; + } else { + lm = __global_locale.cat[cat]; + } + char *ret = lm ? (char *)lm->name : "C"; + + pthread_mutex_unlock(&__locale_lock); + + return ret; } diff --git a/third_party/musl/strfmon.c b/third_party/musl/strfmon.c index b80801d5f..d6c436ae4 100644 --- a/third_party/musl/strfmon.c +++ b/third_party/musl/strfmon.c @@ -27,7 +27,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" #include "libc/stdio/stdio.h" -#include "libc/str/locale.h" +#include "libc/str/locale.internal.h" #include "libc/str/str.h" #include "libc/ctype.h" #include "libc/thread/tls.h" @@ -37,7 +37,7 @@ static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_l { size_t l; double x; - int left; + int fill, nogrp, negpar, nosym, left, intl; int lp, rp, w, fw; char *s0=s; for (; n && *fmt; ) { @@ -50,17 +50,29 @@ static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_l fmt++; if (*fmt == '%') goto literal; + fill = ' '; + nogrp = 0; + negpar = 0; + nosym = 0; left = 0; for (; ; fmt++) { switch (*fmt) { case '=': + fill = *++fmt; + (void)fill; continue; case '^': + nogrp = 1; + (void)nogrp; continue; case '(': + negpar = 1; + (void)negpar; case '+': continue; case '!': + nosym = 1; + (void)nosym; continue; case '-': left = 1; @@ -78,6 +90,9 @@ static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_l if (*fmt=='.') for (rp=0, fmt++; isdigit(*fmt); fmt++) rp = 10*rp + (*fmt-'0'); + intl = *fmt++ == 'i'; + (void)intl; + w = lp + 1 + rp; if (!left && fw>w) w = fw; @@ -112,7 +127,7 @@ ssize_t strfmon(char *restrict s, size_t n, const char *restrict fmt, ...) ssize_t ret; va_start(ap, fmt); - ret = vstrfmon_l(s, n, (locale_t)__get_tls()->tib_locale, fmt, ap); + ret = vstrfmon_l(s, n, CURRENT_LOCALE, fmt, ap); va_end(ap); return ret; diff --git a/third_party/musl/strftime.c b/third_party/musl/strftime.c new file mode 100644 index 000000000..b83aa3e08 --- /dev/null +++ b/third_party/musl/strftime.c @@ -0,0 +1,313 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/ctype.h" +#include "libc/limits.h" +#include "libc/stdio/stdio.h" +#include "libc/str/langinfo.h" +#include "libc/str/locale.h" +#include "libc/str/locale.internal.h" +#include "libc/str/nltypes.h" +#include "libc/str/str.h" +#include "libc/time.h" +#include "third_party/musl/time_impl.h" +__static_yoink("musl_libc_notice"); + +static int is_leap(int y) +{ + /* Avoid overflow */ + if (y>INT_MAX-1900) y -= 2000; + y += 1900; + return !(y%4) && ((y%100) || !(y%400)); +} + +static int week_num(const struct tm *tm) +{ + int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + /* If 1 Jan is just 1-3 days past Monday, + * the previous week is also in this year. */ + if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2) + val++; + if (!val) { + val = 52; + /* If 31 December of prev year a Thursday, + * or Friday of a leap year, then the + * prev year has 53 weeks. */ + int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7; + if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) + val++; + } else if (val == 53) { + /* If 1 January is not a Thursday, and not + * a Wednesday of a leap year, then this + * year has only 52 weeks. */ + int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7; + if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year))) + val = 1; + } + return val; +} + +const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) +{ + nl_item item; + long long val; + const char *fmt = "-"; + int width = 2, def_pad = '0'; + + switch (f) { + case 'a': + if (tm->tm_wday > 6U) goto string; + item = ABDAY_1 + tm->tm_wday; + goto nl_strcat; + case 'A': + if (tm->tm_wday > 6U) goto string; + item = DAY_1 + tm->tm_wday; + goto nl_strcat; + case 'h': + case 'b': + if (tm->tm_mon > 11U) goto string; + item = ABMON_1 + tm->tm_mon; + goto nl_strcat; + case 'B': + if (tm->tm_mon > 11U) goto string; + item = MON_1 + tm->tm_mon; + goto nl_strcat; + case 'c': + item = D_T_FMT; + goto nl_strftime; + case 'C': + val = (1900LL+tm->tm_year) / 100; + goto number; + case 'e': + def_pad = '_'; + case 'd': + val = tm->tm_mday; + goto number; + case 'D': + fmt = "%m/%d/%y"; + goto recu_strftime; + case 'F': + fmt = "%Y-%m-%d"; + goto recu_strftime; + case 'g': + case 'G': + val = tm->tm_year + 1900LL; + if (tm->tm_yday < 3 && week_num(tm) != 1) val--; + else if (tm->tm_yday > 360 && week_num(tm) == 1) val++; + if (f=='g') val %= 100; + else width = 4; + goto number; + case 'H': + val = tm->tm_hour; + goto number; + case 'I': + val = tm->tm_hour; + if (!val) val = 12; + else if (val > 12) val -= 12; + goto number; + case 'j': + val = tm->tm_yday+1; + width = 3; + goto number; + case 'm': + val = tm->tm_mon+1; + goto number; + case 'M': + val = tm->tm_min; + goto number; + case 'n': + *l = 1; + return "\n"; + case 'p': + item = tm->tm_hour >= 12 ? PM_STR : AM_STR; + goto nl_strcat; + case 'r': + item = T_FMT_AMPM; + goto nl_strftime; + case 'R': + fmt = "%H:%M"; + goto recu_strftime; + case 's': + val = __tm_to_secs(tm) - tm->tm_gmtoff; + width = 1; + goto number; + case 'S': + val = tm->tm_sec; + goto number; + case 't': + *l = 1; + return "\t"; + case 'T': + fmt = "%H:%M:%S"; + goto recu_strftime; + case 'u': + val = tm->tm_wday ? tm->tm_wday : 7; + width = 1; + goto number; + case 'U': + val = (tm->tm_yday + 7U - tm->tm_wday) / 7; + goto number; + case 'W': + val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + goto number; + case 'V': + val = week_num(tm); + goto number; + case 'w': + val = tm->tm_wday; + width = 1; + goto number; + case 'x': + item = D_FMT; + goto nl_strftime; + case 'X': + item = T_FMT; + goto nl_strftime; + case 'y': + val = (tm->tm_year + 1900LL) % 100; + if (val < 0) val = -val; + goto number; + case 'Y': + val = tm->tm_year + 1900LL; + if (val >= 10000) { + *l = snprintf(*s, sizeof *s, "%lld", val); + return *s; + } + width = 4; + goto number; + case 'z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } + *l = snprintf(*s, sizeof *s, "%+.4ld", + tm->tm_gmtoff/3600*100 + tm->tm_gmtoff%3600/60); + return *s; + case 'Z': + if (tm->tm_isdst < 0 || !tm->tm_zone) { + *l = 0; + return ""; + } + fmt = tm->tm_zone; + goto string; + case '%': + *l = 1; + return "%"; + default: + return 0; + } +number: + switch (pad ? pad : def_pad) { + case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break; + case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break; + case '0': + default: *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break; + } + return *s; +nl_strcat: + fmt = nl_langinfo_l(item, loc); +string: + *l = strlen(fmt); + return fmt; +nl_strftime: + fmt = nl_langinfo_l(item, loc); +recu_strftime: + *l = strftime_l(*s, sizeof *s, fmt, tm, loc); + if (!*l) return 0; + return *s; +} + +size_t strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) +{ + size_t l, k; + char buf[100]; + char *p; + const char *t; + int pad, plus; + unsigned long width; + for (l=0; ltm_year < -1900) { + s[l++] = '-'; + width--; + } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { + s[l++] = '+'; + width--; + } + for (; width > k && l < n; width--) + s[l++] = '0'; + } + if (k > n-l) k = n-l; + memcpy(s+l, t, k); + l += k; + } + if (n) { + if (l==n) l=n-1; + s[l] = 0; + } + return 0; +} + +size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) +{ + return strftime_l(s, n, f, tm, CURRENT_LOCALE); +} diff --git a/third_party/musl/strptime.c b/third_party/musl/strptime.c index 682d33d77..afbad2570 100644 --- a/third_party/musl/strptime.c +++ b/third_party/musl/strptime.c @@ -29,248 +29,269 @@ #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/ctype.h" +#include "libc/str/langinfo.h" #include "libc/time.h" __static_yoink("musl_libc_notice"); -char * -strptime(const char *s, const char *f, struct tm *tm) +char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm) { - int i, w, neg, adj, min, range, itemsize, *dest, dummy; - const char *ex, *ss; + int i, w, neg, adj, min, range, *dest, dummy; + const char *ex; size_t len; int want_century = 0, century = 0, relyear = 0; while (*f) { if (*f != '%') { - if (isspace(*f)) { - for (; *s && isspace(*s); s++); - } else if (*s != *f) { - return 0; - } else { - s++; - } + if (isspace(*f)) for (; *s && isspace(*s); s++); + else if (*s != *f) return 0; + else s++; f++; continue; } f++; - if (*f == '+') - f++; + if (*f == '+') f++; if (isdigit(*f)) { char *new_f; - w = strtoul(f, &new_f, 10); + w=strtoul(f, &new_f, 10); f = new_f; } else { - w = -1; + w=-1; } - adj = 0; + adj=0; switch (*f++) { - case 'a': - dest = &tm->tm_wday; - ss = (const char *)kWeekdayNameShort; - range = ARRAYLEN(kWeekdayNameShort); - itemsize = sizeof(kWeekdayNameShort[0]); - goto symbolic_range; - case 'A': - dest = &tm->tm_wday; - ss = (const char *)kWeekdayName; - range = ARRAYLEN(kWeekdayName); - itemsize = sizeof(kWeekdayName[0]); - goto symbolic_range; - case 'b': - case 'h': - dest = &tm->tm_mon; - ss = (const char *)kMonthNameShort; - range = ARRAYLEN(kMonthNameShort); - itemsize = sizeof(kMonthNameShort[0]); - goto symbolic_range; - case 'B': - dest = &tm->tm_mon; - ss = (const char *)kMonthName; - range = ARRAYLEN(kMonthName); - itemsize = sizeof(kMonthName[0]); - goto symbolic_range; - case 'c': - s = strptime(s, "%a %b %e %T %Y", tm); - if (!s) - return 0; + case 'a': case 'A': + dest = &tm->tm_wday; + min = ABDAY_1; + range = 7; + goto symbolic_range; + case 'b': case 'B': case 'h': + dest = &tm->tm_mon; + min = ABMON_1; + range = 12; + goto symbolic_range; + case 'c': + s = strptime(s, nl_langinfo(D_T_FMT), tm); + if (!s) return 0; + break; + case 'C': + dest = ¢ury; + if (w<0) w=2; + want_century |= 2; + goto numeric_digits; + case 'd': case 'e': + dest = &tm->tm_mday; + min = 1; + range = 31; + goto numeric_range; + case 'D': + s = strptime(s, "%m/%d/%y", tm); + if (!s) return 0; + break; + case 'F': + /* Use temp buffer to implement the odd requirement + * that entire field be width-limited but the year + * subfield not itself be limited. */ + i = 0; + char tmp[20]; + if (*s == '-' || *s == '+') tmp[i++] = *s++; + while (*s=='0' && isdigit(s[1])) s++; + for (; *s && i<(size_t)w && i+1tm_hour; + min = 0; + range = 24; + goto numeric_range; + case 'I': + dest = &tm->tm_hour; + min = 1; + range = 12; + goto numeric_range; + case 'j': + dest = &tm->tm_yday; + min = 1; + range = 366; + adj = 1; + goto numeric_range; + case 'm': + dest = &tm->tm_mon; + min = 1; + range = 12; + adj = 1; + goto numeric_range; + case 'M': + dest = &tm->tm_min; + min = 0; + range = 60; + goto numeric_range; + case 'n': case 't': + for (; *s && isspace(*s); s++); + break; + case 'p': + ex = nl_langinfo(AM_STR); + len = strlen(ex); + if (!strncasecmp(s, ex, len)) { + tm->tm_hour %= 12; + s += len; break; - case 'C': - dest = ¢ury; - if (w < 0) - w = 2; - want_century |= 2; - goto numeric_digits; - case 'd': - case 'e': - dest = &tm->tm_mday; - min = 1; - range = 31; - goto numeric_range; - case 'D': - s = strptime(s, "%m/%d/%y", tm); - if (!s) - return 0; + } + ex = nl_langinfo(PM_STR); + len = strlen(ex); + if (!strncasecmp(s, ex, len)) { + tm->tm_hour %= 12; + tm->tm_hour += 12; + s += len; break; - case 'H': - dest = &tm->tm_hour; - min = 0; - range = 24; - goto numeric_range; - case 'I': - dest = &tm->tm_hour; - min = 1; - range = 12; - goto numeric_range; - case 'j': - dest = &tm->tm_yday; - min = 1; - range = 366; - adj = 1; - goto numeric_range; - case 'm': - dest = &tm->tm_mon; - min = 1; - range = 12; - adj = 1; - goto numeric_range; - case 'M': - dest = &tm->tm_min; - min = 0; - range = 60; - goto numeric_range; - case 'n': - case 't': - for (; *s && isspace(*s); s++); - break; - case 'p': - ex = "AM"; + } + return 0; + case 'r': + s = strptime(s, nl_langinfo(T_FMT_AMPM), tm); + if (!s) return 0; + break; + case 'R': + s = strptime(s, "%H:%M", tm); + if (!s) return 0; + break; + case 's': + /* Parse only. Effect on tm is unspecified + * and presently no effect is implemented.. */ + if (*s == '-') s++; + if (!isdigit(*s)) return 0; + while (isdigit(*s)) s++; + break; + case 'S': + dest = &tm->tm_sec; + min = 0; + range = 61; + goto numeric_range; + case 'T': + s = strptime(s, "%H:%M:%S", tm); + if (!s) return 0; + break; + case 'U': + case 'W': + /* Throw away result of %U, %V, %W, %g, and %G. Effect + * is unspecified and there is no clear right choice. */ + dest = &dummy; + min = 0; + range = 54; + goto numeric_range; + case 'V': + dest = &dummy; + min = 1; + range = 53; + goto numeric_range; + case 'g': + dest = &dummy; + w = 2; + goto numeric_digits; + case 'G': + dest = &dummy; + if (w<0) w=4; + goto numeric_digits; + case 'u': + dest = &tm->tm_wday; + min = 1; + range = 7; + goto numeric_range; + case 'w': + dest = &tm->tm_wday; + min = 0; + range = 7; + goto numeric_range; + case 'x': + s = strptime(s, nl_langinfo(D_FMT), tm); + if (!s) return 0; + break; + case 'X': + s = strptime(s, nl_langinfo(T_FMT), tm); + if (!s) return 0; + break; + case 'y': + dest = &relyear; + w = 2; + want_century |= 1; + goto numeric_digits; + case 'Y': + dest = &tm->tm_year; + if (w<0) w=4; + adj = 1900; + want_century = 0; + goto numeric_digits; + case 'z': + if (*s == '+') neg = 0; + else if (*s == '-') neg = 1; + else return 0; + for (i=0; i<4; i++) if (!isdigit(s[1+i])) return 0; + tm->tm_gmtoff = (s[1]-'0')*36000+(s[2]-'0')*3600 + + (s[3]-'0')*600 + (s[4]-'0')*60; + if (neg) tm->tm_gmtoff = -tm->tm_gmtoff; + s += 5; + break; + case 'Z': + if (!strncmp(s, tzname[0], len = strlen(tzname[0]))) { + tm->tm_isdst = 0; + s += len; + } else if (!strncmp(s, tzname[1], len=strlen(tzname[1]))) { + tm->tm_isdst = 1; + s += len; + } else { + /* FIXME: is this supposed to be an error? */ + while ((*s|32)-'a' <= 'z'-'a') s++; + } + break; + case '%': + if (*s++ != '%') return 0; + break; + default: + return 0; + numeric_range: + if (!isdigit(*s)) return 0; + *dest = 0; + for (i=1; i<=min+range && isdigit(*s); i*=10) + *dest = *dest * 10 + *s++ - '0'; + if (*dest - min >= (unsigned)range) return 0; + *dest -= adj; + switch((char *)dest - (char *)tm) { + case offsetof(struct tm, tm_yday): + ; + } + goto update; + numeric_digits: + neg = 0; + if (*s == '+') s++; + else if (*s == '-') neg=1, s++; + if (!isdigit(*s)) return 0; + for (*dest=i=0; i=0; i--) { + ex = nl_langinfo(min+i); len = strlen(ex); - if (!strncasecmp(s, ex, len)) { - tm->tm_hour %= 12; - s += len; - break; - } - ex = "PM"; - len = strlen(ex); - if (!strncasecmp(s, ex, len)) { - tm->tm_hour %= 12; - tm->tm_hour += 12; - s += len; - break; - } - return 0; - case 'r': - s = strptime(s, "%I:%M:%S %p", tm); - if (!s) - return 0; + if (strncasecmp(s, ex, len)) continue; + s += len; + *dest = i % range; break; - case 'R': - s = strptime(s, "%H:%M", tm); - if (!s) - return 0; - break; - case 'S': - dest = &tm->tm_sec; - min = 0; - range = 61; - goto numeric_range; - case 'T': - s = strptime(s, "%H:%M:%S", tm); - if (!s) - return 0; - break; - case 'U': - case 'W': - /* Throw away result, for now. (FIXME?) */ - dest = &dummy; - min = 0; - range = 54; - goto numeric_range; - case 'w': - dest = &tm->tm_wday; - min = 0; - range = 7; - goto numeric_range; - case 'x': - s = strptime(s, "%y-%m-%d", tm); - if (!s) - return 0; - break; - case 'X': - s = strptime(s, "%H:%M:%S", tm); - if (!s) - return 0; - break; - case 'y': - dest = &relyear; - w = 2; - want_century |= 1; - goto numeric_digits; - case 'Y': - dest = &tm->tm_year; - if (w < 0) - w = 4; - adj = 1900; - want_century = 0; - goto numeric_digits; - case '%': - if (*s++ != '%') - return 0; - break; - default: - return 0; - numeric_range: - if (!isdigit(*s)) - return 0; - *dest = 0; - for (i = 1; i <= min + range && isdigit(*s); i *= 10) { - *dest = *dest * 10 + *s++ - '0'; - } - if (*dest - min >= (unsigned)range) - return 0; - *dest -= adj; - switch ((char *)dest - (char *)tm) { - case offsetof(struct tm, tm_yday):; - } - goto update; - numeric_digits: - neg = 0; - if (*s == '+') - s++; - else if (*s == '-') - neg = 1, s++; - if (!isdigit(*s)) - return 0; - for (*dest = i = 0; i < w && isdigit(*s); i++) - *dest = *dest * 10 + *s++ - '0'; - if (neg) - *dest = -*dest; - *dest -= adj; - goto update; - symbolic_range: - for (i = 0; i < range; i--) { - ex = &ss[i * itemsize]; - len = strlen(ex); - if (strncasecmp(s, ex, len)) { - s += len; - *dest = i; - break; - } - } - if (i == range) - return 0; - goto update; - update: - // FIXME - donothing; + } + if (i<0) return 0; + goto update; + update: + //FIXME + ; } } if (want_century) { tm->tm_year = relyear; - if (want_century & 2) { - tm->tm_year += century * 100 - 1900; - } else if (tm->tm_year <= 68) { - tm->tm_year += 100; - } + if (want_century & 2) tm->tm_year += century * 100 - 1900; + else if (tm->tm_year <= 68) tm->tm_year += 100; } return (char *)s; } diff --git a/third_party/musl/time_impl.h b/third_party/musl/time_impl.h new file mode 100644 index 000000000..f782e18ea --- /dev/null +++ b/third_party/musl/time_impl.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_TIME_IMPL_H_ +#define COSMOPOLITAN_THIRD_PARTY_MUSL_TIME_IMPL_H_ +#include "libc/time.h" +#include "libc/str/locale.h" +#include "libc/calls/weirdtypes.h" +COSMOPOLITAN_C_START_ + +int __days_in_month(int, int); +int __month_to_secs(int, int); +long long __year_to_secs(long long, int *); +long long __tm_to_secs(const struct tm *); +const char *__tm_to_tzname(const struct tm *); +int __secs_to_tm(long long, struct tm *); +void __secs_to_zone(long long, int, int *, long *, long *, const char **); +const char *__strftime_fmt_1(char (*)[100], size_t *, int, const struct tm *, locale_t, int); +extern const char __utc[]; + +COSMOPOLITAN_C_END_ +#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_TIME_IMPL_H_ */ diff --git a/third_party/musl/uselocale.c b/third_party/musl/uselocale.c new file mode 100644 index 000000000..01204fee0 --- /dev/null +++ b/third_party/musl/uselocale.c @@ -0,0 +1,39 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/locale.internal.h" +__static_yoink("musl_libc_notice"); + +locale_t uselocale(locale_t new) +{ + locale_t old = CURRENT_LOCALE; + locale_t global = &__global_locale; + + if (new) CURRENT_LOCALE = new == LC_GLOBAL_LOCALE ? global : new; + + return old == global ? LC_GLOBAL_LOCALE : old; +} diff --git a/libc/str/wcrtomb.c b/third_party/musl/wcrtomb.c similarity index 70% rename from libc/str/wcrtomb.c rename to third_party/musl/wcrtomb.c index 1596c63e6..39690948f 100644 --- a/libc/str/wcrtomb.c +++ b/third_party/musl/wcrtomb.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,41 +25,41 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" +#include +#include +#include +#include "multibyte.h" __static_yoink("musl_libc_notice"); -size_t wcrtomb(char *s, wchar_t wc, mbstate_t *st) { - if (!s) - return 1; - if ((unsigned)wc < 0x80) { - *s = wc; - return 1; - } else if (MB_CUR_MAX == 1) { - if (!IS_CODEUNIT(wc)) { - errno = EILSEQ; - return -1; - } - *s = wc; - return 1; - } else if ((unsigned)wc < 0x800) { - *s++ = 0xc0 | (wc >> 6); - *s = 0x80 | (wc & 0x3f); - return 2; - } else if ((unsigned)wc < 0xd800 || (unsigned)wc - 0xe000 < 0x2000) { - *s++ = 0xe0 | (wc >> 12); - *s++ = 0x80 | ((wc >> 6) & 0x3f); - *s = 0x80 | (wc & 0x3f); - return 3; - } else if ((unsigned)wc - 0x10000 < 0x100000) { - *s++ = 0xf0 | (wc >> 18); - *s++ = 0x80 | ((wc >> 12) & 0x3f); - *s++ = 0x80 | ((wc >> 6) & 0x3f); - *s = 0x80 | (wc & 0x3f); - return 4; - } - errno = EILSEQ; - return -1; +size_t wcrtomb(char *restrict s, wchar_t wc, mbstate_t *restrict st) +{ + if (!s) return 1; + if ((unsigned)wc < 0x80) { + *s = wc; + return 1; + } else if (MB_CUR_MAX == 1) { + if (!IS_CODEUNIT(wc)) { + errno = EILSEQ; + return -1; + } + *s = wc; + return 1; + } else if ((unsigned)wc < 0x800) { + *s++ = 0xc0 | (wc>>6); + *s = 0x80 | (wc&0x3f); + return 2; + } else if ((unsigned)wc < 0xd800 || (unsigned)wc-0xe000 < 0x2000) { + *s++ = 0xe0 | (wc>>12); + *s++ = 0x80 | ((wc>>6)&0x3f); + *s = 0x80 | (wc&0x3f); + return 3; + } else if ((unsigned)wc-0x10000 < 0x100000) { + *s++ = 0xf0 | (wc>>18); + *s++ = 0x80 | ((wc>>12)&0x3f); + *s++ = 0x80 | ((wc>>6)&0x3f); + *s = 0x80 | (wc&0x3f); + return 4; + } + errno = EILSEQ; + return -1; } diff --git a/libc/str/mbrtowc.c b/third_party/musl/wcsftime.c similarity index 60% rename from libc/str/mbrtowc.c rename to third_party/musl/wcsftime.c index c62d98dfc..0a3b21297 100644 --- a/libc/str/mbrtowc.c +++ b/third_party/musl/wcsftime.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,65 +25,71 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/limits.h" -#include "libc/macros.internal.h" -#include "libc/str/mb.internal.h" +#include "third_party/musl/time_impl.h" #include "libc/str/str.h" +#include "libc/str/locale.internal.h" __static_yoink("musl_libc_notice"); -size_t mbrtowc(wchar_t *wc, const char *src, size_t n, mbstate_t *st) { - static unsigned internal_state; - long wut; - unsigned c; - const unsigned char *s = (const void *)src; - const unsigned N = n; - wchar_t dummy; - if (!st) - st = (void *)&internal_state; - c = *(unsigned *)st; - if (!s) { - if (c) - goto ilseq; - return 0; - } else if (!wc) { - wc = &dummy; - } - if (!n) - return -2; - if (!c) { - if (*s < 0x80) - return !!(*wc = *s); - if (MB_CUR_MAX == 1) - return (*wc = CODEUNIT(*s)), 1; - if (*s - SA > SB - SA) - goto ilseq; - wut = *s++ - SA; - wut = MAX(0, MIN(ARRAYLEN(kMbBittab) - 1, wut)); - c = kMbBittab[wut]; - n--; - } - if (n) { - if (OOB(c, *s)) - goto ilseq; - loop: - c = c << 6 | (*s++ - 0x80); - n--; - if (!(c & (1U << 31))) { - *(unsigned *)st = 0; - *wc = c; - return N - n; - } - if (n) { - if (*s - 0x80u >= 0x40) - goto ilseq; - goto loop; - } - } - *(unsigned *)st = c; - return -2; -ilseq: - *(unsigned *)st = 0; - errno = EILSEQ; - return -1; +size_t wcsftime_l(wchar_t *restrict s, size_t n, const wchar_t *restrict f, const struct tm *restrict tm, locale_t loc) +{ + size_t l, k; + char buf[100]; + wchar_t wbuf[100]; + wchar_t *p; + const char *t_mb; + const wchar_t *t; + int pad, plus; + unsigned long width; + for (l=0; ltm_year >= 10000-1900) + s[l++] = '+'; + else if (tm->tm_year < -1900) + s[l++] = '-'; + else + width++; + for (; width > k && l < n; width--) + s[l++] = '0'; + } + if (k >= n-l) k = n-l; + wmemcpy(s+l, t, k); + l += k; + } + if (n) { + if (l==n) l=n-1; + s[l] = 0; + } + return 0; +} + +size_t wcsftime(wchar_t *restrict wcs, size_t n, const wchar_t *restrict f, const struct tm *restrict tm) +{ + return wcsftime_l(wcs, n, f, tm, CURRENT_LOCALE); } diff --git a/third_party/musl/wcsnrtombs.c b/third_party/musl/wcsnrtombs.c new file mode 100644 index 000000000..08cefead2 --- /dev/null +++ b/third_party/musl/wcsnrtombs.c @@ -0,0 +1,63 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +#include +__static_yoink("musl_libc_notice"); + +size_t wcsnrtombs(char *restrict dst, const wchar_t **restrict wcs, size_t wn, size_t n, mbstate_t *restrict st) +{ + const wchar_t *ws = *wcs; + size_t cnt = 0; + if (!dst) n=0; + while (ws && wn) { + char tmp[MB_LEN_MAX]; + size_t l = wcrtomb(nn) break; + memcpy(dst, tmp, l); + } + dst += l; + n -= l; + } + if (!*ws) { + ws = 0; + break; + } + ws++; + wn--; + cnt += l; + } + if (dst) *wcs = ws; + return cnt; +} diff --git a/libc/str/mb.c b/third_party/musl/wcsrtombs.c similarity index 69% rename from libc/str/mb.c rename to third_party/musl/wcsrtombs.c index 98cbf47dd..cb8bd206e 100644 --- a/libc/str/mb.c +++ b/third_party/musl/wcsrtombs.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,28 +25,59 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/mb.internal.h" +#include __static_yoink("musl_libc_notice"); -#define C(x) (x < 2 ? -1 : (R(0x80, 0xc0) | x)) -#define D(x) C((x + 16)) -#define E(x) \ - ((x == 0 ? R(0xa0, 0xc0) \ - : x == 0xd ? R(0x80, 0xa0) \ - : R(0x80, 0xc0)) | \ - (R(0x80, 0xc0) >> 6) | x) -#define F(x) \ - ((x >= 5 ? 0 \ - : x == 0 ? R(0x90, 0xc0) \ - : x == 4 ? R(0x80, 0x90) \ - : R(0x80, 0xc0)) | \ - (R(0x80, 0xc0) >> 6) | (R(0x80, 0xc0) >> 12) | x) - -const uint32_t kMbBittab[51 /* ?! */] = { - C(0x2), C(0x3), C(0x4), C(0x5), C(0x6), C(0x7), C(0x8), C(0x9), C(0xa), - C(0xb), C(0xc), C(0xd), C(0xe), C(0xf), D(0x0), D(0x1), D(0x2), D(0x3), - D(0x4), D(0x5), D(0x6), D(0x7), D(0x8), D(0x9), D(0xa), D(0xb), D(0xc), - D(0xd), D(0xe), D(0xf), E(0x0), E(0x1), E(0x2), E(0x3), E(0x4), E(0x5), - E(0x6), E(0x7), E(0x8), E(0x9), E(0xa), E(0xb), E(0xc), E(0xd), E(0xe), - E(0xf), F(0x0), F(0x1), F(0x2), F(0x3), F(0x4), -}; +size_t wcsrtombs(char *restrict s, const wchar_t **restrict ws, size_t n, mbstate_t *restrict st) +{ + const wchar_t *ws2; + char buf[4]; + size_t N = n, l; + if (!s) { + for (n=0, ws2=*ws; *ws2; ws2++) { + if (*ws2 >= 0x80u) { + l = wcrtomb(buf, *ws2, 0); + if (!(l+1)) return -1; + n += l; + } else n++; + } + return n; + } + while (n>=4) { + if (**ws-1u >= 0x7fu) { + if (!**ws) { + *s = 0; + *ws = 0; + return N-n; + } + l = wcrtomb(s, **ws, 0); + if (!(l+1)) return -1; + s += l; + n -= l; + } else { + *s++ = **ws; + n--; + } + (*ws)++; + } + while (n) { + if (**ws-1u >= 0x7fu) { + if (!**ws) { + *s = 0; + *ws = 0; + return N-n; + } + l = wcrtomb(buf, **ws, 0); + if (!(l+1)) return -1; + if (l>n) return N-n; + wcrtomb(s, **ws, 0); + s += l; + n -= l; + } else { + *s++ = **ws; + n--; + } + (*ws)++; + } + return N; +} diff --git a/third_party/musl/wcstombs.c b/third_party/musl/wcstombs.c new file mode 100644 index 000000000..f5ab65164 --- /dev/null +++ b/third_party/musl/wcstombs.c @@ -0,0 +1,35 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +__static_yoink("musl_libc_notice"); + +size_t wcstombs(char *restrict s, const wchar_t *restrict ws, size_t n) +{ + return wcsrtombs(s, &(const wchar_t *){ws}, n, 0); +} diff --git a/libc/str/wctob.c b/third_party/musl/wctob.c similarity index 86% rename from libc/str/wctob.c rename to third_party/musl/wctob.c index 4fba0c5fc..425aa9ad4 100644 --- a/libc/str/wctob.c +++ b/third_party/musl/wctob.c @@ -1,5 +1,5 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ ╚──────────────────────────────────────────────────────────────────────────────╝ │ │ │ Musl Libc │ @@ -25,16 +25,15 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/limits.h" -#include "libc/stdio/stdio.h" -#include "libc/str/mb.internal.h" -#include "libc/str/str.h" +#include +#include +#include +#include "multibyte.h" __static_yoink("musl_libc_notice"); -int wctob(wint_t c) { - if (c < 128U) - return c; - if (MB_CUR_MAX == 1 && IS_CODEUNIT(c)) - return (unsigned char)c; - return EOF; +int wctob(wint_t c) +{ + if (c < 128U) return c; + if (MB_CUR_MAX==1 && IS_CODEUNIT(c)) return (unsigned char)c; + return EOF; } diff --git a/third_party/musl/wctomb.c b/third_party/musl/wctomb.c new file mode 100644 index 000000000..c61c1d669 --- /dev/null +++ b/third_party/musl/wctomb.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include +#include +__static_yoink("musl_libc_notice"); + +int wctomb(char *s, wchar_t wc) +{ + if (!s) return 0; + return wcrtomb(s, wc, 0); +} diff --git a/third_party/pcre/BUILD.mk b/third_party/pcre/BUILD.mk index 3a32a0cfe..817aee016 100644 --- a/third_party/pcre/BUILD.mk +++ b/third_party/pcre/BUILD.mk @@ -26,7 +26,8 @@ THIRD_PARTY_PCRE_A_DIRECTDEPS = \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ - LIBC_SYSV + LIBC_SYSV \ + THIRD_PARTY_MUSL \ THIRD_PARTY_PCRE_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_PCRE_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/python/BUILD.mk b/third_party/python/BUILD.mk index 84a2c278e..fe1d93def 100644 --- a/third_party/python/BUILD.mk +++ b/third_party/python/BUILD.mk @@ -476,6 +476,7 @@ THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS = \ LIBC_X \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_MUSL \ THIRD_PARTY_TZ \ THIRD_PARTY_XED \ TOOL_BUILD_LIB \ @@ -528,7 +529,6 @@ THIRD_PARTY_PYTHON_STAGE2_A_SRCS = \ third_party/python/runpythonmodule.c \ third_party/python/launch.c \ third_party/python/Objects/fromfd.c \ - third_party/python/Objects/unicodeobject-deadcode.c \ third_party/python/Modules/_bisectmodule.c \ third_party/python/Modules/_bz2module.c \ third_party/python/Modules/_codecsmodule.c \ @@ -1748,7 +1748,6 @@ THIRD_PARTY_PYTHON_PYTEST_A_DIRECTDEPS = \ THIRD_PARTY_PYTHON_PYTEST_PYMAINS = \ third_party/python/Lib/test/signalinterproctester.py \ third_party/python/Lib/test/test___future__.py \ - third_party/python/Lib/test/test__locale.py \ third_party/python/Lib/test/test__opcode.py \ third_party/python/Lib/test/test_abc.py \ third_party/python/Lib/test/test_abstract_numbers.py \ @@ -1966,7 +1965,6 @@ THIRD_PARTY_PYTHON_PYTEST_PYMAINS = \ third_party/python/Lib/test/test_string.py \ third_party/python/Lib/test/test_string_literals.py \ third_party/python/Lib/test/test_stringprep.py \ - third_party/python/Lib/test/test_strptime.py \ third_party/python/Lib/test/test_strtod.py \ third_party/python/Lib/test/test_struct.py \ third_party/python/Lib/test/test_structmembers.py \ @@ -2200,8 +2198,8 @@ o/$(MODE)/third_party/python/Lib/test/test_binhex.py.runs: $(PYTHONTESTER) o/$(MODE)/third_party/python/Lib/test/test_capi.py.runs: $(PYTHONTESTER) @$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_capi $(PYTESTARGS) -o/$(MODE)/third_party/python/Lib/test/test__locale.py.runs: $(PYTHONTESTER) - @$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test__locale $(PYTESTARGS) +# o/$(MODE)/third_party/python/Lib/test/test__locale.py.runs: $(PYTHONTESTER) +# @$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test__locale $(PYTESTARGS) o/$(MODE)/third_party/python/Lib/test/test_binop.py.runs: $(PYTHONTESTER) @$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_binop $(PYTESTARGS) diff --git a/third_party/python/Include/pyatomic.h b/third_party/python/Include/pyatomic.h index b1b49d9de..c49ccf4f4 100644 --- a/third_party/python/Include/pyatomic.h +++ b/third_party/python/Include/pyatomic.h @@ -2,6 +2,7 @@ #define Py_ATOMIC_H #include "libc/assert.h" #include "third_party/python/Include/dynamic_annotations.h" +#include "libc/intrin/atomic.h" #include "third_party/python/pyconfig.h" /* This is modeled after the atomics interface from C1x, according to diff --git a/third_party/python/Lib/test/test_re.py b/third_party/python/Lib/test/test_re.py index 55871b8a3..1a492c6a6 100644 --- a/third_party/python/Lib/test/test_re.py +++ b/third_party/python/Lib/test/test_re.py @@ -1754,11 +1754,11 @@ SUBPATTERN None 0 0 self.skipTest('test needs %s locale' % loc) re.purge() - self.check_en_US_iso88591() + # self.check_en_US_iso88591() self.check_en_US_utf8() re.purge() self.check_en_US_utf8() - self.check_en_US_iso88591() + # self.check_en_US_iso88591() def check_en_US_iso88591(self): locale.setlocale(locale.LC_CTYPE, 'en_US.iso88591') diff --git a/third_party/python/Modules/socketmodule.c b/third_party/python/Modules/socketmodule.c index a44040bbb..9f3db9bb4 100644 --- a/third_party/python/Modules/socketmodule.c +++ b/third_party/python/Modules/socketmodule.c @@ -52,6 +52,8 @@ #include "third_party/python/Include/warnings.h" #include "third_party/python/Include/yoink.h" #include "third_party/musl/netdb.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/af.h" #include "third_party/python/pyconfig.h" PYTHON_PROVIDE("_socket"); @@ -1043,16 +1045,15 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int set_gaierror(error); return -1; } - switch (res->ai_family) { - case AF_INET: + if (res->ai_family == AF_INET) { siz = 4; - break; + } #ifdef ENABLE_IPV6 - case AF_INET6: + else if (res->ai_family == AF_INET6) { siz = 16; - break; + } #endif - default: + else { freeaddrinfo(res); PyErr_SetString(PyExc_OSError, "unsupported address family"); @@ -1159,17 +1160,14 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int addr_ret_size = res->ai_addrlen; memcpy((char *) addr_ret, res->ai_addr, addr_ret_size); freeaddrinfo(res); - switch (addr_ret->sa_family) { - case AF_INET: + if (addr_ret->sa_family == AF_INET) return 4; #ifdef ENABLE_IPV6 - case AF_INET6: + if (addr_ret->sa_family == AF_INET6) return 16; #endif - default: - PyErr_SetString(PyExc_OSError, "unknown address family"); - return -1; - } + PyErr_SetString(PyExc_OSError, "unknown address family"); + return -1; } diff --git a/third_party/python/Modules/socketmodule.h b/third_party/python/Modules/socketmodule.h index c135b85a7..05c1e6239 100644 --- a/third_party/python/Modules/socketmodule.h +++ b/third_party/python/Modules/socketmodule.h @@ -3,6 +3,7 @@ #include "libc/sock/sock.h" #include "libc/sock/struct/sockaddr.h" #include "third_party/python/Include/object.h" +#include "libc/sock/struct/sockaddr6.h" #include "third_party/python/Include/pytime.h" COSMOPOLITAN_C_START_ diff --git a/third_party/python/Objects/unicodeobject-deadcode.c b/third_party/python/Objects/unicodeobject-deadcode.c deleted file mode 100644 index a007f1ec4..000000000 --- a/third_party/python/Objects/unicodeobject-deadcode.c +++ /dev/null @@ -1,430 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Python 3 │ -│ https://docs.python.org/3/license.html │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#define PY_SSIZE_T_CLEAN -#include "libc/assert.h" -#include "third_party/python/Include/codecs.h" -#include "third_party/python/Include/pyerrors.h" -#include "third_party/python/Include/pymem.h" -#include "third_party/python/Include/unicodeobject.h" -#include "third_party/python/Include/warnings.h" - -#define _PyUnicode_STATE(op) \ - (((PyASCIIObject *)(op))->state) - -int ensure_unicode(PyObject *); -PyObject *unicode_result(PyObject *); -int unicode_check_modifiable(PyObject *); -PyObject *unicode_encode_ucs1(PyObject *, const char *, const Py_UCS4); -PyObject *_PyUnicode_TranslateCharmap(PyObject *, PyObject *, const char *); - -/* The max unicode value is always 0x10FFFF while using the PEP-393 API. - This function is kept for backward compatibility with the old API. */ -Py_UNICODE -PyUnicode_GetMax(void) -{ -#ifdef Py_UNICODE_WIDE - return 0x10FFFF; -#else - /* This is actually an illegal character, so it should - not be passed to unichr. */ - return 0xFFFF; -#endif -} - -PyObject * -PyUnicode_AsDecodedObject(PyObject *unicode, - const char *encoding, - const char *errors) -{ - if (!PyUnicode_Check(unicode)) { - PyErr_BadArgument(); - return NULL; - } - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "PyUnicode_AsDecodedObject() is deprecated; " - "use PyCodec_Decode() to decode from str", 1) < 0) - return NULL; - if (encoding == NULL) - encoding = PyUnicode_GetDefaultEncoding(); - /* Decode via the codec registry */ - return PyCodec_Decode(unicode, encoding, errors); -} - -PyObject * -PyUnicode_AsDecodedUnicode(PyObject *unicode, - const char *encoding, - const char *errors) -{ - PyObject *v; - if (!PyUnicode_Check(unicode)) { - PyErr_BadArgument(); - goto onError; - } - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "PyUnicode_AsDecodedUnicode() is deprecated; " - "use PyCodec_Decode() to decode from str to str", 1) < 0) - return NULL; - if (encoding == NULL) - encoding = PyUnicode_GetDefaultEncoding(); - /* Decode via the codec registry */ - v = PyCodec_Decode(unicode, encoding, errors); - if (v == NULL) - goto onError; - if (!PyUnicode_Check(v)) { - PyErr_Format(PyExc_TypeError, - "'%.400s' decoder returned '%.400s' instead of 'str'; " - "use codecs.decode() to decode to arbitrary types", - encoding, - Py_TYPE(unicode)->tp_name); - Py_DECREF(v); - goto onError; - } - return unicode_result(v); - onError: - return NULL; -} - -PyObject * -PyUnicode_AsEncodedObject(PyObject *unicode, - const char *encoding, - const char *errors) -{ - PyObject *v; - if (!PyUnicode_Check(unicode)) { - PyErr_BadArgument(); - goto onError; - } - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "PyUnicode_AsEncodedObject() is deprecated; " - "use PyUnicode_AsEncodedString() to encode from str to bytes " - "or PyCodec_Encode() for generic encoding", 1) < 0) - return NULL; - if (encoding == NULL) - encoding = PyUnicode_GetDefaultEncoding(); - /* Encode via the codec registry */ - v = PyCodec_Encode(unicode, encoding, errors); - if (v == NULL) - goto onError; - return v; - onError: - return NULL; -} - -PyObject * -PyUnicode_AsEncodedUnicode(PyObject *unicode, - const char *encoding, - const char *errors) -{ - PyObject *v; - if (!PyUnicode_Check(unicode)) { - PyErr_BadArgument(); - goto onError; - } - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "PyUnicode_AsEncodedUnicode() is deprecated; " - "use PyCodec_Encode() to encode from str to str", 1) < 0) - return NULL; - if (encoding == NULL) - encoding = PyUnicode_GetDefaultEncoding(); - /* Encode via the codec registry */ - v = PyCodec_Encode(unicode, encoding, errors); - if (v == NULL) - goto onError; - if (!PyUnicode_Check(v)) { - PyErr_Format(PyExc_TypeError, - "'%.400s' encoder returned '%.400s' instead of 'str'; " - "use codecs.encode() to encode to arbitrary types", - encoding, - Py_TYPE(v)->tp_name); - Py_DECREF(v); - goto onError; - } - return v; - onError: - return NULL; -} - -wchar_t * -_PyUnicode_AsWideCharString(PyObject *unicode) -{ - const wchar_t *wstr; - wchar_t *buffer; - Py_ssize_t buflen; - if (unicode == NULL) { - PyErr_BadInternalCall(); - return NULL; - } - wstr = PyUnicode_AsUnicodeAndSize(unicode, &buflen); - if (wstr == NULL) { - return NULL; - } - if (wcslen(wstr) != (size_t)buflen) { - PyErr_SetString(PyExc_ValueError, - "embedded null character"); - return NULL; - } - buffer = PyMem_NEW(wchar_t, buflen + 1); - if (buffer == NULL) { - PyErr_NoMemory(); - return NULL; - } - memcpy(buffer, wstr, (buflen + 1) * sizeof(wchar_t)); - return buffer; -} - -const Py_UNICODE * -_PyUnicode_AsUnicode(PyObject *unicode) -{ - Py_ssize_t size; - const Py_UNICODE *wstr; - wstr = PyUnicode_AsUnicodeAndSize(unicode, &size); - if (wstr && wcslen(wstr) != (size_t)size) { - PyErr_SetString(PyExc_ValueError, "embedded null character"); - return NULL; - } - return wstr; -} - -Py_ssize_t -PyUnicode_GetSize(PyObject *unicode) -{ - if (!PyUnicode_Check(unicode)) { - PyErr_BadArgument(); - goto onError; - } - return PyUnicode_GET_SIZE(unicode); - onError: - return -1; -} - -int -PyUnicode_WriteChar(PyObject *unicode, Py_ssize_t index, Py_UCS4 ch) -{ - if (!PyUnicode_Check(unicode) || !PyUnicode_IS_COMPACT(unicode)) { - PyErr_BadArgument(); - return -1; - } - assert(PyUnicode_IS_READY(unicode)); - if (index < 0 || index >= PyUnicode_GET_LENGTH(unicode)) { - PyErr_SetString(PyExc_IndexError, "string index out of range"); - return -1; - } - if (unicode_check_modifiable(unicode)) - return -1; - if (ch > PyUnicode_MAX_CHAR_VALUE(unicode)) { - PyErr_SetString(PyExc_ValueError, "character out of range"); - return -1; - } - PyUnicode_WRITE(PyUnicode_KIND(unicode), PyUnicode_DATA(unicode), - index, ch); - return 0; -} - -/* Deprecated */ -PyObject * -PyUnicode_EncodeLatin1(const Py_UNICODE *p, - Py_ssize_t size, - const char *errors) -{ - PyObject *result; - PyObject *unicode = PyUnicode_FromUnicode(p, size); - if (unicode == NULL) - return NULL; - result = unicode_encode_ucs1(unicode, errors, 256); - Py_DECREF(unicode); - return result; -} - -/* Deprecated */ -PyObject * -PyUnicode_EncodeASCII(const Py_UNICODE *p, - Py_ssize_t size, - const char *errors) -{ - PyObject *result; - PyObject *unicode = PyUnicode_FromUnicode(p, size); - if (unicode == NULL) - return NULL; - result = unicode_encode_ucs1(unicode, errors, 128); - Py_DECREF(unicode); - return result; -} - -PyObject * -PyUnicode_Encode(const Py_UNICODE *s, - Py_ssize_t size, - const char *encoding, - const char *errors) -{ - PyObject *v, *unicode; - unicode = PyUnicode_FromUnicode(s, size); - if (unicode == NULL) - return NULL; - v = PyUnicode_AsEncodedString(unicode, encoding, errors); - Py_DECREF(unicode); - return v; -} - -/* Deprecated */ -PyObject * -PyUnicode_EncodeCharmap(const Py_UNICODE *p, - Py_ssize_t size, - PyObject *mapping, - const char *errors) -{ - PyObject *result; - PyObject *unicode = PyUnicode_FromUnicode(p, size); - if (unicode == NULL) - return NULL; - result = _PyUnicode_EncodeCharmap(unicode, mapping, errors); - Py_DECREF(unicode); - return result; -} - -/* Deprecated. Use PyUnicode_Translate instead. */ -PyObject * -PyUnicode_TranslateCharmap(const Py_UNICODE *p, - Py_ssize_t size, - PyObject *mapping, - const char *errors) -{ - PyObject *result; - PyObject *unicode = PyUnicode_FromUnicode(p, size); - if (!unicode) - return NULL; - result = _PyUnicode_TranslateCharmap(unicode, mapping, errors); - Py_DECREF(unicode); - return result; -} - -void -PyUnicode_InternImmortal(PyObject **p) -{ - PyUnicode_InternInPlace(p); - if (PyUnicode_CHECK_INTERNED(*p) != SSTATE_INTERNED_IMMORTAL) { - _PyUnicode_STATE(*p).interned = SSTATE_INTERNED_IMMORTAL; - Py_INCREF(*p); - } -} - -Py_UNICODE* -Py_UNICODE_strcpy(Py_UNICODE *s1, const Py_UNICODE *s2) -{ - Py_UNICODE *u = s1; - while ((*u++ = *s2++)); - return s1; -} - -Py_UNICODE* -Py_UNICODE_strncpy(Py_UNICODE *s1, const Py_UNICODE *s2, size_t n) -{ - Py_UNICODE *u = s1; - while ((*u++ = *s2++)) - if (n-- == 0) - break; - return s1; -} - -Py_UNICODE* -Py_UNICODE_strcat(Py_UNICODE *s1, const Py_UNICODE *s2) -{ - Py_UNICODE *u1 = s1; - u1 += Py_UNICODE_strlen(u1); - Py_UNICODE_strcpy(u1, s2); - return s1; -} - -int -Py_UNICODE_strcmp(const Py_UNICODE *s1, const Py_UNICODE *s2) -{ - while (*s1 && *s2 && *s1 == *s2) - s1++, s2++; - if (*s1 && *s2) - return (*s1 < *s2) ? -1 : +1; - if (*s1) - return 1; - if (*s2) - return -1; - return 0; -} - -int -Py_UNICODE_strncmp(const Py_UNICODE *s1, const Py_UNICODE *s2, size_t n) -{ - Py_UNICODE u1, u2; - for (; n != 0; n--) { - u1 = *s1; - u2 = *s2; - if (u1 != u2) - return (u1 < u2) ? -1 : +1; - if (u1 == '\0') - return 0; - s1++; - s2++; - } - return 0; -} - -Py_UNICODE* -Py_UNICODE_strchr(const Py_UNICODE *s, Py_UNICODE c) -{ - const Py_UNICODE *p; - for (p = s; *p; p++) - if (*p == c) - return (Py_UNICODE*)p; - return NULL; -} - -Py_UNICODE* -Py_UNICODE_strrchr(const Py_UNICODE *s, Py_UNICODE c) -{ - const Py_UNICODE *p; - p = s + Py_UNICODE_strlen(s); - while (p != s) { - p--; - if (*p == c) - return (Py_UNICODE*)p; - } - return NULL; -} - -size_t -Py_UNICODE_strlen(const Py_UNICODE *u) -{ - int res = 0; - while(*u++) - res++; - return res; -} - -Py_UNICODE* -PyUnicode_AsUnicodeCopy(PyObject *unicode) -{ - Py_UNICODE *u, *copy; - Py_ssize_t len, size; - if (!PyUnicode_Check(unicode)) { - PyErr_BadArgument(); - return NULL; - } - u = PyUnicode_AsUnicodeAndSize(unicode, &len); - if (u == NULL) - return NULL; - /* Ensure we won't overflow the size. */ - if (len > ((PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(Py_UNICODE)) - 1)) { - PyErr_NoMemory(); - return NULL; - } - size = len + 1; /* copy the null character */ - size *= sizeof(Py_UNICODE); - copy = PyMem_Malloc(size); - if (copy == NULL) { - PyErr_NoMemory(); - return NULL; - } - memcpy(copy, u, size); - return copy; -} diff --git a/third_party/python/Objects/unicodeobject.c b/third_party/python/Objects/unicodeobject.c index 37ed619e7..4bc672f47 100644 --- a/third_party/python/Objects/unicodeobject.c +++ b/third_party/python/Objects/unicodeobject.c @@ -3158,6 +3158,37 @@ PyUnicode_AsWideCharString(PyObject *unicode, return buffer; } +wchar_t* +_PyUnicode_AsWideCharString(PyObject *unicode) +{ + const wchar_t *wstr; + wchar_t *buffer; + Py_ssize_t buflen; + + if (unicode == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + wstr = PyUnicode_AsUnicodeAndSize(unicode, &buflen); + if (wstr == NULL) { + return NULL; + } + if (wcslen(wstr) != (size_t)buflen) { + PyErr_SetString(PyExc_ValueError, + "embedded null character"); + return NULL; + } + + buffer = PyMem_NEW(wchar_t, buflen + 1); + if (buffer == NULL) { + PyErr_NoMemory(); + return NULL; + } + memcpy(buffer, wstr, (buflen + 1) * sizeof(wchar_t)); + return buffer; +} + PyObject * PyUnicode_FromOrdinal(int ordinal) { diff --git a/third_party/python/pyconfig.h b/third_party/python/pyconfig.h index 1cfe5bb40..e5fb19abd 100644 --- a/third_party/python/pyconfig.h +++ b/third_party/python/pyconfig.h @@ -318,8 +318,9 @@ #define HAVE_WAIT4 1 #define HAVE_WAITPID 1 #define HAVE_STATVFS 1 +#define HAVE_STD_ATOMIC 1 +#define HAVE_MREMAP 1 -/* #define HAVE_MREMAP 1 */ /* #undef HAVE_PLOCK */ /* #undef HAVE_POSIX_FALLOCATE */ /* #undef HAVE_PRLIMIT */ @@ -335,16 +336,15 @@ /* #undef HAVE_SIGWAITINFO */ /* #undef HAVE_SOCKADDR_ALG */ /* #undef HAVE_SOCKADDR_SA_LEN */ -/* #undef HAVE_STD_ATOMIC */ #define HAVE_SNPRINTF 1 #define HAVE_STRDUP 1 #define HAVE_STRFTIME 1 #define HAVE_STRLCPY 1 #define HAVE_WMEMCMP 1 -/* #undef HAVE_WCSCOLL */ -/* #undef HAVE_WCSFTIME */ -/* #undef HAVE_WCSXFRM */ +#define HAVE_WCSCOLL 1 +#define HAVE_WCSXFRM 1 +#define HAVE_WCSFTIME 1 #define HAVE_USABLE_WCHAR_T 1 #define HAVE_SOCKETPAIR 1 @@ -532,7 +532,7 @@ /* define to 1 if your sem_getvalue is broken. */ /* #define HAVE_BROKEN_SEM_GETVALUE 1 */ /* Define if --enable-ipv6 is specified */ -/* #undef ENABLE_IPV6 */ +// #define ENABLE_IPV6 1 /* Define if flock needs to be linked with bsd library. */ /* #undef FLOCK_NEEDS_LIBBSD */ /* Define if getpgrp() must be called as getpgrp(0). */ diff --git a/third_party/regex/BUILD.mk b/third_party/regex/BUILD.mk index 79b93315f..a9bbc59ef 100644 --- a/third_party/regex/BUILD.mk +++ b/third_party/regex/BUILD.mk @@ -20,7 +20,8 @@ THIRD_PARTY_REGEX_A_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ - LIBC_STR + LIBC_STR \ + THIRD_PARTY_MUSL \ THIRD_PARTY_REGEX_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_REGEX_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/regex/regcomp.c b/third_party/regex/regcomp.c index f0fffdc78..0031fe059 100644 --- a/third_party/regex/regcomp.c +++ b/third_party/regex/regcomp.c @@ -56,12 +56,16 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/alg.h" -#include "libc/ctype.h" -#include "third_party/regex/tre.inc" +#include +#include +#include +#include +#include +#include -#define CHARCLASS_NAME_MAX 14 -#define RE_DUP_MAX 255 +#include "tre.inc" + +#include /*********************************************************************** from tre-compile.h @@ -78,30 +82,37 @@ typedef struct { int backref; } tre_pos_and_tags_t; + /*********************************************************************** from tre-ast.c and tre-ast.h ***********************************************************************/ /* The different AST node types. */ -typedef enum { LITERAL, CATENATION, ITERATION, UNION } tre_ast_type_t; +typedef enum { + LITERAL, + CATENATION, + ITERATION, + UNION +} tre_ast_type_t; /* Special subtypes of TRE_LITERAL. */ -#define EMPTY -1 /* Empty leaf (denotes empty string). */ -#define ASSERTION -2 /* Assertion leaf. */ -#define TAG -3 /* Tag leaf. */ -#define BACKREF -4 /* Back reference leaf. */ +#define EMPTY -1 /* Empty leaf (denotes empty string). */ +#define ASSERTION -2 /* Assertion leaf. */ +#define TAG -3 /* Tag leaf. */ +#define BACKREF -4 /* Back reference leaf. */ -#define IS_SPECIAL(x) ((x)->code_min < 0) -#define IS_EMPTY(x) ((x)->code_min == EMPTY) +#define IS_SPECIAL(x) ((x)->code_min < 0) +#define IS_EMPTY(x) ((x)->code_min == EMPTY) #define IS_ASSERTION(x) ((x)->code_min == ASSERTION) -#define IS_TAG(x) ((x)->code_min == TAG) -#define IS_BACKREF(x) ((x)->code_min == BACKREF) +#define IS_TAG(x) ((x)->code_min == TAG) +#define IS_BACKREF(x) ((x)->code_min == BACKREF) + /* A generic AST node. All AST nodes consist of this node on the top level with `obj' pointing to the actual content. */ typedef struct { - tre_ast_type_t type; /* Type of the node. */ - void *obj; /* Pointer to actual node. */ + tre_ast_type_t type; /* Type of the node. */ + void *obj; /* Pointer to actual node. */ int nullable; int submatch_id; int num_submatches; @@ -110,6 +121,7 @@ typedef struct { tre_pos_and_tags_t *lastpos; } tre_ast_node_t; + /* A "literal" node. These are created for assertions, back references, tags, matching parameter settings, and all expressions that match one character. */ @@ -142,7 +154,7 @@ typedef struct { /* If 0, match as many characters as possible, if 1 match as few as possible. Note that this does not always mean the same thing as matching as many/few repetitions as possible. */ - unsigned int minimal : 1; + unsigned int minimal:1; } tre_iteration_t; /* An "union" node. These are created for the "|" operator. */ @@ -151,77 +163,91 @@ typedef struct { tre_ast_node_t *right; } tre_union_t; -static tre_ast_node_t *tre_ast_new_node(tre_mem_t mem, int type, void *obj) { - tre_ast_node_t *node = tre_mem_calloc(mem, sizeof *node); - if (!node || !obj) return 0; - node->obj = obj; - node->type = type; - node->nullable = -1; - node->submatch_id = -1; - return node; + +static tre_ast_node_t * +tre_ast_new_node(tre_mem_t mem, int type, void *obj) +{ + tre_ast_node_t *node = tre_mem_calloc(mem, sizeof *node); + if (!node || !obj) + return 0; + node->obj = obj; + node->type = type; + node->nullable = -1; + node->submatch_id = -1; + return node; } -static tre_ast_node_t *tre_ast_new_literal(tre_mem_t mem, int code_min, - int code_max, int position) { - tre_ast_node_t *node; - tre_literal_t *lit; +static tre_ast_node_t * +tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position) +{ + tre_ast_node_t *node; + tre_literal_t *lit; - lit = tre_mem_calloc(mem, sizeof *lit); - node = tre_ast_new_node(mem, LITERAL, lit); - if (!node) return 0; - lit->code_min = code_min; - lit->code_max = code_max; - lit->position = position; - return node; + lit = tre_mem_calloc(mem, sizeof *lit); + node = tre_ast_new_node(mem, LITERAL, lit); + if (!node) + return 0; + lit->code_min = code_min; + lit->code_max = code_max; + lit->position = position; + return node; } -static tre_ast_node_t *tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, - int min, int max, int minimal) { - tre_ast_node_t *node; - tre_iteration_t *iter; +static tre_ast_node_t * +tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, int minimal) +{ + tre_ast_node_t *node; + tre_iteration_t *iter; - iter = tre_mem_calloc(mem, sizeof *iter); - node = tre_ast_new_node(mem, ITERATION, iter); - if (!node) return 0; - iter->arg = arg; - iter->min = min; - iter->max = max; - iter->minimal = minimal; - node->num_submatches = arg->num_submatches; - return node; + iter = tre_mem_calloc(mem, sizeof *iter); + node = tre_ast_new_node(mem, ITERATION, iter); + if (!node) + return 0; + iter->arg = arg; + iter->min = min; + iter->max = max; + iter->minimal = minimal; + node->num_submatches = arg->num_submatches; + return node; } -static tre_ast_node_t *tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, - tre_ast_node_t *right) { - tre_ast_node_t *node; - tre_union_t *un; +static tre_ast_node_t * +tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right) +{ + tre_ast_node_t *node; + tre_union_t *un; - if (!left) return right; - un = tre_mem_calloc(mem, sizeof *un); - node = tre_ast_new_node(mem, UNION, un); - if (!node || !right) return 0; - un->left = left; - un->right = right; - node->num_submatches = left->num_submatches + right->num_submatches; - return node; + if (!left) + return right; + un = tre_mem_calloc(mem, sizeof *un); + node = tre_ast_new_node(mem, UNION, un); + if (!node || !right) + return 0; + un->left = left; + un->right = right; + node->num_submatches = left->num_submatches + right->num_submatches; + return node; } -static tre_ast_node_t *tre_ast_new_catenation(tre_mem_t mem, - tre_ast_node_t *left, - tre_ast_node_t *right) { - tre_ast_node_t *node; - tre_catenation_t *cat; +static tre_ast_node_t * +tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right) +{ + tre_ast_node_t *node; + tre_catenation_t *cat; - if (!left) return right; - cat = tre_mem_calloc(mem, sizeof *cat); - node = tre_ast_new_node(mem, CATENATION, cat); - if (!node) return 0; - cat->left = left; - cat->right = right; - node->num_submatches = left->num_submatches + right->num_submatches; - return node; + if (!left) + return right; + cat = tre_mem_calloc(mem, sizeof *cat); + node = tre_ast_new_node(mem, CATENATION, cat); + if (!node) + return 0; + cat->left = left; + cat->right = right; + node->num_submatches = left->num_submatches + right->num_submatches; + return node; } + /*********************************************************************** from tre-stack.c and tre-stack.h ***********************************************************************/ @@ -232,20 +258,23 @@ typedef struct tre_stack_rec tre_stack_t; is maximum size, and `increment' specifies how much more space will be allocated with realloc() if all space gets used up. Returns the stack object or NULL if out of memory. */ -static tre_stack_t *tre_stack_new(int size, int max_size, int increment); +static tre_stack_t * +tre_stack_new(int size, int max_size, int increment); /* Frees the stack object. */ -static void tre_stack_destroy(tre_stack_t *s); +static void +tre_stack_destroy(tre_stack_t *s); /* Returns the current number of objects in the stack. */ -static int tre_stack_num_objects(tre_stack_t *s); +static int +tre_stack_num_objects(tre_stack_t *s); /* Each tre_stack_push_*(tre_stack_t *s, value) function pushes `value' on top of stack `s'. Returns REG_ESPACE if out of memory. This tries to realloc() more space before failing if maximum size has not yet been reached. Returns REG_OK if successful. */ -#define declare_pushf(typetag, type) \ - static reg_errcode_t tre_stack_push_##typetag(tre_stack_t *s, type value) +#define declare_pushf(typetag, type) \ + static reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value) declare_pushf(voidptr, void *); declare_pushf(int, int); @@ -253,29 +282,33 @@ declare_pushf(int, int); /* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost element off of stack `s' and returns it. The stack must not be empty. */ -#define declare_popf(typetag, type) \ - static type tre_stack_pop_##typetag(tre_stack_t *s) +#define declare_popf(typetag, type) \ + static type tre_stack_pop_ ## typetag(tre_stack_t *s) declare_popf(voidptr, void *); declare_popf(int, int); /* Just to save some typing. */ -#define STACK_PUSH(s, typetag, value) \ - do { \ - status = tre_stack_push_##typetag(s, value); \ - } while (/*CONSTCOND*/ 0) +#define STACK_PUSH(s, typetag, value) \ + do \ + { \ + status = tre_stack_push_ ## typetag(s, value); \ + } \ + while (/*CONSTCOND*/0) -#define STACK_PUSHX(s, typetag, value) \ - { \ - status = tre_stack_push_##typetag(s, value); \ - if (status != REG_OK) break; \ +#define STACK_PUSHX(s, typetag, value) \ + { \ + status = tre_stack_push_ ## typetag(s, value); \ + if (status != REG_OK) \ + break; \ } -#define STACK_PUSHR(s, typetag, value) \ - { \ - reg_errcode_t _status; \ - _status = tre_stack_push_##typetag(s, value); \ - if (_status != REG_OK) return _status; \ +#define STACK_PUSHR(s, typetag, value) \ + { \ + reg_errcode_t _status; \ + _status = tre_stack_push_ ## typetag(s, value); \ + if (_status != REG_OK) \ + return _status; \ } union tre_stack_item { @@ -291,193 +324,215 @@ struct tre_stack_rec { union tre_stack_item *stack; }; -static tre_stack_t *tre_stack_new(int size, int max_size, int increment) { + +static tre_stack_t * +tre_stack_new(int size, int max_size, int increment) +{ tre_stack_t *s; - s = malloc(sizeof(*s)); - if (s != NULL) { - s->stack = malloc(sizeof(*s->stack) * size); - if (s->stack == NULL) { - free(s), s = NULL; - return NULL; + s = xmalloc(sizeof(*s)); + if (s != NULL) + { + s->stack = xmalloc(sizeof(*s->stack) * size); + if (s->stack == NULL) + { + xfree(s); + return NULL; + } + s->size = size; + s->max_size = max_size; + s->increment = increment; + s->ptr = 0; } - s->size = size; - s->max_size = max_size; - s->increment = increment; - s->ptr = 0; - } return s; } -static void tre_stack_destroy(tre_stack_t *s) { - free(s->stack), s->stack = NULL; - free(s), s = NULL; +static void +tre_stack_destroy(tre_stack_t *s) +{ + xfree(s->stack); + xfree(s); } -static int tre_stack_num_objects(tre_stack_t *s) { +static int +tre_stack_num_objects(tre_stack_t *s) +{ return s->ptr; } -static reg_errcode_t tre_stack_push(tre_stack_t *s, - union tre_stack_item value) { - if (s->ptr < s->size) { - s->stack[s->ptr] = value; - s->ptr++; - } else { - if (s->size >= s->max_size) { - return REG_ESPACE; - } else { - union tre_stack_item *new_buffer; - int new_size; - new_size = s->size + s->increment; - if (new_size > s->max_size) new_size = s->max_size; - new_buffer = realloc(s->stack, sizeof(*new_buffer) * new_size); - if (new_buffer == NULL) { - return REG_ESPACE; - } - unassert(new_size > s->size); - s->size = new_size; - s->stack = new_buffer; - tre_stack_push(s, value); +static reg_errcode_t +tre_stack_push(tre_stack_t *s, union tre_stack_item value) +{ + if (s->ptr < s->size) + { + s->stack[s->ptr] = value; + s->ptr++; + } + else + { + if (s->size >= s->max_size) + { + return REG_ESPACE; + } + else + { + union tre_stack_item *new_buffer; + int new_size; + new_size = s->size + s->increment; + if (new_size > s->max_size) + new_size = s->max_size; + new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size); + if (new_buffer == NULL) + { + return REG_ESPACE; + } + assert(new_size > s->size); + s->size = new_size; + s->stack = new_buffer; + tre_stack_push(s, value); + } } - } return REG_OK; } -#define define_pushf(typetag, type) \ - declare_pushf(typetag, type) { \ - union tre_stack_item item; \ - item.typetag##_value = value; \ - return tre_stack_push(s, item); \ +#define define_pushf(typetag, type) \ + declare_pushf(typetag, type) { \ + union tre_stack_item item; \ + item.typetag ## _value = value; \ + return tre_stack_push(s, item); \ +} + +define_pushf(int, int) +define_pushf(voidptr, void *) + +#define define_popf(typetag, type) \ + declare_popf(typetag, type) { \ + return s->stack[--s->ptr].typetag ## _value; \ } -define_pushf(int, int) define_pushf(voidptr, void *) -#define define_popf(typetag, type) \ - declare_popf(typetag, type) { \ - return s->stack[--s->ptr].typetag##_value; \ - } +define_popf(int, int) +define_popf(voidptr, void *) - define_popf(int, int) define_popf(voidptr, void *) - /*********************************************************************** - from tre-parse.c and tre-parse.h - ***********************************************************************/ +/*********************************************************************** + from tre-parse.c and tre-parse.h +***********************************************************************/ - /* Parse context. */ - typedef struct { - /* Memory allocator. The AST is allocated using this. */ - tre_mem_t mem; - /* Stack used for keeping track of regexp syntax. */ - tre_stack_t *stack; - /* The parsed node after a parse function returns. */ - tre_ast_node_t *n; - /* Position in the regexp pattern after a parse function returns. */ - const char *s; - /* The first character of the last subexpression parsed. */ - const char *start; - /* Current submatch ID. */ - int submatch_id; - /* Current position (number of literal). */ - int position; - /* The highest back reference or -1 if none seen so far. */ - int max_backref; - /* Compilation flags. */ - int cflags; +/* Parse context. */ +typedef struct { + /* Memory allocator. The AST is allocated using this. */ + tre_mem_t mem; + /* Stack used for keeping track of regexp syntax. */ + tre_stack_t *stack; + /* The parsed node after a parse function returns. */ + tre_ast_node_t *n; + /* Position in the regexp pattern after a parse function returns. */ + const char *s; + /* The first character of the last subexpression parsed. */ + const char *start; + /* Current submatch ID. */ + int submatch_id; + /* Current position (number of literal). */ + int position; + /* The highest back reference or -1 if none seen so far. */ + int max_backref; + /* Compilation flags. */ + int cflags; } tre_parse_ctx_t; /* Some macros for expanding \w, \s, etc. */ static const struct { - char c; - const char *expansion; + char c; + const char *expansion; } tre_macros[] = { - {'t', "\t"}, - {'n', "\n"}, - {'r', "\r"}, - {'f', "\f"}, - {'a', "\a"}, - {'e', "\033"}, - {'w', "[[:alnum:]_]"}, - {'W', "[^[:alnum:]_]"}, - {'s', "[[:space:]]"}, - {'S', "[^[:space:]]"}, - {'d', "[[:digit:]]"}, - {'D', "[^[:digit:]]"}, - {0, 0}, + {'t', "\t"}, {'n', "\n"}, {'r', "\r"}, + {'f', "\f"}, {'a', "\a"}, {'e', "\033"}, + {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"}, + {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"}, + { 0, 0 } }; /* Expands a macro delimited by `regex' and `regex_end' to `buf', which must have at least `len' items. Sets buf[0] to zero if the there is no match in `tre_macros'. */ -static const char *tre_expand_macro(const char *s) { - int i; - for (i = 0; tre_macros[i].c && tre_macros[i].c != *s; i++) - ; - return tre_macros[i].expansion; +static const char *tre_expand_macro(const char *s) +{ + int i; + for (i = 0; tre_macros[i].c && tre_macros[i].c != *s; i++); + return tre_macros[i].expansion; } -static int tre_compare_lit(const void *a, const void *b) { - const tre_literal_t *const *la = a; - const tre_literal_t *const *lb = b; - /* assumes the range of valid code_min is < INT_MAX */ - return la[0]->code_min - lb[0]->code_min; +static int +tre_compare_lit(const void *a, const void *b) +{ + const tre_literal_t *const *la = a; + const tre_literal_t *const *lb = b; + /* assumes the range of valid code_min is < INT_MAX */ + return la[0]->code_min - lb[0]->code_min; } struct literals { - tre_mem_t mem; - tre_literal_t **a; - int len; - int cap; + tre_mem_t mem; + tre_literal_t **a; + int len; + int cap; }; -static tre_literal_t *tre_new_lit(struct literals *p) { - tre_literal_t **a; - if (p->len >= p->cap) { - if (p->cap >= 1 << 15) return 0; - p->cap *= 2; - a = realloc(p->a, p->cap * sizeof *p->a); - if (!a) return 0; - p->a = a; - } - a = p->a + p->len++; - *a = tre_mem_calloc(p->mem, sizeof **a); - return *a; +static tre_literal_t *tre_new_lit(struct literals *p) +{ + tre_literal_t **a; + if (p->len >= p->cap) { + if (p->cap >= 1<<15) + return 0; + p->cap *= 2; + a = xrealloc(p->a, p->cap * sizeof *p->a); + if (!a) + return 0; + p->a = a; + } + a = p->a + p->len++; + *a = tre_mem_calloc(p->mem, sizeof **a); + return *a; } -static int add_icase_literals(struct literals *ls, int min, int max) { - tre_literal_t *lit; - int b, e, c; - for (c = min; c <= max;) { - /* assumes islower(c) and isupper(c) are exclusive - and toupper(c)!=c if islower(c). - multiple opposite case characters are not supported */ - if (tre_islower(c)) { - b = e = tre_toupper(c); - for (c++, e++; c <= max; c++, e++) - if (tre_toupper(c) != e) break; - } else if (tre_isupper(c)) { - b = e = tre_tolower(c); - for (c++, e++; c <= max; c++, e++) - if (tre_tolower(c) != e) break; - } else { - c++; - continue; - } - lit = tre_new_lit(ls); - if (!lit) return -1; - lit->code_min = b; - lit->code_max = e - 1; - lit->position = -1; - } - return 0; +static int add_icase_literals(struct literals *ls, int min, int max) +{ + tre_literal_t *lit; + int b, e, c; + for (c=min; c<=max; ) { + /* assumes islower(c) and isupper(c) are exclusive + and toupper(c)!=c if islower(c). + multiple opposite case characters are not supported */ + if (tre_islower(c)) { + b = e = tre_toupper(c); + for (c++, e++; c<=max; c++, e++) + if (tre_toupper(c) != e) break; + } else if (tre_isupper(c)) { + b = e = tre_tolower(c); + for (c++, e++; c<=max; c++, e++) + if (tre_tolower(c) != e) break; + } else { + c++; + continue; + } + lit = tre_new_lit(ls); + if (!lit) + return -1; + lit->code_min = b; + lit->code_max = e-1; + lit->position = -1; + } + return 0; } + /* Maximum number of character classes in a negated bracket expression. */ #define MAX_NEG_CLASSES 64 struct neg { - int negate; - int len; - tre_ctype_t a[MAX_NEG_CLASSES]; + int negate; + int len; + tre_ctype_t a[MAX_NEG_CLASSES]; }; // TODO: parse bracket into a set of non-overlapping [lo,hi] ranges @@ -500,525 +555,565 @@ coll_single is a single char collating element but it can be '^' anywhere except after the openning '[' */ -static reg_errcode_t parse_bracket_terms(tre_parse_ctx_t *ctx, const char *s, - struct literals *ls, struct neg *neg) { - const char *start = s; - tre_ctype_t class; - int min, max; - wchar_t wc; - int len; +static reg_errcode_t parse_bracket_terms(tre_parse_ctx_t *ctx, const char *s, struct literals *ls, struct neg *neg) +{ + const char *start = s; + tre_ctype_t class; + int min, max; + wchar_t wc; + int len; - for (;;) { - class = 0; - len = mbtowc(&wc, s, -1); - if (len <= 0) return *s ? REG_BADPAT : REG_EBRACK; - if (*s == ']' && s != start) { - ctx->s = s + 1; - return REG_OK; - } - if (*s == '-' && s != start && s[1] != ']' && - /* extension: [a-z--@] is accepted as [a-z]|[--@] */ - (s[1] != '-' || s[2] == ']')) { - return REG_ERANGE; - } - if (*s == '[' && (s[1] == '.' || s[1] == '=')) { - /* collating symbols and equivalence classes are not supported */ - return REG_ECOLLATE; - } - if (*s == '[' && s[1] == ':') { - char tmp[CHARCLASS_NAME_MAX + 1]; - s += 2; - for (len = 0; len < CHARCLASS_NAME_MAX && s[len]; len++) { - if (s[len] == ':') { - memcpy(tmp, s, len); - tmp[len] = 0; - class = tre_ctype(tmp); - break; - } - } - if (!class || s[len + 1] != ']') return REG_ECTYPE; - min = 0; - max = TRE_CHAR_MAX; - s += len + 2; - } else { - min = max = wc; - s += len; - if (*s == '-' && s[1] != ']') { - s++; - len = mbtowc(&wc, s, -1); - max = wc; - /* XXX - Should use collation order instead of - encoding values in character ranges. */ - if (len <= 0 || min > max) { - return REG_ERANGE; - } - s += len; - } - } + for (;;) { + class = 0; + len = mbtowc(&wc, s, -1); + if (len <= 0) + return *s ? REG_BADPAT : REG_EBRACK; + if (*s == ']' && s != start) { + ctx->s = s+1; + return REG_OK; + } + if (*s == '-' && s != start && s[1] != ']' && + /* extension: [a-z--@] is accepted as [a-z]|[--@] */ + (s[1] != '-' || s[2] == ']')) + return REG_ERANGE; + if (*s == '[' && (s[1] == '.' || s[1] == '=')) + /* collating symbols and equivalence classes are not supported */ + return REG_ECOLLATE; + if (*s == '[' && s[1] == ':') { + char tmp[CHARCLASS_NAME_MAX+1]; + s += 2; + for (len=0; len < CHARCLASS_NAME_MAX && s[len]; len++) { + if (s[len] == ':') { + memcpy(tmp, s, len); + tmp[len] = 0; + class = tre_ctype(tmp); + break; + } + } + if (!class || s[len+1] != ']') + return REG_ECTYPE; + min = 0; + max = TRE_CHAR_MAX; + s += len+2; + } else { + min = max = wc; + s += len; + if (*s == '-' && s[1] != ']') { + s++; + len = mbtowc(&wc, s, -1); + max = wc; + /* XXX - Should use collation order instead of + encoding values in character ranges. */ + if (len <= 0 || min > max) + return REG_ERANGE; + s += len; + } + } - if (class && neg->negate) { - if (neg->len >= MAX_NEG_CLASSES) return REG_ESPACE; - neg->a[neg->len++] = class; - } else { - tre_literal_t *lit = tre_new_lit(ls); - if (!lit) return REG_ESPACE; - lit->code_min = min; - lit->code_max = max; - lit->class = class; - lit->position = -1; + if (class && neg->negate) { + if (neg->len >= MAX_NEG_CLASSES) + return REG_ESPACE; + neg->a[neg->len++] = class; + } else { + tre_literal_t *lit = tre_new_lit(ls); + if (!lit) + return REG_ESPACE; + lit->code_min = min; + lit->code_max = max; + lit->class = class; + lit->position = -1; - /* Add opposite-case codepoints if REG_ICASE is present. - It seems that POSIX requires that bracket negation - should happen before case-folding, but most practical - implementations do it the other way around. Changing - the order would need efficient representation of - case-fold ranges and bracket range sets even with - simple patterns so this is ok for now. */ - if (ctx->cflags & REG_ICASE && !class) - if (add_icase_literals(ls, min, max)) return REG_ESPACE; - } - } + /* Add opposite-case codepoints if REG_ICASE is present. + It seems that POSIX requires that bracket negation + should happen before case-folding, but most practical + implementations do it the other way around. Changing + the order would need efficient representation of + case-fold ranges and bracket range sets even with + simple patterns so this is ok for now. */ + if (ctx->cflags & REG_ICASE && !class) + if (add_icase_literals(ls, min, max)) + return REG_ESPACE; + } + } } -static reg_errcode_t parse_bracket(tre_parse_ctx_t *ctx, const char *s) { - int i, max, min, negmax, negmin; - tre_ast_node_t *node = 0, *n; - tre_ctype_t *nc = 0; - tre_literal_t *lit; - struct literals ls; - struct neg neg; - reg_errcode_t err; +static reg_errcode_t parse_bracket(tre_parse_ctx_t *ctx, const char *s) +{ + int i, max, min, negmax, negmin; + tre_ast_node_t *node = 0, *n; + tre_ctype_t *nc = 0; + tre_literal_t *lit; + struct literals ls; + struct neg neg; + reg_errcode_t err; - ls.mem = ctx->mem; - ls.len = 0; - ls.cap = 32; - ls.a = malloc(ls.cap * sizeof *ls.a); - if (!ls.a) return REG_ESPACE; - neg.len = 0; - neg.negate = *s == '^'; - if (neg.negate) s++; + ls.mem = ctx->mem; + ls.len = 0; + ls.cap = 32; + ls.a = xmalloc(ls.cap * sizeof *ls.a); + if (!ls.a) + return REG_ESPACE; + neg.len = 0; + neg.negate = *s == '^'; + if (neg.negate) + s++; - err = parse_bracket_terms(ctx, s, &ls, &neg); - if (err != REG_OK) goto parse_bracket_done; + err = parse_bracket_terms(ctx, s, &ls, &neg); + if (err != REG_OK) + goto parse_bracket_done; - if (neg.negate) { - /* - * With REG_NEWLINE, POSIX requires that newlines are not matched by - * any form of a non-matching list. - */ - if (ctx->cflags & REG_NEWLINE) { - lit = tre_new_lit(&ls); - if (!lit) { - err = REG_ESPACE; - goto parse_bracket_done; - } - lit->code_min = '\n'; - lit->code_max = '\n'; - lit->position = -1; - } - /* Sort the array if we need to negate it. */ - qsort(ls.a, ls.len, sizeof *ls.a, tre_compare_lit); - /* extra lit for the last negated range */ - lit = tre_new_lit(&ls); - if (!lit) { - err = REG_ESPACE; - goto parse_bracket_done; - } - lit->code_min = TRE_CHAR_MAX + 1; - lit->code_max = TRE_CHAR_MAX + 1; - lit->position = -1; - /* negated classes */ - if (neg.len) { - nc = tre_mem_alloc(ctx->mem, (neg.len + 1) * sizeof *neg.a); - if (!nc) { - err = REG_ESPACE; - goto parse_bracket_done; - } - memcpy(nc, neg.a, neg.len * sizeof *neg.a); - nc[neg.len] = 0; - } - } + if (neg.negate) { + /* + * With REG_NEWLINE, POSIX requires that newlines are not matched by + * any form of a non-matching list. + */ + if (ctx->cflags & REG_NEWLINE) { + lit = tre_new_lit(&ls); + if (!lit) { + err = REG_ESPACE; + goto parse_bracket_done; + } + lit->code_min = '\n'; + lit->code_max = '\n'; + lit->position = -1; + } + /* Sort the array if we need to negate it. */ + qsort(ls.a, ls.len, sizeof *ls.a, tre_compare_lit); + /* extra lit for the last negated range */ + lit = tre_new_lit(&ls); + if (!lit) { + err = REG_ESPACE; + goto parse_bracket_done; + } + lit->code_min = TRE_CHAR_MAX+1; + lit->code_max = TRE_CHAR_MAX+1; + lit->position = -1; + /* negated classes */ + if (neg.len) { + nc = tre_mem_alloc(ctx->mem, (neg.len+1)*sizeof *neg.a); + if (!nc) { + err = REG_ESPACE; + goto parse_bracket_done; + } + memcpy(nc, neg.a, neg.len*sizeof *neg.a); + nc[neg.len] = 0; + } + } - /* Build a union of the items in the array, negated if necessary. */ - negmax = negmin = 0; - for (i = 0; i < ls.len; i++) { - lit = ls.a[i]; - min = lit->code_min; - max = lit->code_max; - if (neg.negate) { - if (min <= negmin) { - /* Overlap. */ - negmin = MAX(max + 1, negmin); - continue; - } - negmax = min - 1; - lit->code_min = negmin; - lit->code_max = negmax; - negmin = max + 1; - } - lit->position = ctx->position; - lit->neg_classes = nc; - n = tre_ast_new_node(ctx->mem, LITERAL, lit); - node = tre_ast_new_union(ctx->mem, node, n); - if (!node) { - err = REG_ESPACE; - break; - } - } + /* Build a union of the items in the array, negated if necessary. */ + negmax = negmin = 0; + for (i = 0; i < ls.len; i++) { + lit = ls.a[i]; + min = lit->code_min; + max = lit->code_max; + if (neg.negate) { + if (min <= negmin) { + /* Overlap. */ + negmin = MAX(max + 1, negmin); + continue; + } + negmax = min - 1; + lit->code_min = negmin; + lit->code_max = negmax; + negmin = max + 1; + } + lit->position = ctx->position; + lit->neg_classes = nc; + n = tre_ast_new_node(ctx->mem, LITERAL, lit); + node = tre_ast_new_union(ctx->mem, node, n); + if (!node) { + err = REG_ESPACE; + break; + } + } parse_bracket_done: - free(ls.a), ls.a = NULL; - ctx->position++; - ctx->n = node; - return err; + xfree(ls.a); + ctx->position++; + ctx->n = node; + return err; } -static const char *parse_dup_count(const char *s, int *n) { - *n = -1; - if (!isdigit(*s)) return s; - *n = 0; - for (;;) { - *n = 10 * *n + (*s - '0'); - s++; - if (!isdigit(*s) || *n > RE_DUP_MAX) break; - } - return s; +static const char *parse_dup_count(const char *s, int *n) +{ + *n = -1; + if (!isdigit(*s)) + return s; + *n = 0; + for (;;) { + *n = 10 * *n + (*s - '0'); + s++; + if (!isdigit(*s) || *n > RE_DUP_MAX) + break; + } + return s; } -static const char *parse_dup(const char *s, int ere, int *pmin, int *pmax) { - int min, max; +static const char *parse_dup(const char *s, int ere, int *pmin, int *pmax) +{ + int min, max; - s = parse_dup_count(s, &min); - if (*s == ',') - s = parse_dup_count(s + 1, &max); - else - max = min; + s = parse_dup_count(s, &min); + if (*s == ',') + s = parse_dup_count(s+1, &max); + else + max = min; - if ((max < min && max >= 0) || max > RE_DUP_MAX || min > RE_DUP_MAX || - min < 0 || (!ere && *s++ != '\\') || *s++ != '}') - return 0; - *pmin = min; - *pmax = max; - return s; + if ( + (max < min && max >= 0) || + max > RE_DUP_MAX || + min > RE_DUP_MAX || + min < 0 || + (!ere && *s++ != '\\') || + *s++ != '}' + ) + return 0; + *pmin = min; + *pmax = max; + return s; } -static int hexval(unsigned c) { - if (c - '0' < 10) return c - '0'; - c |= 32; - if (c - 'a' < 6) return c - 'a' + 10; - return -1; +static int hexval(unsigned c) +{ + if (c-'0'<10) return c-'0'; + c |= 32; + if (c-'a'<6) return c-'a'+10; + return -1; } -static reg_errcode_t marksub(tre_parse_ctx_t *ctx, tre_ast_node_t *node, - int subid) { - if (node->submatch_id >= 0) { - tre_ast_node_t *n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); - if (!n) return REG_ESPACE; - n = tre_ast_new_catenation(ctx->mem, n, node); - if (!n) return REG_ESPACE; - n->num_submatches = node->num_submatches; - node = n; - } - node->submatch_id = subid; - node->num_submatches++; - ctx->n = node; - return REG_OK; +static reg_errcode_t marksub(tre_parse_ctx_t *ctx, tre_ast_node_t *node, int subid) +{ + if (node->submatch_id >= 0) { + tre_ast_node_t *n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + if (!n) + return REG_ESPACE; + n = tre_ast_new_catenation(ctx->mem, n, node); + if (!n) + return REG_ESPACE; + n->num_submatches = node->num_submatches; + node = n; + } + node->submatch_id = subid; + node->num_submatches++; + ctx->n = node; + return REG_OK; } /* BRE grammar: -Regex = Branch | '^' | '$' | '^$' | '^' Branch | Branch '$' | '^' -Branch '$' Branch = Atom | Branch Atom Atom = char | quoted_char | '.' -| Bracket | Atom Dup | '\(' Branch '\)' | back_ref Dup = '*' | '\{' -Count '\}' | '\{' Count ',\}' | '\{' Count ',' Count '\}' +Regex = Branch | '^' | '$' | '^$' | '^' Branch | Branch '$' | '^' Branch '$' +Branch = Atom | Branch Atom +Atom = char | quoted_char | '.' | Bracket | Atom Dup | '\(' Branch '\)' | back_ref +Dup = '*' | '\{' Count '\}' | '\{' Count ',\}' | '\{' Count ',' Count '\}' (leading ^ and trailing $ in a sub expr may be an anchor or literal as well) ERE grammar: Regex = Branch | Regex '|' Branch Branch = Atom | Branch Atom -Atom = char | quoted_char | '.' | Bracket | Atom Dup | '(' Regex -')' | '^' | '$' Dup = '*' | '+' | '?' | '{' Count '}' | '{' -Count ',}' | '{' Count ',' Count '}' +Atom = char | quoted_char | '.' | Bracket | Atom Dup | '(' Regex ')' | '^' | '$' +Dup = '*' | '+' | '?' | '{' Count '}' | '{' Count ',}' | '{' Count ',' Count '}' (a*+?, ^*, $+, \X, {, (|a) are unspecified) */ -static reg_errcode_t parse_atom(tre_parse_ctx_t *ctx, const char *s) { - int len, ere = ctx->cflags & REG_EXTENDED; - const char *p; - tre_ast_node_t *node; - wchar_t wc; - switch (*s) { - case '[': - return parse_bracket(ctx, s + 1); - case '\\': - p = tre_expand_macro(s + 1); - if (p) { - /* assume \X expansion is a single atom */ - reg_errcode_t err = parse_atom(ctx, p); - ctx->s = s + 2; - return err; - } - /* extensions: \b, \B, \<, \>, \xHH \x{HHHH} */ - switch (*++s) { - case 0: - return REG_EESCAPE; - case 'b': - node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB, -1); - break; - case 'B': - node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB_NEG, -1); - break; - case '<': - node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOW, -1); - break; - case '>': - node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOW, -1); - break; - case 'x': - s++; - int i, v = 0, c; - len = 2; - if (*s == '{') { - len = 8; - s++; - } - for (i = 0; i < len && v < 0x110000; i++) { - c = hexval(s[i]); - if (c < 0) break; - v = 16 * v + c; - } - s += i; - if (len == 8) { - if (*s != '}') return REG_EBRACE; - s++; - } - node = tre_ast_new_literal(ctx->mem, v, v, ctx->position++); - s--; - break; - case '{': - case '+': - case '?': - /* extension: treat \+, \? as repetitions in BRE */ - /* reject repetitions after empty expression in BRE */ - if (!ere) return REG_BADRPT; - /* fallthrough */ - case '|': - /* extension: treat \| as alternation in BRE */ - if (!ere) { - node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); - s--; - goto end; - } - /* fallthrough */ - default: - if (!ere && (unsigned)*s - '1' < 9) { - /* back reference */ - int val = *s - '0'; - node = tre_ast_new_literal(ctx->mem, BACKREF, val, ctx->position++); - ctx->max_backref = MAX(val, ctx->max_backref); - } else { - /* extension: accept unknown escaped char - as a literal */ - goto parse_literal; - } - } - s++; - break; - case '.': - if (ctx->cflags & REG_NEWLINE) { - tre_ast_node_t *tmp1, *tmp2; - tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n' - 1, ctx->position++); - tmp2 = tre_ast_new_literal(ctx->mem, '\n' + 1, TRE_CHAR_MAX, - ctx->position++); - if (tmp1 && tmp2) - node = tre_ast_new_union(ctx->mem, tmp1, tmp2); - else - node = 0; - } else { - node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position++); - } - s++; - break; - case '^': - /* '^' has a special meaning everywhere in EREs, and at beginning of BRE. - */ - if (!ere && s != ctx->start) goto parse_literal; - node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOL, -1); - s++; - break; - case '$': - /* '$' is special everywhere in EREs, and at the end of a BRE - * subexpression. */ - if (!ere && s[1] && (s[1] != '\\' || (s[2] != ')' && s[2] != '|'))) - goto parse_literal; - node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOL, -1); - s++; - break; - case '*': - case '{': - case '+': - case '?': - /* reject repetitions after empty expression in ERE */ - if (ere) return REG_BADRPT; - /* fallthrough */ - case '|': - if (!ere) goto parse_literal; - /* fallthrough */ - case 0: - node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); - break; - default: - parse_literal: - len = mbtowc(&wc, s, -1); - if (len < 0) return REG_BADPAT; - if (ctx->cflags & REG_ICASE && (tre_isupper(wc) || tre_islower(wc))) { - tre_ast_node_t *tmp1, *tmp2; - /* multiple opposite case characters are not supported */ - tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc), tre_toupper(wc), - ctx->position); - tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc), tre_tolower(wc), - ctx->position); - if (tmp1 && tmp2) - node = tre_ast_new_union(ctx->mem, tmp1, tmp2); - else - node = 0; - } else { - node = tre_ast_new_literal(ctx->mem, wc, wc, ctx->position); - } - ctx->position++; - s += len; - break; - } +static reg_errcode_t parse_atom(tre_parse_ctx_t *ctx, const char *s) +{ + int len, ere = ctx->cflags & REG_EXTENDED; + const char *p; + tre_ast_node_t *node; + wchar_t wc; + switch (*s) { + case '[': + return parse_bracket(ctx, s+1); + case '\\': + p = tre_expand_macro(s+1); + if (p) { + /* assume \X expansion is a single atom */ + reg_errcode_t err = parse_atom(ctx, p); + ctx->s = s+2; + return err; + } + /* extensions: \b, \B, \<, \>, \xHH \x{HHHH} */ + switch (*++s) { + case 0: + return REG_EESCAPE; + case 'b': + node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB, -1); + break; + case 'B': + node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB_NEG, -1); + break; + case '<': + node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOW, -1); + break; + case '>': + node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOW, -1); + break; + case 'x': + s++; + int i, v = 0, c; + len = 2; + if (*s == '{') { + len = 8; + s++; + } + for (i=0; imem, v, v, ctx->position++); + s--; + break; + case '{': + case '+': + case '?': + /* extension: treat \+, \? as repetitions in BRE */ + /* reject repetitions after empty expression in BRE */ + if (!ere) + return REG_BADRPT; + case '|': + /* extension: treat \| as alternation in BRE */ + if (!ere) { + node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + s--; + goto end; + } + /* fallthrough */ + default: + if (!ere && (unsigned)*s-'1' < 9) { + /* back reference */ + int val = *s - '0'; + node = tre_ast_new_literal(ctx->mem, BACKREF, val, ctx->position++); + ctx->max_backref = MAX(val, ctx->max_backref); + } else { + /* extension: accept unknown escaped char + as a literal */ + goto parse_literal; + } + } + s++; + break; + case '.': + if (ctx->cflags & REG_NEWLINE) { + tre_ast_node_t *tmp1, *tmp2; + tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n'-1, ctx->position++); + tmp2 = tre_ast_new_literal(ctx->mem, '\n'+1, TRE_CHAR_MAX, ctx->position++); + if (tmp1 && tmp2) + node = tre_ast_new_union(ctx->mem, tmp1, tmp2); + else + node = 0; + } else { + node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position++); + } + s++; + break; + case '^': + /* '^' has a special meaning everywhere in EREs, and at beginning of BRE. */ + if (!ere && s != ctx->start) + goto parse_literal; + node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOL, -1); + s++; + break; + case '$': + /* '$' is special everywhere in EREs, and at the end of a BRE subexpression. */ + if (!ere && s[1] && (s[1]!='\\'|| (s[2]!=')' && s[2]!='|'))) + goto parse_literal; + node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOL, -1); + s++; + break; + case '*': + case '{': + case '+': + case '?': + /* reject repetitions after empty expression in ERE */ + if (ere) + return REG_BADRPT; + case '|': + if (!ere) + goto parse_literal; + case 0: + node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + break; + default: +parse_literal: + len = mbtowc(&wc, s, -1); + if (len < 0) + return REG_BADPAT; + if (ctx->cflags & REG_ICASE && (tre_isupper(wc) || tre_islower(wc))) { + tre_ast_node_t *tmp1, *tmp2; + /* multiple opposite case characters are not supported */ + tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc), tre_toupper(wc), ctx->position); + tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc), tre_tolower(wc), ctx->position); + if (tmp1 && tmp2) + node = tre_ast_new_union(ctx->mem, tmp1, tmp2); + else + node = 0; + } else { + node = tre_ast_new_literal(ctx->mem, wc, wc, ctx->position); + } + ctx->position++; + s += len; + break; + } end: - if (!node) return REG_ESPACE; - ctx->n = node; - ctx->s = s; - return REG_OK; + if (!node) + return REG_ESPACE; + ctx->n = node; + ctx->s = s; + return REG_OK; } -#define PUSHPTR(err, s, v) \ - do { \ - if ((err = tre_stack_push_voidptr(s, v)) != REG_OK) return err; \ - } while (0) +#define PUSHPTR(err, s, v) do { \ + if ((err = tre_stack_push_voidptr(s, v)) != REG_OK) \ + return err; \ +} while(0) -#define PUSHINT(err, s, v) \ - do { \ - if ((err = tre_stack_push_int(s, v)) != REG_OK) return err; \ - } while (0) +#define PUSHINT(err, s, v) do { \ + if ((err = tre_stack_push_int(s, v)) != REG_OK) \ + return err; \ +} while(0) -static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx) { - tre_ast_node_t *nbranch = 0, *nunion = 0; - int ere = ctx->cflags & REG_EXTENDED; - const char *s = ctx->start; - int subid = 0; - int depth = 0; - reg_errcode_t err; - tre_stack_t *stack = ctx->stack; +static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx) +{ + tre_ast_node_t *nbranch=0, *nunion=0; + int ere = ctx->cflags & REG_EXTENDED; + const char *s = ctx->start; + int subid = 0; + int depth = 0; + reg_errcode_t err; + tre_stack_t *stack = ctx->stack; - PUSHINT(err, stack, subid++); - for (;;) { - if ((!ere && *s == '\\' && s[1] == '(') || (ere && *s == '(')) { - PUSHPTR(err, stack, nunion); - PUSHPTR(err, stack, nbranch); - PUSHINT(err, stack, subid++); - s++; - if (!ere) s++; - depth++; - nbranch = nunion = 0; - ctx->start = s; - continue; - } - if ((!ere && *s == '\\' && s[1] == ')') || (ere && *s == ')' && depth)) { - ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); - if (!ctx->n) return REG_ESPACE; - } else { - err = parse_atom(ctx, s); - if (err != REG_OK) return err; - s = ctx->s; - } + PUSHINT(err, stack, subid++); + for (;;) { + if ((!ere && *s == '\\' && s[1] == '(') || + (ere && *s == '(')) { + PUSHPTR(err, stack, nunion); + PUSHPTR(err, stack, nbranch); + PUSHINT(err, stack, subid++); + s++; + if (!ere) + s++; + depth++; + nbranch = nunion = 0; + ctx->start = s; + continue; + } + if ((!ere && *s == '\\' && s[1] == ')') || + (ere && *s == ')' && depth)) { + ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + if (!ctx->n) + return REG_ESPACE; + } else { + err = parse_atom(ctx, s); + if (err != REG_OK) + return err; + s = ctx->s; + } - parse_iter: - for (;;) { - int min, max; + parse_iter: + for (;;) { + int min, max; - if (*s != '\\' && *s != '*') { - if (!ere) break; - if (*s != '+' && *s != '?' && *s != '{') break; - } - if (*s == '\\' && ere) break; - /* extension: treat \+, \? as repetitions in BRE */ - if (*s == '\\' && s[1] != '+' && s[1] != '?' && s[1] != '{') break; - if (*s == '\\') s++; + if (*s!='\\' && *s!='*') { + if (!ere) + break; + if (*s!='+' && *s!='?' && *s!='{') + break; + } + if (*s=='\\' && ere) + break; + /* extension: treat \+, \? as repetitions in BRE */ + if (*s=='\\' && s[1]!='+' && s[1]!='?' && s[1]!='{') + break; + if (*s=='\\') + s++; - /* handle ^* at the start of a BRE. */ - if (!ere && s == ctx->start + 1 && s[-1] == '^') break; + /* handle ^* at the start of a BRE. */ + if (!ere && s==ctx->start+1 && s[-1]=='^') + break; - /* extension: multiple consecutive *+?{,} is unspecified, - but (a+)+ has to be supported so accepting a++ makes - sense, note however that the RE_DUP_MAX limit can be - circumvented: (a{255}){255} uses a lot of memory.. */ - if (*s == '{') { - s = parse_dup(s + 1, ere, &min, &max); - if (!s) return REG_BADBR; - } else { - min = 0; - max = -1; - if (*s == '+') min = 1; - if (*s == '?') max = 1; - s++; - } - if (max == 0) - ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); - else - ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0); - if (!ctx->n) return REG_ESPACE; - } + /* extension: multiple consecutive *+?{,} is unspecified, + but (a+)+ has to be supported so accepting a++ makes + sense, note however that the RE_DUP_MAX limit can be + circumvented: (a{255}){255} uses a lot of memory.. */ + if (*s=='{') { + s = parse_dup(s+1, ere, &min, &max); + if (!s) + return REG_BADBR; + } else { + min=0; + max=-1; + if (*s == '+') + min = 1; + if (*s == '?') + max = 1; + s++; + } + if (max == 0) + ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + else + ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0); + if (!ctx->n) + return REG_ESPACE; + } - nbranch = tre_ast_new_catenation(ctx->mem, nbranch, ctx->n); - if ((ere && *s == '|') || (ere && *s == ')' && depth) || - (!ere && *s == '\\' && s[1] == ')') || - /* extension: treat \| as alternation in BRE */ - (!ere && *s == '\\' && s[1] == '|') || !*s) { - /* extension: empty branch is unspecified (), (|a), (a|) - here they are not rejected but match on empty string */ - int c = *s; - nunion = tre_ast_new_union(ctx->mem, nunion, nbranch); - nbranch = 0; + nbranch = tre_ast_new_catenation(ctx->mem, nbranch, ctx->n); + if ((ere && *s == '|') || + (ere && *s == ')' && depth) || + (!ere && *s == '\\' && s[1] == ')') || + /* extension: treat \| as alternation in BRE */ + (!ere && *s == '\\' && s[1] == '|') || + !*s) { + /* extension: empty branch is unspecified (), (|a), (a|) + here they are not rejected but match on empty string */ + int c = *s; + nunion = tre_ast_new_union(ctx->mem, nunion, nbranch); + nbranch = 0; - if (c == '\\' && s[1] == '|') { - s += 2; - ctx->start = s; - } else if (c == '|') { - s++; - ctx->start = s; - } else { - if (c == '\\') { - if (!depth) return REG_EPAREN; - s += 2; - } else if (c == ')') - s++; - depth--; - err = marksub(ctx, nunion, tre_stack_pop_int(stack)); - if (err != REG_OK) return err; - if (!c && depth < 0) { - ctx->submatch_id = subid; - return REG_OK; - } - if (!c || depth < 0) return REG_EPAREN; - nbranch = tre_stack_pop_voidptr(stack); - nunion = tre_stack_pop_voidptr(stack); - goto parse_iter; - } - } - } + if (c == '\\' && s[1] == '|') { + s+=2; + ctx->start = s; + } else if (c == '|') { + s++; + ctx->start = s; + } else { + if (c == '\\') { + if (!depth) return REG_EPAREN; + s+=2; + } else if (c == ')') + s++; + depth--; + err = marksub(ctx, nunion, tre_stack_pop_int(stack)); + if (err != REG_OK) + return err; + if (!c && depth<0) { + ctx->submatch_id = subid; + return REG_OK; + } + if (!c || depth<0) + return REG_EPAREN; + nbranch = tre_stack_pop_voidptr(stack); + nunion = tre_stack_pop_voidptr(stack); + goto parse_iter; + } + } + } } + /*********************************************************************** from tre-compile.c ***********************************************************************/ + /* TODO: - Fix tre_ast_to_tnfa() to recurse using a stack instead of recursive @@ -1029,19 +1124,24 @@ static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx) { Algorithms to setup tags so that submatch addressing can be done. */ + /* Inserts a catenation node to the root of the tree given in `node'. As the left child a new tag with number `tag_id' to `node' is added, and the right child is the old root. */ -static reg_errcode_t tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, - int tag_id) { +static reg_errcode_t +tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, int tag_id) +{ tre_catenation_t *c; c = tre_mem_alloc(mem, sizeof(*c)); - if (c == NULL) return REG_ESPACE; + if (c == NULL) + return REG_ESPACE; c->left = tre_ast_new_literal(mem, TAG, tag_id, -1); - if (c->left == NULL) return REG_ESPACE; + if (c->left == NULL) + return REG_ESPACE; c->right = tre_mem_alloc(mem, sizeof(tre_ast_node_t)); - if (c->right == NULL) return REG_ESPACE; + if (c->right == NULL) + return REG_ESPACE; c->right->obj = node->obj; c->right->type = node->type; @@ -1059,16 +1159,20 @@ static reg_errcode_t tre_add_tag_left(tre_mem_t mem, tre_ast_node_t *node, /* Inserts a catenation node to the root of the tree given in `node'. As the right child a new tag with number `tag_id' to `node' is added, and the left child is the old root. */ -static reg_errcode_t tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, - int tag_id) { +static reg_errcode_t +tre_add_tag_right(tre_mem_t mem, tre_ast_node_t *node, int tag_id) +{ tre_catenation_t *c; c = tre_mem_alloc(mem, sizeof(*c)); - if (c == NULL) return REG_ESPACE; + if (c == NULL) + return REG_ESPACE; c->right = tre_ast_new_literal(mem, TAG, tag_id, -1); - if (c->right == NULL) return REG_ESPACE; + if (c->right == NULL) + return REG_ESPACE; c->left = tre_mem_alloc(mem, sizeof(tre_ast_node_t)); - if (c->left == NULL) return REG_ESPACE; + if (c->left == NULL) + return REG_ESPACE; c->left->obj = node->obj; c->left->type = node->type; @@ -1093,31 +1197,39 @@ typedef enum { ADDTAGS_SET_SUBMATCH_END } tre_addtags_symbol_t; + typedef struct { int tag; int next_tag; } tre_tag_states_t; + /* Go through `regset' and set submatch data for submatches that are using this tag. */ -static void tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag) { +static void +tre_purge_regset(int *regset, tre_tnfa_t *tnfa, int tag) +{ int i; - for (i = 0; regset[i] >= 0; i++) { - int id = regset[i] / 2; - int start = !(regset[i] % 2); - if (start) - tnfa->submatch_data[id].so_tag = tag; - else - tnfa->submatch_data[id].eo_tag = tag; - } + for (i = 0; regset[i] >= 0; i++) + { + int id = regset[i] / 2; + int start = !(regset[i] % 2); + if (start) + tnfa->submatch_data[id].so_tag = tag; + else + tnfa->submatch_data[id].eo_tag = tag; + } regset[0] = -1; } + /* Adds tags to appropriate locations in the parse tree in `tree', so that subexpressions marked for submatch addressing can be traced. */ -static reg_errcode_t tre_add_tags(tre_mem_t mem, tre_stack_t *stack, - tre_ast_node_t *tree, tre_tnfa_t *tnfa) { +static reg_errcode_t +tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree, + tre_tnfa_t *tnfa) +{ reg_errcode_t status = REG_OK; tre_addtags_symbol_t symbol; tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */ @@ -1125,422 +1237,484 @@ static reg_errcode_t tre_add_tags(tre_mem_t mem, tre_stack_t *stack, /* True for first pass (counting number of needed tags) */ int first_pass = (mem == NULL || tnfa == NULL); int *regset, *orig_regset; - int num_tags = 0; /* Total number of tags. */ - int num_minimals = 0; /* Number of special minimal tags. */ - int tag = 0; /* The tag that is to be added next. */ - int next_tag = 1; /* Next tag to use after this one. */ - int *parents; /* Stack of submatches the current submatch is - contained in. */ + int num_tags = 0; /* Total number of tags. */ + int num_minimals = 0; /* Number of special minimal tags. */ + int tag = 0; /* The tag that is to be added next. */ + int next_tag = 1; /* Next tag to use after this one. */ + int *parents; /* Stack of submatches the current submatch is + contained in. */ int minimal_tag = -1; /* Tag that marks the beginning of a minimal match. */ tre_tag_states_t *saved_states; tre_tag_direction_t direction = TRE_TAG_MINIMIZE; - if (!first_pass) { - tnfa->end_tag = 0; - tnfa->minimal_tags[0] = -1; - } + if (!first_pass) + { + tnfa->end_tag = 0; + tnfa->minimal_tags[0] = -1; + } - regset = malloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2)); - if (regset == NULL) return REG_ESPACE; + regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2)); + if (regset == NULL) + return REG_ESPACE; regset[0] = -1; orig_regset = regset; - parents = malloc(sizeof(*parents) * (tnfa->num_submatches + 1)); - if (parents == NULL) { - free(regset), regset = NULL; - return REG_ESPACE; - } + parents = xmalloc(sizeof(*parents) * (tnfa->num_submatches + 1)); + if (parents == NULL) + { + xfree(regset); + return REG_ESPACE; + } parents[0] = -1; - saved_states = malloc(sizeof(*saved_states) * (tnfa->num_submatches + 1)); - if (saved_states == NULL) { - free(regset), regset = NULL; - free(parents), parents = NULL; - return REG_ESPACE; - } else { - unsigned int i; - for (i = 0; i <= tnfa->num_submatches; i++) saved_states[i].tag = -1; - } + saved_states = xmalloc(sizeof(*saved_states) * (tnfa->num_submatches + 1)); + if (saved_states == NULL) + { + xfree(regset); + xfree(parents); + return REG_ESPACE; + } + else + { + unsigned int i; + for (i = 0; i <= tnfa->num_submatches; i++) + saved_states[i].tag = -1; + } STACK_PUSH(stack, voidptr, node); STACK_PUSH(stack, int, ADDTAGS_RECURSE); - while (tre_stack_num_objects(stack) > bottom) { - if (status != REG_OK) break; + while (tre_stack_num_objects(stack) > bottom) + { + if (status != REG_OK) + break; - symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack); - switch (symbol) { - case ADDTAGS_SET_SUBMATCH_END: { - int id = tre_stack_pop_int(stack); - int i; + symbol = (tre_addtags_symbol_t)tre_stack_pop_int(stack); + switch (symbol) + { - /* Add end of this submatch to regset. */ - for (i = 0; regset[i] >= 0; i++) - ; - regset[i] = id * 2 + 1; - regset[i + 1] = -1; + case ADDTAGS_SET_SUBMATCH_END: + { + int id = tre_stack_pop_int(stack); + int i; - /* Pop this submatch from the parents stack. */ - for (i = 0; parents[i] >= 0; i++) - ; - parents[i - 1] = -1; - break; - } + /* Add end of this submatch to regset. */ + for (i = 0; regset[i] >= 0; i++); + regset[i] = id * 2 + 1; + regset[i + 1] = -1; - case ADDTAGS_RECURSE: - node = tre_stack_pop_voidptr(stack); + /* Pop this submatch from the parents stack. */ + for (i = 0; parents[i] >= 0; i++); + parents[i - 1] = -1; + break; + } - if (node->submatch_id >= 0) { - int id = node->submatch_id; - int i; + case ADDTAGS_RECURSE: + node = tre_stack_pop_voidptr(stack); - /* Add start of this submatch to regset. */ - for (i = 0; regset[i] >= 0; i++) - ; - regset[i] = id * 2; - regset[i + 1] = -1; + if (node->submatch_id >= 0) + { + int id = node->submatch_id; + int i; - if (!first_pass) { - for (i = 0; parents[i] >= 0; i++) - ; - tnfa->submatch_data[id].parents = NULL; - if (i > 0) { - int *p = malloc(sizeof(*p) * (i + 1)); - if (p == NULL) { - status = REG_ESPACE; - break; - } - unassert(tnfa->submatch_data[id].parents == NULL); - tnfa->submatch_data[id].parents = p; - for (i = 0; parents[i] >= 0; i++) p[i] = parents[i]; - p[i] = -1; - } - } - /* Add end of this submatch to regset after processing this - node. */ - STACK_PUSHX(stack, int, node->submatch_id); - STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END); - } + /* Add start of this submatch to regset. */ + for (i = 0; regset[i] >= 0; i++); + regset[i] = id * 2; + regset[i + 1] = -1; - switch (node->type) { - case LITERAL: { - tre_literal_t *lit = node->obj; + if (!first_pass) + { + for (i = 0; parents[i] >= 0; i++); + tnfa->submatch_data[id].parents = NULL; + if (i > 0) + { + int *p = xmalloc(sizeof(*p) * (i + 1)); + if (p == NULL) + { + status = REG_ESPACE; + break; + } + assert(tnfa->submatch_data[id].parents == NULL); + tnfa->submatch_data[id].parents = p; + for (i = 0; parents[i] >= 0; i++) + p[i] = parents[i]; + p[i] = -1; + } + } - if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) { - int i; - if (regset[0] >= 0) { - /* Regset is not empty, so add a tag before the - literal or backref. */ - if (!first_pass) { - status = tre_add_tag_left(mem, node, tag); - tnfa->tag_directions[tag] = direction; - if (minimal_tag >= 0) { - for (i = 0; tnfa->minimal_tags[i] >= 0; i++) - ; - tnfa->minimal_tags[i] = tag; - tnfa->minimal_tags[i + 1] = minimal_tag; - tnfa->minimal_tags[i + 2] = -1; - minimal_tag = -1; - num_minimals++; - } - tre_purge_regset(regset, tnfa, tag); - } else { - node->num_tags = 1; - } + /* Add end of this submatch to regset after processing this + node. */ + STACK_PUSHX(stack, int, node->submatch_id); + STACK_PUSHX(stack, int, ADDTAGS_SET_SUBMATCH_END); + } - regset[0] = -1; - tag = next_tag; - num_tags++; - next_tag++; - } - } else { - unassert(!IS_TAG(lit)); - } - break; - } - case CATENATION: { - tre_catenation_t *cat = node->obj; - tre_ast_node_t *left = cat->left; - tre_ast_node_t *right = cat->right; - int reserved_tag = -1; + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit = node->obj; - /* After processing right child. */ - STACK_PUSHX(stack, voidptr, node); - STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT); + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { + int i; + if (regset[0] >= 0) + { + /* Regset is not empty, so add a tag before the + literal or backref. */ + if (!first_pass) + { + status = tre_add_tag_left(mem, node, tag); + tnfa->tag_directions[tag] = direction; + if (minimal_tag >= 0) + { + for (i = 0; tnfa->minimal_tags[i] >= 0; i++); + tnfa->minimal_tags[i] = tag; + tnfa->minimal_tags[i + 1] = minimal_tag; + tnfa->minimal_tags[i + 2] = -1; + minimal_tag = -1; + num_minimals++; + } + tre_purge_regset(regset, tnfa, tag); + } + else + { + node->num_tags = 1; + } - /* Process right child. */ - STACK_PUSHX(stack, voidptr, right); - STACK_PUSHX(stack, int, ADDTAGS_RECURSE); + regset[0] = -1; + tag = next_tag; + num_tags++; + next_tag++; + } + } + else + { + assert(!IS_TAG(lit)); + } + break; + } + case CATENATION: + { + tre_catenation_t *cat = node->obj; + tre_ast_node_t *left = cat->left; + tre_ast_node_t *right = cat->right; + int reserved_tag = -1; - /* After processing left child. */ - STACK_PUSHX(stack, int, next_tag + left->num_tags); - if (left->num_tags > 0 && right->num_tags > 0) { - /* Reserve the next tag to the right child. */ - reserved_tag = next_tag; - next_tag++; - } - STACK_PUSHX(stack, int, reserved_tag); - STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT); - /* Process left child. */ - STACK_PUSHX(stack, voidptr, left); - STACK_PUSHX(stack, int, ADDTAGS_RECURSE); + /* After processing right child. */ + STACK_PUSHX(stack, voidptr, node); + STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_RIGHT); - } break; - case ITERATION: { - tre_iteration_t *iter = node->obj; + /* Process right child. */ + STACK_PUSHX(stack, voidptr, right); + STACK_PUSHX(stack, int, ADDTAGS_RECURSE); - if (first_pass) { - STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal); - } else { - STACK_PUSHX(stack, int, tag); - STACK_PUSHX(stack, int, iter->minimal); - } - STACK_PUSHX(stack, voidptr, node); - STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION); + /* After processing left child. */ + STACK_PUSHX(stack, int, next_tag + left->num_tags); + if (left->num_tags > 0 && right->num_tags > 0) + { + /* Reserve the next tag to the right child. */ + reserved_tag = next_tag; + next_tag++; + } + STACK_PUSHX(stack, int, reserved_tag); + STACK_PUSHX(stack, int, ADDTAGS_AFTER_CAT_LEFT); - STACK_PUSHX(stack, voidptr, iter->arg); - STACK_PUSHX(stack, int, ADDTAGS_RECURSE); + /* Process left child. */ + STACK_PUSHX(stack, voidptr, left); + STACK_PUSHX(stack, int, ADDTAGS_RECURSE); - /* Regset is not empty, so add a tag here. */ - if (regset[0] >= 0 || iter->minimal) { - if (!first_pass) { - int i; - status = tre_add_tag_left(mem, node, tag); - if (iter->minimal) - tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE; - else - tnfa->tag_directions[tag] = direction; - if (minimal_tag >= 0) { - for (i = 0; tnfa->minimal_tags[i] >= 0; i++) - ; - tnfa->minimal_tags[i] = tag; - tnfa->minimal_tags[i + 1] = minimal_tag; - tnfa->minimal_tags[i + 2] = -1; - minimal_tag = -1; - num_minimals++; - } - tre_purge_regset(regset, tnfa, tag); - } + } + break; + case ITERATION: + { + tre_iteration_t *iter = node->obj; - regset[0] = -1; - tag = next_tag; - num_tags++; - next_tag++; - } - direction = TRE_TAG_MINIMIZE; - } break; - case UNION: { - tre_union_t *uni = node->obj; - tre_ast_node_t *left = uni->left; - tre_ast_node_t *right = uni->right; - int left_tag; - int right_tag; + if (first_pass) + { + STACK_PUSHX(stack, int, regset[0] >= 0 || iter->minimal); + } + else + { + STACK_PUSHX(stack, int, tag); + STACK_PUSHX(stack, int, iter->minimal); + } + STACK_PUSHX(stack, voidptr, node); + STACK_PUSHX(stack, int, ADDTAGS_AFTER_ITERATION); - if (regset[0] >= 0) { - left_tag = next_tag; - right_tag = next_tag + 1; - } else { - left_tag = tag; - right_tag = next_tag; - } + STACK_PUSHX(stack, voidptr, iter->arg); + STACK_PUSHX(stack, int, ADDTAGS_RECURSE); - /* After processing right child. */ - STACK_PUSHX(stack, int, right_tag); - STACK_PUSHX(stack, int, left_tag); - STACK_PUSHX(stack, voidptr, regset); - STACK_PUSHX(stack, int, regset[0] >= 0); - STACK_PUSHX(stack, voidptr, node); - STACK_PUSHX(stack, voidptr, right); - STACK_PUSHX(stack, voidptr, left); - STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT); + /* Regset is not empty, so add a tag here. */ + if (regset[0] >= 0 || iter->minimal) + { + if (!first_pass) + { + int i; + status = tre_add_tag_left(mem, node, tag); + if (iter->minimal) + tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE; + else + tnfa->tag_directions[tag] = direction; + if (minimal_tag >= 0) + { + for (i = 0; tnfa->minimal_tags[i] >= 0; i++); + tnfa->minimal_tags[i] = tag; + tnfa->minimal_tags[i + 1] = minimal_tag; + tnfa->minimal_tags[i + 2] = -1; + minimal_tag = -1; + num_minimals++; + } + tre_purge_regset(regset, tnfa, tag); + } - /* Process right child. */ - STACK_PUSHX(stack, voidptr, right); - STACK_PUSHX(stack, int, ADDTAGS_RECURSE); + regset[0] = -1; + tag = next_tag; + num_tags++; + next_tag++; + } + direction = TRE_TAG_MINIMIZE; + } + break; + case UNION: + { + tre_union_t *uni = node->obj; + tre_ast_node_t *left = uni->left; + tre_ast_node_t *right = uni->right; + int left_tag; + int right_tag; - /* After processing left child. */ - STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT); + if (regset[0] >= 0) + { + left_tag = next_tag; + right_tag = next_tag + 1; + } + else + { + left_tag = tag; + right_tag = next_tag; + } - /* Process left child. */ - STACK_PUSHX(stack, voidptr, left); - STACK_PUSHX(stack, int, ADDTAGS_RECURSE); + /* After processing right child. */ + STACK_PUSHX(stack, int, right_tag); + STACK_PUSHX(stack, int, left_tag); + STACK_PUSHX(stack, voidptr, regset); + STACK_PUSHX(stack, int, regset[0] >= 0); + STACK_PUSHX(stack, voidptr, node); + STACK_PUSHX(stack, voidptr, right); + STACK_PUSHX(stack, voidptr, left); + STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_RIGHT); - /* Regset is not empty, so add a tag here. */ - if (regset[0] >= 0) { - if (!first_pass) { - int i; - status = tre_add_tag_left(mem, node, tag); - tnfa->tag_directions[tag] = direction; - if (minimal_tag >= 0) { - for (i = 0; tnfa->minimal_tags[i] >= 0; i++) - ; - tnfa->minimal_tags[i] = tag; - tnfa->minimal_tags[i + 1] = minimal_tag; - tnfa->minimal_tags[i + 2] = -1; - minimal_tag = -1; - num_minimals++; - } - tre_purge_regset(regset, tnfa, tag); - } + /* Process right child. */ + STACK_PUSHX(stack, voidptr, right); + STACK_PUSHX(stack, int, ADDTAGS_RECURSE); - regset[0] = -1; - tag = next_tag; - num_tags++; - next_tag++; - } + /* After processing left child. */ + STACK_PUSHX(stack, int, ADDTAGS_AFTER_UNION_LEFT); - if (node->num_submatches > 0) { - /* The next two tags are reserved for markers. */ - next_tag++; - tag = next_tag; - next_tag++; - } + /* Process left child. */ + STACK_PUSHX(stack, voidptr, left); + STACK_PUSHX(stack, int, ADDTAGS_RECURSE); - break; - } - } + /* Regset is not empty, so add a tag here. */ + if (regset[0] >= 0) + { + if (!first_pass) + { + int i; + status = tre_add_tag_left(mem, node, tag); + tnfa->tag_directions[tag] = direction; + if (minimal_tag >= 0) + { + for (i = 0; tnfa->minimal_tags[i] >= 0; i++); + tnfa->minimal_tags[i] = tag; + tnfa->minimal_tags[i + 1] = minimal_tag; + tnfa->minimal_tags[i + 2] = -1; + minimal_tag = -1; + num_minimals++; + } + tre_purge_regset(regset, tnfa, tag); + } - if (node->submatch_id >= 0) { - int i; - /* Push this submatch on the parents stack. */ - for (i = 0; parents[i] >= 0; i++) - ; - parents[i] = node->submatch_id; - parents[i + 1] = -1; - } + regset[0] = -1; + tag = next_tag; + num_tags++; + next_tag++; + } - break; /* end case: ADDTAGS_RECURSE */ + if (node->num_submatches > 0) + { + /* The next two tags are reserved for markers. */ + next_tag++; + tag = next_tag; + next_tag++; + } - case ADDTAGS_AFTER_ITERATION: { - int minimal = 0; - int enter_tag; - node = tre_stack_pop_voidptr(stack); - if (first_pass) { - node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags + - tre_stack_pop_int(stack); - minimal_tag = -1; - } else { - minimal = tre_stack_pop_int(stack); - enter_tag = tre_stack_pop_int(stack); - if (minimal) minimal_tag = enter_tag; - } + break; + } + } - if (!first_pass) { - if (minimal) - direction = TRE_TAG_MINIMIZE; - else - direction = TRE_TAG_MAXIMIZE; - } - break; - } + if (node->submatch_id >= 0) + { + int i; + /* Push this submatch on the parents stack. */ + for (i = 0; parents[i] >= 0; i++); + parents[i] = node->submatch_id; + parents[i + 1] = -1; + } - case ADDTAGS_AFTER_CAT_LEFT: { - int new_tag = tre_stack_pop_int(stack); - next_tag = tre_stack_pop_int(stack); - if (new_tag >= 0) { - tag = new_tag; - } - break; - } + break; /* end case: ADDTAGS_RECURSE */ - case ADDTAGS_AFTER_CAT_RIGHT: - node = tre_stack_pop_voidptr(stack); - if (first_pass) - node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags + - ((tre_catenation_t *)node->obj)->right->num_tags; - break; + case ADDTAGS_AFTER_ITERATION: + { + int minimal = 0; + int enter_tag; + node = tre_stack_pop_voidptr(stack); + if (first_pass) + { + node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags + + tre_stack_pop_int(stack); + minimal_tag = -1; + } + else + { + minimal = tre_stack_pop_int(stack); + enter_tag = tre_stack_pop_int(stack); + if (minimal) + minimal_tag = enter_tag; + } - case ADDTAGS_AFTER_UNION_LEFT: - /* Lift the bottom of the `regset' array so that when processing - the right operand the items currently in the array are - invisible. The original bottom was saved at ADDTAGS_UNION and - will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */ - while (*regset >= 0) regset++; - break; + if (!first_pass) + { + if (minimal) + direction = TRE_TAG_MINIMIZE; + else + direction = TRE_TAG_MAXIMIZE; + } + break; + } - case ADDTAGS_AFTER_UNION_RIGHT: { - int added_tags, tag_left, tag_right; - tre_ast_node_t *left = tre_stack_pop_voidptr(stack); - tre_ast_node_t *right = tre_stack_pop_voidptr(stack); - node = tre_stack_pop_voidptr(stack); - added_tags = tre_stack_pop_int(stack); - if (first_pass) { - node->num_tags = ((tre_union_t *)node->obj)->left->num_tags + - ((tre_union_t *)node->obj)->right->num_tags + - added_tags + ((node->num_submatches > 0) ? 2 : 0); - } - regset = tre_stack_pop_voidptr(stack); - tag_left = tre_stack_pop_int(stack); - tag_right = tre_stack_pop_int(stack); + case ADDTAGS_AFTER_CAT_LEFT: + { + int new_tag = tre_stack_pop_int(stack); + next_tag = tre_stack_pop_int(stack); + if (new_tag >= 0) + { + tag = new_tag; + } + break; + } - /* Add tags after both children, the left child gets a smaller - tag than the right child. This guarantees that we prefer - the left child over the right child. */ - /* XXX - This is not always necessary (if the children have - tags which must be seen for every match of that child). */ - /* XXX - Check if this is the only place where tre_add_tag_right - is used. If so, use tre_add_tag_left (putting the tag before - the child as opposed after the child) and throw away - tre_add_tag_right. */ - if (node->num_submatches > 0) { - if (!first_pass) { - status = tre_add_tag_right(mem, left, tag_left); - tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE; - if (status == REG_OK) - status = tre_add_tag_right(mem, right, tag_right); - tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE; - } - num_tags += 2; - } - direction = TRE_TAG_MAXIMIZE; - break; - } + case ADDTAGS_AFTER_CAT_RIGHT: + node = tre_stack_pop_voidptr(stack); + if (first_pass) + node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags + + ((tre_catenation_t *)node->obj)->right->num_tags; + break; - default: - __builtin_unreachable(); + case ADDTAGS_AFTER_UNION_LEFT: + /* Lift the bottom of the `regset' array so that when processing + the right operand the items currently in the array are + invisible. The original bottom was saved at ADDTAGS_UNION and + will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */ + while (*regset >= 0) + regset++; + break; - } /* end switch(symbol) */ - } /* end while(tre_stack_num_objects(stack) > bottom) */ + case ADDTAGS_AFTER_UNION_RIGHT: + { + int added_tags, tag_left, tag_right; + tre_ast_node_t *left = tre_stack_pop_voidptr(stack); + tre_ast_node_t *right = tre_stack_pop_voidptr(stack); + node = tre_stack_pop_voidptr(stack); + added_tags = tre_stack_pop_int(stack); + if (first_pass) + { + node->num_tags = ((tre_union_t *)node->obj)->left->num_tags + + ((tre_union_t *)node->obj)->right->num_tags + added_tags + + ((node->num_submatches > 0) ? 2 : 0); + } + regset = tre_stack_pop_voidptr(stack); + tag_left = tre_stack_pop_int(stack); + tag_right = tre_stack_pop_int(stack); - if (!first_pass) tre_purge_regset(regset, tnfa, tag); + /* Add tags after both children, the left child gets a smaller + tag than the right child. This guarantees that we prefer + the left child over the right child. */ + /* XXX - This is not always necessary (if the children have + tags which must be seen for every match of that child). */ + /* XXX - Check if this is the only place where tre_add_tag_right + is used. If so, use tre_add_tag_left (putting the tag before + the child as opposed after the child) and throw away + tre_add_tag_right. */ + if (node->num_submatches > 0) + { + if (!first_pass) + { + status = tre_add_tag_right(mem, left, tag_left); + tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE; + if (status == REG_OK) + status = tre_add_tag_right(mem, right, tag_right); + tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE; + } + num_tags += 2; + } + direction = TRE_TAG_MAXIMIZE; + break; + } - if (!first_pass && minimal_tag >= 0) { - int i; - for (i = 0; tnfa->minimal_tags[i] >= 0; i++) - ; - tnfa->minimal_tags[i] = tag; - tnfa->minimal_tags[i + 1] = minimal_tag; - tnfa->minimal_tags[i + 2] = -1; - minimal_tag = -1; - num_minimals++; - } + default: + assert(0); + break; - unassert(tree->num_tags == num_tags); + } /* end switch(symbol) */ + } /* end while(tre_stack_num_objects(stack) > bottom) */ + + if (!first_pass) + tre_purge_regset(regset, tnfa, tag); + + if (!first_pass && minimal_tag >= 0) + { + int i; + for (i = 0; tnfa->minimal_tags[i] >= 0; i++); + tnfa->minimal_tags[i] = tag; + tnfa->minimal_tags[i + 1] = minimal_tag; + tnfa->minimal_tags[i + 2] = -1; + minimal_tag = -1; + num_minimals++; + } + + assert(tree->num_tags == num_tags); tnfa->end_tag = num_tags; tnfa->num_tags = num_tags; tnfa->num_minimals = num_minimals; - free(orig_regset), orig_regset = NULL; - free(parents), parents = NULL; - free(saved_states), saved_states = NULL; + xfree(orig_regset); + xfree(parents); + xfree(saved_states); return status; } + + /* AST to TNFA compilation routines. */ -typedef enum { COPY_RECURSE, COPY_SET_RESULT_PTR } tre_copyast_symbol_t; +typedef enum { + COPY_RECURSE, + COPY_SET_RESULT_PTR +} tre_copyast_symbol_t; /* Flags for tre_copy_ast(). */ -#define COPY_REMOVE_TAGS 1 -#define COPY_MAXIMIZE_FIRST_TAG 2 +#define COPY_REMOVE_TAGS 1 +#define COPY_MAXIMIZE_FIRST_TAG 2 -static reg_errcode_t tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, - tre_ast_node_t *ast, int flags, int *pos_add, - tre_tag_direction_t *tag_directions, - tre_ast_node_t **copy, int *max_pos) { +static reg_errcode_t +tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + int flags, int *pos_add, tre_tag_direction_t *tag_directions, + tre_ast_node_t **copy, int *max_pos) +{ reg_errcode_t status = REG_OK; int bottom = tre_stack_num_objects(stack); int num_copied = 0; @@ -1551,121 +1725,143 @@ static reg_errcode_t tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, STACK_PUSH(stack, voidptr, ast); STACK_PUSH(stack, int, COPY_RECURSE); - while (status == REG_OK && tre_stack_num_objects(stack) > bottom) { - tre_ast_node_t *node; - if (status != REG_OK) break; + while (status == REG_OK && tre_stack_num_objects(stack) > bottom) + { + tre_ast_node_t *node; + if (status != REG_OK) + break; - symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack); - switch (symbol) { - case COPY_SET_RESULT_PTR: - result = tre_stack_pop_voidptr(stack); - break; - case COPY_RECURSE: - node = tre_stack_pop_voidptr(stack); - switch (node->type) { - case LITERAL: { - tre_literal_t *lit = node->obj; - int pos = lit->position; - int min = lit->code_min; - int max = lit->code_max; - if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) { - /* XXX - e.g. [ab] has only one position but two - nodes, so we are creating holes in the state space - here. Not fatal, just wastes memory. */ - pos += *pos_add; - num_copied++; - } else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS)) { - /* Change this tag to empty. */ - min = EMPTY; - max = pos = -1; - } else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG) && - first_tag) { - /* Maximize the first tag. */ - tag_directions[max] = TRE_TAG_MAXIMIZE; - first_tag = 0; - } - *result = tre_ast_new_literal(mem, min, max, pos); - if (*result == NULL) - status = REG_ESPACE; - else { - tre_literal_t *p = (*result)->obj; - p->class = lit->class; - p->neg_classes = lit->neg_classes; - } + symbol = (tre_copyast_symbol_t)tre_stack_pop_int(stack); + switch (symbol) + { + case COPY_SET_RESULT_PTR: + result = tre_stack_pop_voidptr(stack); + break; + case COPY_RECURSE: + node = tre_stack_pop_voidptr(stack); + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit = node->obj; + int pos = lit->position; + int min = lit->code_min; + int max = lit->code_max; + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { + /* XXX - e.g. [ab] has only one position but two + nodes, so we are creating holes in the state space + here. Not fatal, just wastes memory. */ + pos += *pos_add; + num_copied++; + } + else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS)) + { + /* Change this tag to empty. */ + min = EMPTY; + max = pos = -1; + } + else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG) + && first_tag) + { + /* Maximize the first tag. */ + tag_directions[max] = TRE_TAG_MAXIMIZE; + first_tag = 0; + } + *result = tre_ast_new_literal(mem, min, max, pos); + if (*result == NULL) + status = REG_ESPACE; + else { + tre_literal_t *p = (*result)->obj; + p->class = lit->class; + p->neg_classes = lit->neg_classes; + } - if (pos > *max_pos) *max_pos = pos; - break; - } - case UNION: { - tre_union_t *uni = node->obj; - tre_union_t *tmp; - *result = tre_ast_new_union(mem, uni->left, uni->right); - if (*result == NULL) { - status = REG_ESPACE; - break; - } - tmp = (*result)->obj; - result = &tmp->left; - STACK_PUSHX(stack, voidptr, uni->right); - STACK_PUSHX(stack, int, COPY_RECURSE); - STACK_PUSHX(stack, voidptr, &tmp->right); - STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR); - STACK_PUSHX(stack, voidptr, uni->left); - STACK_PUSHX(stack, int, COPY_RECURSE); - break; - } - case CATENATION: { - tre_catenation_t *cat = node->obj; - tre_catenation_t *tmp; - *result = tre_ast_new_catenation(mem, cat->left, cat->right); - if (*result == NULL) { - status = REG_ESPACE; - break; - } - tmp = (*result)->obj; - tmp->left = NULL; - tmp->right = NULL; - result = &tmp->left; + if (pos > *max_pos) + *max_pos = pos; + break; + } + case UNION: + { + tre_union_t *uni = node->obj; + tre_union_t *tmp; + *result = tre_ast_new_union(mem, uni->left, uni->right); + if (*result == NULL) + { + status = REG_ESPACE; + break; + } + tmp = (*result)->obj; + result = &tmp->left; + STACK_PUSHX(stack, voidptr, uni->right); + STACK_PUSHX(stack, int, COPY_RECURSE); + STACK_PUSHX(stack, voidptr, &tmp->right); + STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR); + STACK_PUSHX(stack, voidptr, uni->left); + STACK_PUSHX(stack, int, COPY_RECURSE); + break; + } + case CATENATION: + { + tre_catenation_t *cat = node->obj; + tre_catenation_t *tmp; + *result = tre_ast_new_catenation(mem, cat->left, cat->right); + if (*result == NULL) + { + status = REG_ESPACE; + break; + } + tmp = (*result)->obj; + tmp->left = NULL; + tmp->right = NULL; + result = &tmp->left; - STACK_PUSHX(stack, voidptr, cat->right); - STACK_PUSHX(stack, int, COPY_RECURSE); - STACK_PUSHX(stack, voidptr, &tmp->right); - STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR); - STACK_PUSHX(stack, voidptr, cat->left); - STACK_PUSHX(stack, int, COPY_RECURSE); - break; - } - case ITERATION: { - tre_iteration_t *iter = node->obj; - STACK_PUSHX(stack, voidptr, iter->arg); - STACK_PUSHX(stack, int, COPY_RECURSE); - *result = tre_ast_new_iter(mem, iter->arg, iter->min, iter->max, - iter->minimal); - if (*result == NULL) { - status = REG_ESPACE; - break; - } - iter = (*result)->obj; - result = &iter->arg; - break; - } - default: - __builtin_unreachable(); - } - break; + STACK_PUSHX(stack, voidptr, cat->right); + STACK_PUSHX(stack, int, COPY_RECURSE); + STACK_PUSHX(stack, voidptr, &tmp->right); + STACK_PUSHX(stack, int, COPY_SET_RESULT_PTR); + STACK_PUSHX(stack, voidptr, cat->left); + STACK_PUSHX(stack, int, COPY_RECURSE); + break; + } + case ITERATION: + { + tre_iteration_t *iter = node->obj; + STACK_PUSHX(stack, voidptr, iter->arg); + STACK_PUSHX(stack, int, COPY_RECURSE); + *result = tre_ast_new_iter(mem, iter->arg, iter->min, + iter->max, iter->minimal); + if (*result == NULL) + { + status = REG_ESPACE; + break; + } + iter = (*result)->obj; + result = &iter->arg; + break; + } + default: + assert(0); + break; + } + break; + } } - } *pos_add += num_copied; return status; } -typedef enum { EXPAND_RECURSE, EXPAND_AFTER_ITER } tre_expand_ast_symbol_t; +typedef enum { + EXPAND_RECURSE, + EXPAND_AFTER_ITER +} tre_expand_ast_symbol_t; /* Expands each iteration node that has a finite nonzero minimum or maximum iteration count to a catenated sequence of copies of the node. */ -static reg_errcode_t tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, - tre_ast_node_t *ast, int *position, - tre_tag_direction_t *tag_directions) { +static reg_errcode_t +tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + int *position, tre_tag_direction_t *tag_directions) +{ reg_errcode_t status = REG_OK; int bottom = tre_stack_num_objects(stack); int pos_add = 0; @@ -1675,133 +1871,165 @@ static reg_errcode_t tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, STACK_PUSHR(stack, voidptr, ast); STACK_PUSHR(stack, int, EXPAND_RECURSE); - while (status == REG_OK && tre_stack_num_objects(stack) > bottom) { - tre_ast_node_t *node; - tre_expand_ast_symbol_t symbol; + while (status == REG_OK && tre_stack_num_objects(stack) > bottom) + { + tre_ast_node_t *node; + tre_expand_ast_symbol_t symbol; - if (status != REG_OK) break; + if (status != REG_OK) + break; - symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack); - node = tre_stack_pop_voidptr(stack); - switch (symbol) { - case EXPAND_RECURSE: - switch (node->type) { - case LITERAL: { - tre_literal_t *lit = node->obj; - if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) { - lit->position += pos_add; - if (lit->position > max_pos) max_pos = lit->position; - } - break; - } - case UNION: { - tre_union_t *uni = node->obj; - STACK_PUSHX(stack, voidptr, uni->right); - STACK_PUSHX(stack, int, EXPAND_RECURSE); - STACK_PUSHX(stack, voidptr, uni->left); - STACK_PUSHX(stack, int, EXPAND_RECURSE); - break; - } - case CATENATION: { - tre_catenation_t *cat = node->obj; - STACK_PUSHX(stack, voidptr, cat->right); - STACK_PUSHX(stack, int, EXPAND_RECURSE); - STACK_PUSHX(stack, voidptr, cat->left); - STACK_PUSHX(stack, int, EXPAND_RECURSE); - break; - } - case ITERATION: { - tre_iteration_t *iter = node->obj; - STACK_PUSHX(stack, int, pos_add); - STACK_PUSHX(stack, voidptr, node); - STACK_PUSHX(stack, int, EXPAND_AFTER_ITER); - STACK_PUSHX(stack, voidptr, iter->arg); - STACK_PUSHX(stack, int, EXPAND_RECURSE); - /* If we are going to expand this node at EXPAND_AFTER_ITER - then don't increase the `pos' fields of the nodes now, it - will get done when expanding. */ - if (iter->min > 1 || iter->max > 1) pos_add = 0; - iter_depth++; - break; - } - default: - __builtin_unreachable(); - } - break; - case EXPAND_AFTER_ITER: { - tre_iteration_t *iter = node->obj; - int pos_add_last; - pos_add = tre_stack_pop_int(stack); - pos_add_last = pos_add; - if (iter->min > 1 || iter->max > 1) { - tre_ast_node_t *seq1 = NULL, *seq2 = NULL; - int j; - int pos_add_save = pos_add; + symbol = (tre_expand_ast_symbol_t)tre_stack_pop_int(stack); + node = tre_stack_pop_voidptr(stack); + switch (symbol) + { + case EXPAND_RECURSE: + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit= node->obj; + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { + lit->position += pos_add; + if (lit->position > max_pos) + max_pos = lit->position; + } + break; + } + case UNION: + { + tre_union_t *uni = node->obj; + STACK_PUSHX(stack, voidptr, uni->right); + STACK_PUSHX(stack, int, EXPAND_RECURSE); + STACK_PUSHX(stack, voidptr, uni->left); + STACK_PUSHX(stack, int, EXPAND_RECURSE); + break; + } + case CATENATION: + { + tre_catenation_t *cat = node->obj; + STACK_PUSHX(stack, voidptr, cat->right); + STACK_PUSHX(stack, int, EXPAND_RECURSE); + STACK_PUSHX(stack, voidptr, cat->left); + STACK_PUSHX(stack, int, EXPAND_RECURSE); + break; + } + case ITERATION: + { + tre_iteration_t *iter = node->obj; + STACK_PUSHX(stack, int, pos_add); + STACK_PUSHX(stack, voidptr, node); + STACK_PUSHX(stack, int, EXPAND_AFTER_ITER); + STACK_PUSHX(stack, voidptr, iter->arg); + STACK_PUSHX(stack, int, EXPAND_RECURSE); + /* If we are going to expand this node at EXPAND_AFTER_ITER + then don't increase the `pos' fields of the nodes now, it + will get done when expanding. */ + if (iter->min > 1 || iter->max > 1) + pos_add = 0; + iter_depth++; + break; + } + default: + assert(0); + break; + } + break; + case EXPAND_AFTER_ITER: + { + tre_iteration_t *iter = node->obj; + int pos_add_last; + pos_add = tre_stack_pop_int(stack); + pos_add_last = pos_add; + if (iter->min > 1 || iter->max > 1) + { + tre_ast_node_t *seq1 = NULL, *seq2 = NULL; + int j; + int pos_add_save = pos_add; - /* Create a catenated sequence of copies of the node. */ - for (j = 0; j < iter->min; j++) { - tre_ast_node_t *copy; - /* Remove tags from all but the last copy. */ - int flags = ((j + 1 < iter->min) ? COPY_REMOVE_TAGS - : COPY_MAXIMIZE_FIRST_TAG); - pos_add_save = pos_add; - status = tre_copy_ast(mem, stack, iter->arg, flags, &pos_add, - tag_directions, ©, &max_pos); - if (status != REG_OK) return status; - if (seq1 != NULL) - seq1 = tre_ast_new_catenation(mem, seq1, copy); - else - seq1 = copy; - if (seq1 == NULL) return REG_ESPACE; - } + /* Create a catenated sequence of copies of the node. */ + for (j = 0; j < iter->min; j++) + { + tre_ast_node_t *copy; + /* Remove tags from all but the last copy. */ + int flags = ((j + 1 < iter->min) + ? COPY_REMOVE_TAGS + : COPY_MAXIMIZE_FIRST_TAG); + pos_add_save = pos_add; + status = tre_copy_ast(mem, stack, iter->arg, flags, + &pos_add, tag_directions, ©, + &max_pos); + if (status != REG_OK) + return status; + if (seq1 != NULL) + seq1 = tre_ast_new_catenation(mem, seq1, copy); + else + seq1 = copy; + if (seq1 == NULL) + return REG_ESPACE; + } - if (iter->max == -1) { - /* No upper limit. */ - pos_add_save = pos_add; - status = tre_copy_ast(mem, stack, iter->arg, 0, &pos_add, NULL, - &seq2, &max_pos); - if (status != REG_OK) return status; - seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0); - if (seq2 == NULL) return REG_ESPACE; - } else { - for (j = iter->min; j < iter->max; j++) { - tre_ast_node_t *tmp, *copy; - pos_add_save = pos_add; - status = tre_copy_ast(mem, stack, iter->arg, 0, &pos_add, NULL, - ©, &max_pos); - if (status != REG_OK) return status; - if (seq2 != NULL) - seq2 = tre_ast_new_catenation(mem, copy, seq2); - else - seq2 = copy; - if (seq2 == NULL) return REG_ESPACE; - tmp = tre_ast_new_literal(mem, EMPTY, -1, -1); - if (tmp == NULL) return REG_ESPACE; - seq2 = tre_ast_new_union(mem, tmp, seq2); - if (seq2 == NULL) return REG_ESPACE; - } - } + if (iter->max == -1) + { + /* No upper limit. */ + pos_add_save = pos_add; + status = tre_copy_ast(mem, stack, iter->arg, 0, + &pos_add, NULL, &seq2, &max_pos); + if (status != REG_OK) + return status; + seq2 = tre_ast_new_iter(mem, seq2, 0, -1, 0); + if (seq2 == NULL) + return REG_ESPACE; + } + else + { + for (j = iter->min; j < iter->max; j++) + { + tre_ast_node_t *tmp, *copy; + pos_add_save = pos_add; + status = tre_copy_ast(mem, stack, iter->arg, 0, + &pos_add, NULL, ©, &max_pos); + if (status != REG_OK) + return status; + if (seq2 != NULL) + seq2 = tre_ast_new_catenation(mem, copy, seq2); + else + seq2 = copy; + if (seq2 == NULL) + return REG_ESPACE; + tmp = tre_ast_new_literal(mem, EMPTY, -1, -1); + if (tmp == NULL) + return REG_ESPACE; + seq2 = tre_ast_new_union(mem, tmp, seq2); + if (seq2 == NULL) + return REG_ESPACE; + } + } - pos_add = pos_add_save; - if (seq1 == NULL) - seq1 = seq2; - else if (seq2 != NULL) - seq1 = tre_ast_new_catenation(mem, seq1, seq2); - if (seq1 == NULL) return REG_ESPACE; - node->obj = seq1->obj; - node->type = seq1->type; - } + pos_add = pos_add_save; + if (seq1 == NULL) + seq1 = seq2; + else if (seq2 != NULL) + seq1 = tre_ast_new_catenation(mem, seq1, seq2); + if (seq1 == NULL) + return REG_ESPACE; + node->obj = seq1->obj; + node->type = seq1->type; + } - iter_depth--; - pos_add_total += pos_add - pos_add_last; - if (iter_depth == 0) pos_add = pos_add_total; + iter_depth--; + pos_add_total += pos_add - pos_add_last; + if (iter_depth == 0) + pos_add = pos_add_total; - break; - } - default: - __builtin_unreachable(); + break; + } + default: + assert(0); + break; + } } - } *position += pos_add_total; @@ -1809,16 +2037,20 @@ static reg_errcode_t tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, code works, but just an extra safeguard let's make sure `*position' is set large enough so enough memory will be allocated for the transition table. */ - if (max_pos > *position) *position = max_pos; + if (max_pos > *position) + *position = max_pos; return status; } -static tre_pos_and_tags_t *tre_set_empty(tre_mem_t mem) { +static tre_pos_and_tags_t * +tre_set_empty(tre_mem_t mem) +{ tre_pos_and_tags_t *new_set; new_set = tre_mem_calloc(mem, sizeof(*new_set)); - if (new_set == NULL) return NULL; + if (new_set == NULL) + return NULL; new_set[0].position = -1; new_set[0].code_min = -1; @@ -1827,14 +2059,15 @@ static tre_pos_and_tags_t *tre_set_empty(tre_mem_t mem) { return new_set; } -static tre_pos_and_tags_t *tre_set_one(tre_mem_t mem, int position, - int code_min, int code_max, - tre_ctype_t class, - tre_ctype_t *neg_classes, int backref) { +static tre_pos_and_tags_t * +tre_set_one(tre_mem_t mem, int position, int code_min, int code_max, + tre_ctype_t class, tre_ctype_t *neg_classes, int backref) +{ tre_pos_and_tags_t *new_set; new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2); - if (new_set == NULL) return NULL; + if (new_set == NULL) + return NULL; new_set[0].position = position; new_set[0].code_min = code_min; @@ -1849,67 +2082,73 @@ static tre_pos_and_tags_t *tre_set_one(tre_mem_t mem, int position, return new_set; } -static tre_pos_and_tags_t *tre_set_union(tre_mem_t mem, - tre_pos_and_tags_t *set1, - tre_pos_and_tags_t *set2, int *tags, - int assertions) { +static tre_pos_and_tags_t * +tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2, + int *tags, int assertions) +{ int s1, s2, i, j; tre_pos_and_tags_t *new_set; int *new_tags; int num_tags; - for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++) - ; - for (s1 = 0; set1[s1].position >= 0; s1++) - ; - for (s2 = 0; set2[s2].position >= 0; s2++) - ; + for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++); + for (s1 = 0; set1[s1].position >= 0; s1++); + for (s2 = 0; set2[s2].position >= 0; s2++); new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1)); - if (!new_set) return NULL; + if (!new_set ) + return NULL; - for (s1 = 0; set1[s1].position >= 0; s1++) { - new_set[s1].position = set1[s1].position; - new_set[s1].code_min = set1[s1].code_min; - new_set[s1].code_max = set1[s1].code_max; - new_set[s1].assertions = set1[s1].assertions | assertions; - new_set[s1].class = set1[s1].class; - new_set[s1].neg_classes = set1[s1].neg_classes; - new_set[s1].backref = set1[s1].backref; - if (set1[s1].tags == NULL && tags == NULL) - new_set[s1].tags = NULL; - else { - for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++) - ; - new_tags = tre_mem_alloc(mem, (sizeof(*new_tags) * (i + num_tags + 1))); - if (new_tags == NULL) return NULL; - for (j = 0; j < i; j++) new_tags[j] = set1[s1].tags[j]; - for (i = 0; i < num_tags; i++) new_tags[j + i] = tags[i]; - new_tags[j + i] = -1; - new_set[s1].tags = new_tags; + for (s1 = 0; set1[s1].position >= 0; s1++) + { + new_set[s1].position = set1[s1].position; + new_set[s1].code_min = set1[s1].code_min; + new_set[s1].code_max = set1[s1].code_max; + new_set[s1].assertions = set1[s1].assertions | assertions; + new_set[s1].class = set1[s1].class; + new_set[s1].neg_classes = set1[s1].neg_classes; + new_set[s1].backref = set1[s1].backref; + if (set1[s1].tags == NULL && tags == NULL) + new_set[s1].tags = NULL; + else + { + for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++); + new_tags = tre_mem_alloc(mem, (sizeof(*new_tags) + * (i + num_tags + 1))); + if (new_tags == NULL) + return NULL; + for (j = 0; j < i; j++) + new_tags[j] = set1[s1].tags[j]; + for (i = 0; i < num_tags; i++) + new_tags[j + i] = tags[i]; + new_tags[j + i] = -1; + new_set[s1].tags = new_tags; + } } - } - for (s2 = 0; set2[s2].position >= 0; s2++) { - new_set[s1 + s2].position = set2[s2].position; - new_set[s1 + s2].code_min = set2[s2].code_min; - new_set[s1 + s2].code_max = set2[s2].code_max; - /* XXX - why not | assertions here as well? */ - new_set[s1 + s2].assertions = set2[s2].assertions; - new_set[s1 + s2].class = set2[s2].class; - new_set[s1 + s2].neg_classes = set2[s2].neg_classes; - new_set[s1 + s2].backref = set2[s2].backref; - if (set2[s2].tags == NULL) - new_set[s1 + s2].tags = NULL; - else { - for (i = 0; set2[s2].tags[i] >= 0; i++) - ; - new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1)); - if (new_tags == NULL) return NULL; - for (j = 0; j < i; j++) new_tags[j] = set2[s2].tags[j]; - new_tags[j] = -1; - new_set[s1 + s2].tags = new_tags; + for (s2 = 0; set2[s2].position >= 0; s2++) + { + new_set[s1 + s2].position = set2[s2].position; + new_set[s1 + s2].code_min = set2[s2].code_min; + new_set[s1 + s2].code_max = set2[s2].code_max; + /* XXX - why not | assertions here as well? */ + new_set[s1 + s2].assertions = set2[s2].assertions; + new_set[s1 + s2].class = set2[s2].class; + new_set[s1 + s2].neg_classes = set2[s2].neg_classes; + new_set[s1 + s2].backref = set2[s2].backref; + if (set2[s2].tags == NULL) + new_set[s1 + s2].tags = NULL; + else + { + for (i = 0; set2[s2].tags[i] >= 0; i++); + new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1)); + if (new_tags == NULL) + return NULL; + for (j = 0; j < i; j++) + new_tags[j] = set2[s2].tags[j]; + new_tags[j] = -1; + new_set[s1 + s2].tags = new_tags; + } } - } new_set[s1 + s2].position = -1; return new_set; } @@ -1918,9 +2157,10 @@ static tre_pos_and_tags_t *tre_set_union(tre_mem_t mem, taken according to POSIX.2 rules, and adds the tags on that path to `tags'. `tags' may be NULL. If `num_tags_seen' is not NULL, it is set to the number of tags seen on the path. */ -static reg_errcode_t tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, - int *tags, int *assertions, - int *num_tags_seen) { +static reg_errcode_t +tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags, + int *assertions, int *num_tags_seen) +{ tre_literal_t *lit; tre_union_t *uni; tre_catenation_t *cat; @@ -1928,80 +2168,95 @@ static reg_errcode_t tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int i; int bottom = tre_stack_num_objects(stack); reg_errcode_t status = REG_OK; - if (num_tags_seen) *num_tags_seen = 0; + if (num_tags_seen) + *num_tags_seen = 0; status = tre_stack_push_voidptr(stack, node); /* Walk through the tree recursively. */ - while (status == REG_OK && tre_stack_num_objects(stack) > bottom) { - node = tre_stack_pop_voidptr(stack); + while (status == REG_OK && tre_stack_num_objects(stack) > bottom) + { + node = tre_stack_pop_voidptr(stack); - switch (node->type) { - case LITERAL: - lit = (tre_literal_t *)node->obj; - switch (lit->code_min) { - case TAG: - if (lit->code_max >= 0) { - if (tags != NULL) { - /* Add the tag to `tags'. */ - for (i = 0; tags[i] >= 0; i++) - if (tags[i] == lit->code_max) break; - if (tags[i] < 0) { - tags[i] = lit->code_max; - tags[i + 1] = -1; - } - } - if (num_tags_seen) (*num_tags_seen)++; - } - break; - case ASSERTION: - unassert(lit->code_max >= 1 || lit->code_max <= ASSERT_LAST); - if (assertions != NULL) *assertions |= lit->code_max; - break; - case EMPTY: - break; - default: - __builtin_unreachable(); - } - break; + switch (node->type) + { + case LITERAL: + lit = (tre_literal_t *)node->obj; + switch (lit->code_min) + { + case TAG: + if (lit->code_max >= 0) + { + if (tags != NULL) + { + /* Add the tag to `tags'. */ + for (i = 0; tags[i] >= 0; i++) + if (tags[i] == lit->code_max) + break; + if (tags[i] < 0) + { + tags[i] = lit->code_max; + tags[i + 1] = -1; + } + } + if (num_tags_seen) + (*num_tags_seen)++; + } + break; + case ASSERTION: + assert(lit->code_max >= 1 + || lit->code_max <= ASSERT_LAST); + if (assertions != NULL) + *assertions |= lit->code_max; + break; + case EMPTY: + break; + default: + assert(0); + break; + } + break; - case UNION: - /* Subexpressions starting earlier take priority over ones - starting later, so we prefer the left subexpression over the - right subexpression. */ - uni = (tre_union_t *)node->obj; - if (uni->left->nullable) - STACK_PUSHX(stack, voidptr, uni->left) - else if (uni->right->nullable) - STACK_PUSHX(stack, voidptr, uni->right) - else - __builtin_unreachable(); - break; + case UNION: + /* Subexpressions starting earlier take priority over ones + starting later, so we prefer the left subexpression over the + right subexpression. */ + uni = (tre_union_t *)node->obj; + if (uni->left->nullable) + STACK_PUSHX(stack, voidptr, uni->left) + else if (uni->right->nullable) + STACK_PUSHX(stack, voidptr, uni->right) + else + assert(0); + break; - case CATENATION: - /* The path must go through both children. */ - cat = (tre_catenation_t *)node->obj; - unassert(cat->left->nullable); - unassert(cat->right->nullable); - STACK_PUSHX(stack, voidptr, cat->left); - STACK_PUSHX(stack, voidptr, cat->right); - break; + case CATENATION: + /* The path must go through both children. */ + cat = (tre_catenation_t *)node->obj; + assert(cat->left->nullable); + assert(cat->right->nullable); + STACK_PUSHX(stack, voidptr, cat->left); + STACK_PUSHX(stack, voidptr, cat->right); + break; - case ITERATION: - /* A match with an empty string is preferred over no match at - all, so we go through the argument if possible. */ - iter = (tre_iteration_t *)node->obj; - if (iter->arg->nullable) STACK_PUSHX(stack, voidptr, iter->arg); - break; + case ITERATION: + /* A match with an empty string is preferred over no match at + all, so we go through the argument if possible. */ + iter = (tre_iteration_t *)node->obj; + if (iter->arg->nullable) + STACK_PUSHX(stack, voidptr, iter->arg); + break; - default: - __builtin_unreachable(); + default: + assert(0); + break; + } } - } return status; } + typedef enum { NFL_RECURSE, NFL_POST_UNION, @@ -2009,211 +2264,263 @@ typedef enum { NFL_POST_ITERATION } tre_nfl_stack_symbol_t; + /* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for the nodes of the AST `tree'. */ -static reg_errcode_t tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, - tre_ast_node_t *tree) { +static reg_errcode_t +tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree) +{ int bottom = tre_stack_num_objects(stack); STACK_PUSHR(stack, voidptr, tree); STACK_PUSHR(stack, int, NFL_RECURSE); - while (tre_stack_num_objects(stack) > bottom) { - tre_nfl_stack_symbol_t symbol; - tre_ast_node_t *node; + while (tre_stack_num_objects(stack) > bottom) + { + tre_nfl_stack_symbol_t symbol; + tre_ast_node_t *node; - symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack); - node = tre_stack_pop_voidptr(stack); - switch (symbol) { - case NFL_RECURSE: - switch (node->type) { - case LITERAL: { - tre_literal_t *lit = (tre_literal_t *)node->obj; - if (IS_BACKREF(lit)) { - /* Back references: nullable = false, firstpos = {i}, - lastpos = {i}. */ - node->nullable = 0; - node->firstpos = - tre_set_one(mem, lit->position, 0, TRE_CHAR_MAX, 0, NULL, -1); - if (!node->firstpos) return REG_ESPACE; - node->lastpos = tre_set_one(mem, lit->position, 0, TRE_CHAR_MAX, - 0, NULL, (int)lit->code_max); - if (!node->lastpos) return REG_ESPACE; - } else if (lit->code_min < 0) { - /* Tags, empty strings, params, and zero width assertions: - nullable = true, firstpos = {}, and lastpos = {}. */ - node->nullable = 1; - node->firstpos = tre_set_empty(mem); - if (!node->firstpos) return REG_ESPACE; - node->lastpos = tre_set_empty(mem); - if (!node->lastpos) return REG_ESPACE; - } else { - /* Literal at position i: nullable = false, firstpos = {i}, - lastpos = {i}. */ - node->nullable = 0; - node->firstpos = - tre_set_one(mem, lit->position, (int)lit->code_min, - (int)lit->code_max, 0, NULL, -1); - if (!node->firstpos) return REG_ESPACE; - node->lastpos = tre_set_one( - mem, lit->position, (int)lit->code_min, (int)lit->code_max, - lit->class, lit->neg_classes, -1); - if (!node->lastpos) return REG_ESPACE; - } - break; - } + symbol = (tre_nfl_stack_symbol_t)tre_stack_pop_int(stack); + node = tre_stack_pop_voidptr(stack); + switch (symbol) + { + case NFL_RECURSE: + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit = (tre_literal_t *)node->obj; + if (IS_BACKREF(lit)) + { + /* Back references: nullable = false, firstpos = {i}, + lastpos = {i}. */ + node->nullable = 0; + node->firstpos = tre_set_one(mem, lit->position, 0, + TRE_CHAR_MAX, 0, NULL, -1); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_one(mem, lit->position, 0, + TRE_CHAR_MAX, 0, NULL, + (int)lit->code_max); + if (!node->lastpos) + return REG_ESPACE; + } + else if (lit->code_min < 0) + { + /* Tags, empty strings, params, and zero width assertions: + nullable = true, firstpos = {}, and lastpos = {}. */ + node->nullable = 1; + node->firstpos = tre_set_empty(mem); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_empty(mem); + if (!node->lastpos) + return REG_ESPACE; + } + else + { + /* Literal at position i: nullable = false, firstpos = {i}, + lastpos = {i}. */ + node->nullable = 0; + node->firstpos = + tre_set_one(mem, lit->position, (int)lit->code_min, + (int)lit->code_max, 0, NULL, -1); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_one(mem, lit->position, + (int)lit->code_min, + (int)lit->code_max, + lit->class, lit->neg_classes, + -1); + if (!node->lastpos) + return REG_ESPACE; + } + break; + } - case UNION: - /* Compute the attributes for the two subtrees, and after that - for this node. */ - STACK_PUSHR(stack, voidptr, node); - STACK_PUSHR(stack, int, NFL_POST_UNION); - STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right); - STACK_PUSHR(stack, int, NFL_RECURSE); - STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left); - STACK_PUSHR(stack, int, NFL_RECURSE); - break; + case UNION: + /* Compute the attributes for the two subtrees, and after that + for this node. */ + STACK_PUSHR(stack, voidptr, node); + STACK_PUSHR(stack, int, NFL_POST_UNION); + STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->right); + STACK_PUSHR(stack, int, NFL_RECURSE); + STACK_PUSHR(stack, voidptr, ((tre_union_t *)node->obj)->left); + STACK_PUSHR(stack, int, NFL_RECURSE); + break; - case CATENATION: - /* Compute the attributes for the two subtrees, and after that - for this node. */ - STACK_PUSHR(stack, voidptr, node); - STACK_PUSHR(stack, int, NFL_POST_CATENATION); - STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right); - STACK_PUSHR(stack, int, NFL_RECURSE); - STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left); - STACK_PUSHR(stack, int, NFL_RECURSE); - break; + case CATENATION: + /* Compute the attributes for the two subtrees, and after that + for this node. */ + STACK_PUSHR(stack, voidptr, node); + STACK_PUSHR(stack, int, NFL_POST_CATENATION); + STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->right); + STACK_PUSHR(stack, int, NFL_RECURSE); + STACK_PUSHR(stack, voidptr, ((tre_catenation_t *)node->obj)->left); + STACK_PUSHR(stack, int, NFL_RECURSE); + break; - case ITERATION: - /* Compute the attributes for the subtree, and after that for - this node. */ - STACK_PUSHR(stack, voidptr, node); - STACK_PUSHR(stack, int, NFL_POST_ITERATION); - STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg); - STACK_PUSHR(stack, int, NFL_RECURSE); - break; - } - break; /* end case: NFL_RECURSE */ + case ITERATION: + /* Compute the attributes for the subtree, and after that for + this node. */ + STACK_PUSHR(stack, voidptr, node); + STACK_PUSHR(stack, int, NFL_POST_ITERATION); + STACK_PUSHR(stack, voidptr, ((tre_iteration_t *)node->obj)->arg); + STACK_PUSHR(stack, int, NFL_RECURSE); + break; + } + break; /* end case: NFL_RECURSE */ - case NFL_POST_UNION: { - tre_union_t *uni = (tre_union_t *)node->obj; - node->nullable = uni->left->nullable || uni->right->nullable; - node->firstpos = tre_set_union(mem, uni->left->firstpos, - uni->right->firstpos, NULL, 0); - if (!node->firstpos) return REG_ESPACE; - node->lastpos = tre_set_union(mem, uni->left->lastpos, - uni->right->lastpos, NULL, 0); - if (!node->lastpos) return REG_ESPACE; - break; - } + case NFL_POST_UNION: + { + tre_union_t *uni = (tre_union_t *)node->obj; + node->nullable = uni->left->nullable || uni->right->nullable; + node->firstpos = tre_set_union(mem, uni->left->firstpos, + uni->right->firstpos, NULL, 0); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_union(mem, uni->left->lastpos, + uni->right->lastpos, NULL, 0); + if (!node->lastpos) + return REG_ESPACE; + break; + } - case NFL_POST_ITERATION: { - tre_iteration_t *iter = (tre_iteration_t *)node->obj; + case NFL_POST_ITERATION: + { + tre_iteration_t *iter = (tre_iteration_t *)node->obj; - if (iter->min == 0 || iter->arg->nullable) - node->nullable = 1; - else - node->nullable = 0; - node->firstpos = iter->arg->firstpos; - node->lastpos = iter->arg->lastpos; - break; - } + if (iter->min == 0 || iter->arg->nullable) + node->nullable = 1; + else + node->nullable = 0; + node->firstpos = iter->arg->firstpos; + node->lastpos = iter->arg->lastpos; + break; + } - case NFL_POST_CATENATION: { - int num_tags, *tags, assertions; - reg_errcode_t status; - tre_catenation_t *cat = node->obj; - node->nullable = cat->left->nullable && cat->right->nullable; + case NFL_POST_CATENATION: + { + int num_tags, *tags, assertions; + reg_errcode_t status; + tre_catenation_t *cat = node->obj; + node->nullable = cat->left->nullable && cat->right->nullable; - /* Compute firstpos. */ - if (cat->left->nullable) { - /* The left side matches the empty string. Make a first pass - with tre_match_empty() to get the number of tags and - parameters. */ - status = tre_match_empty(stack, cat->left, NULL, NULL, &num_tags); - if (status != REG_OK) return status; - /* Allocate arrays for the tags and parameters. */ - tags = malloc(sizeof(*tags) * (num_tags + 1)); - if (!tags) return REG_ESPACE; - tags[0] = -1; - assertions = 0; - /* Second pass with tre_mach_empty() to get the list of - tags and parameters. */ - status = tre_match_empty(stack, cat->left, tags, &assertions, NULL); - if (status != REG_OK) { - free(tags), tags = NULL; - return status; - } - node->firstpos = tre_set_union(mem, cat->right->firstpos, - cat->left->firstpos, tags, assertions); - free(tags), tags = NULL; - if (!node->firstpos) return REG_ESPACE; - } else { - node->firstpos = cat->left->firstpos; - } + /* Compute firstpos. */ + if (cat->left->nullable) + { + /* The left side matches the empty string. Make a first pass + with tre_match_empty() to get the number of tags and + parameters. */ + status = tre_match_empty(stack, cat->left, + NULL, NULL, &num_tags); + if (status != REG_OK) + return status; + /* Allocate arrays for the tags and parameters. */ + tags = xmalloc(sizeof(*tags) * (num_tags + 1)); + if (!tags) + return REG_ESPACE; + tags[0] = -1; + assertions = 0; + /* Second pass with tre_mach_empty() to get the list of + tags and parameters. */ + status = tre_match_empty(stack, cat->left, tags, + &assertions, NULL); + if (status != REG_OK) + { + xfree(tags); + return status; + } + node->firstpos = + tre_set_union(mem, cat->right->firstpos, cat->left->firstpos, + tags, assertions); + xfree(tags); + if (!node->firstpos) + return REG_ESPACE; + } + else + { + node->firstpos = cat->left->firstpos; + } - /* Compute lastpos. */ - if (cat->right->nullable) { - /* The right side matches the empty string. Make a first pass - with tre_match_empty() to get the number of tags and - parameters. */ - status = tre_match_empty(stack, cat->right, NULL, NULL, &num_tags); - if (status != REG_OK) return status; - /* Allocate arrays for the tags and parameters. */ - tags = malloc(sizeof(int) * (num_tags + 1)); - if (!tags) return REG_ESPACE; - tags[0] = -1; - assertions = 0; - /* Second pass with tre_mach_empty() to get the list of - tags and parameters. */ - status = tre_match_empty(stack, cat->right, tags, &assertions, NULL); - if (status != REG_OK) { - free(tags), tags = NULL; - return status; - } - node->lastpos = tre_set_union(mem, cat->left->lastpos, - cat->right->lastpos, tags, assertions); - free(tags), tags = NULL; - if (!node->lastpos) return REG_ESPACE; - } else { - node->lastpos = cat->right->lastpos; - } - break; - } + /* Compute lastpos. */ + if (cat->right->nullable) + { + /* The right side matches the empty string. Make a first pass + with tre_match_empty() to get the number of tags and + parameters. */ + status = tre_match_empty(stack, cat->right, + NULL, NULL, &num_tags); + if (status != REG_OK) + return status; + /* Allocate arrays for the tags and parameters. */ + tags = xmalloc(sizeof(int) * (num_tags + 1)); + if (!tags) + return REG_ESPACE; + tags[0] = -1; + assertions = 0; + /* Second pass with tre_mach_empty() to get the list of + tags and parameters. */ + status = tre_match_empty(stack, cat->right, tags, + &assertions, NULL); + if (status != REG_OK) + { + xfree(tags); + return status; + } + node->lastpos = + tre_set_union(mem, cat->left->lastpos, cat->right->lastpos, + tags, assertions); + xfree(tags); + if (!node->lastpos) + return REG_ESPACE; + } + else + { + node->lastpos = cat->right->lastpos; + } + break; + } - default: - __builtin_unreachable(); + default: + assert(0); + break; + } } - } return REG_OK; } + /* Adds a transition from each position in `p1' to each position in `p2'. */ -static reg_errcode_t tre_make_trans(tre_pos_and_tags_t *p1, - tre_pos_and_tags_t *p2, - tre_tnfa_transition_t *transitions, - int *counts, int *offs) { +static reg_errcode_t +tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2, + tre_tnfa_transition_t *transitions, + int *counts, int *offs) +{ tre_pos_and_tags_t *orig_p2 = p2; tre_tnfa_transition_t *trans; int i, j, k, l, dup, prev_p2_pos; if (transitions != NULL) - while (p1->position >= 0) { - p2 = orig_p2; - prev_p2_pos = -1; - while (p2->position >= 0) { - /* Optimization: if this position was already handled, skip it. */ - if (p2->position == prev_p2_pos) { - p2++; - continue; - } - prev_p2_pos = p2->position; - /* Set `trans' to point to the next unused transition from - position `p1->position'. */ - trans = transitions + offs[p1->position]; - while (trans->state != NULL) { + while (p1->position >= 0) + { + p2 = orig_p2; + prev_p2_pos = -1; + while (p2->position >= 0) + { + /* Optimization: if this position was already handled, skip it. */ + if (p2->position == prev_p2_pos) + { + p2++; + continue; + } + prev_p2_pos = p2->position; + /* Set `trans' to point to the next unused transition from + position `p1->position'. */ + trans = transitions + offs[p1->position]; + while (trans->state != NULL) + { #if 0 /* If we find a previous transition from `p1->position' to `p2->position', it is overwritten. This can happen only @@ -2231,92 +2538,108 @@ static reg_errcode_t tre_make_trans(tre_pos_and_tags_t *p1, break; } #endif - trans++; - } + trans++; + } - if (trans->state == NULL) (trans + 1)->state = NULL; - /* Use the character ranges, assertions, etc. from `p1' for - the transition from `p1' to `p2'. */ - trans->code_min = p1->code_min; - trans->code_max = p1->code_max; - trans->state = transitions + offs[p2->position]; - trans->state_id = p2->position; - trans->assertions = - p1->assertions | p2->assertions | - (p1->class ? ASSERT_CHAR_CLASS : 0) | - (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0); - if (p1->backref >= 0) { - unassert((trans->assertions & ASSERT_CHAR_CLASS) == 0); - unassert(p2->backref < 0); - trans->u.backref = p1->backref; - trans->assertions |= ASSERT_BACKREF; - } else - trans->u.class = p1->class; - if (p1->neg_classes != NULL) { - for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++) - ; - trans->neg_classes = malloc(sizeof(*trans->neg_classes) * (i + 1)); - if (trans->neg_classes == NULL) return REG_ESPACE; - for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++) - trans->neg_classes[i] = p1->neg_classes[i]; - trans->neg_classes[i] = (tre_ctype_t)0; - } else - trans->neg_classes = NULL; + if (trans->state == NULL) + (trans + 1)->state = NULL; + /* Use the character ranges, assertions, etc. from `p1' for + the transition from `p1' to `p2'. */ + trans->code_min = p1->code_min; + trans->code_max = p1->code_max; + trans->state = transitions + offs[p2->position]; + trans->state_id = p2->position; + trans->assertions = p1->assertions | p2->assertions + | (p1->class ? ASSERT_CHAR_CLASS : 0) + | (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0); + if (p1->backref >= 0) + { + assert((trans->assertions & ASSERT_CHAR_CLASS) == 0); + assert(p2->backref < 0); + trans->u.backref = p1->backref; + trans->assertions |= ASSERT_BACKREF; + } + else + trans->u.class = p1->class; + if (p1->neg_classes != NULL) + { + for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++); + trans->neg_classes = + xmalloc(sizeof(*trans->neg_classes) * (i + 1)); + if (trans->neg_classes == NULL) + return REG_ESPACE; + for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++) + trans->neg_classes[i] = p1->neg_classes[i]; + trans->neg_classes[i] = (tre_ctype_t)0; + } + else + trans->neg_classes = NULL; - /* Find out how many tags this transition has. */ - i = 0; - if (p1->tags != NULL) - while (p1->tags[i] >= 0) i++; - j = 0; - if (p2->tags != NULL) - while (p2->tags[j] >= 0) j++; + /* Find out how many tags this transition has. */ + i = 0; + if (p1->tags != NULL) + while(p1->tags[i] >= 0) + i++; + j = 0; + if (p2->tags != NULL) + while(p2->tags[j] >= 0) + j++; - /* If we are overwriting a transition, free the old tag array. */ - if (trans->tags != NULL) free(trans->tags), trans->tags = NULL; - trans->tags = NULL; + /* If we are overwriting a transition, free the old tag array. */ + if (trans->tags != NULL) + xfree(trans->tags); + trans->tags = NULL; - /* If there were any tags, allocate an array and fill it. */ - if (i + j > 0) { - trans->tags = malloc(sizeof(*trans->tags) * (i + j + 1)); - if (!trans->tags) return REG_ESPACE; - i = 0; - if (p1->tags != NULL) - while (p1->tags[i] >= 0) { - trans->tags[i] = p1->tags[i]; - i++; - } - l = i; - j = 0; - if (p2->tags != NULL) - while (p2->tags[j] >= 0) { - /* Don't add duplicates. */ - dup = 0; - for (k = 0; k < i; k++) - if (trans->tags[k] == p2->tags[j]) { - dup = 1; - break; - } - if (!dup) trans->tags[l++] = p2->tags[j]; - j++; - } - trans->tags[l] = -1; - } + /* If there were any tags, allocate an array and fill it. */ + if (i + j > 0) + { + trans->tags = xmalloc(sizeof(*trans->tags) * (i + j + 1)); + if (!trans->tags) + return REG_ESPACE; + i = 0; + if (p1->tags != NULL) + while(p1->tags[i] >= 0) + { + trans->tags[i] = p1->tags[i]; + i++; + } + l = i; + j = 0; + if (p2->tags != NULL) + while (p2->tags[j] >= 0) + { + /* Don't add duplicates. */ + dup = 0; + for (k = 0; k < i; k++) + if (trans->tags[k] == p2->tags[j]) + { + dup = 1; + break; + } + if (!dup) + trans->tags[l++] = p2->tags[j]; + j++; + } + trans->tags[l] = -1; + } - p2++; + p2++; + } + p1++; } - p1++; - } else /* Compute a maximum limit for the number of transitions leaving from each state. */ - while (p1->position >= 0) { - p2 = orig_p2; - while (p2->position >= 0) { - counts[p1->position]++; - p2++; + while (p1->position >= 0) + { + p2 = orig_p2; + while (p2->position >= 0) + { + counts[p1->position]++; + p2++; + } + p1++; } - p1++; - } return REG_OK; } @@ -2324,60 +2647,72 @@ static reg_errcode_t tre_make_trans(tre_pos_and_tags_t *p1, labelled with one character range (there are no transitions on empty strings). The TNFA takes O(n^2) space in the worst case, `n' is size of the regexp. */ -static reg_errcode_t tre_ast_to_tnfa(tre_ast_node_t *node, - tre_tnfa_transition_t *transitions, - int *counts, int *offs) { +static reg_errcode_t +tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions, + int *counts, int *offs) +{ tre_union_t *uni; tre_catenation_t *cat; tre_iteration_t *iter; reg_errcode_t errcode = REG_OK; /* XXX - recurse using a stack!. */ - switch (node->type) { + switch (node->type) + { case LITERAL: break; case UNION: uni = (tre_union_t *)node->obj; errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs); - if (errcode != REG_OK) return errcode; + if (errcode != REG_OK) + return errcode; errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs); break; case CATENATION: cat = (tre_catenation_t *)node->obj; /* Add a transition from each position in cat->left->lastpos - to each position in cat->right->firstpos. */ + to each position in cat->right->firstpos. */ errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos, - transitions, counts, offs); - if (errcode != REG_OK) return errcode; + transitions, counts, offs); + if (errcode != REG_OK) + return errcode; errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs); - if (errcode != REG_OK) return errcode; + if (errcode != REG_OK) + return errcode; errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs); break; case ITERATION: iter = (tre_iteration_t *)node->obj; - unassert(iter->max == -1 || iter->max == 1); + assert(iter->max == -1 || iter->max == 1); - if (iter->max == -1) { - unassert(iter->min == 0 || iter->min == 1); - /* Add a transition from each last position in the iterated - expression to each first position. */ - errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos, - transitions, counts, offs); - if (errcode != REG_OK) return errcode; - } + if (iter->max == -1) + { + assert(iter->min == 0 || iter->min == 1); + /* Add a transition from each last position in the iterated + expression to each first position. */ + errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos, + transitions, counts, offs); + if (errcode != REG_OK) + return errcode; + } errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs); break; - } + } return errcode; } -#define ERROR_EXIT(err) \ - do { \ - errcode = err; \ - if (/*CONSTCOND*/ 1) goto error_exit; \ - } while (/*CONSTCOND*/ 0) + +#define ERROR_EXIT(err) \ + do \ + { \ + errcode = err; \ + if (/*CONSTCOND*/1) \ + goto error_exit; \ + } \ + while (/*CONSTCOND*/0) + /** * Compiles regular expression, e.g. @@ -2396,7 +2731,9 @@ static reg_errcode_t tre_ast_to_tnfa(tre_ast_node_t *node, * @return REG_OK, REG_NOMATCH, REG_BADPAT, etc. * @see regexec(), regfree(), regerror() */ -int regcomp(regex_t *preg, const char *regex, int cflags) { +int +regcomp(regex_t *restrict preg, const char *restrict regex, int cflags) +{ tre_stack_t *stack; tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r; tre_pos_and_tags_t *p; @@ -2415,23 +2752,26 @@ int regcomp(regex_t *preg, const char *regex, int cflags) { /* Allocate a stack used throughout the compilation process for various purposes. */ stack = tre_stack_new(512, 1024000, 128); - if (!stack) return REG_ESPACE; + if (!stack) + return REG_ESPACE; /* Allocate a fast memory allocator. */ mem = tre_mem_new(); - if (!mem) { - tre_stack_destroy(stack); - return REG_ESPACE; - } + if (!mem) + { + tre_stack_destroy(stack); + return REG_ESPACE; + } /* Parse the regexp. */ - bzero(&parse_ctx, sizeof(parse_ctx)); + memset(&parse_ctx, 0, sizeof(parse_ctx)); parse_ctx.mem = mem; parse_ctx.stack = stack; parse_ctx.start = regex; parse_ctx.cflags = cflags; parse_ctx.max_backref = -1; errcode = tre_parse(&parse_ctx); - if (errcode != REG_OK) ERROR_EXIT(errcode); + if (errcode != REG_OK) + ERROR_EXIT(errcode); preg->re_nsub = parse_ctx.submatch_id - 1; tree = parse_ctx.n; @@ -2440,115 +2780,141 @@ int regcomp(regex_t *preg, const char *regex, int cflags) { #endif /* TRE_DEBUG */ /* Referring to nonexistent subexpressions is illegal. */ - if (parse_ctx.max_backref > (int)preg->re_nsub) ERROR_EXIT(REG_ESUBREG); + if (parse_ctx.max_backref > (int)preg->re_nsub) + ERROR_EXIT(REG_ESUBREG); /* Allocate the TNFA struct. */ - tnfa = calloc(1, sizeof(tre_tnfa_t)); - if (tnfa == NULL) ERROR_EXIT(REG_ESPACE); + tnfa = xcalloc(1, sizeof(tre_tnfa_t)); + if (tnfa == NULL) + ERROR_EXIT(REG_ESPACE); tnfa->have_backrefs = parse_ctx.max_backref >= 0; tnfa->have_approx = 0; tnfa->num_submatches = parse_ctx.submatch_id; /* Set up tags for submatch addressing. If REG_NOSUB is set and the regexp does not have back references, this can be skipped. */ - if (tnfa->have_backrefs || !(cflags & REG_NOSUB)) { - /* Figure out how many tags we will need. */ - errcode = tre_add_tags(NULL, stack, tree, tnfa); - if (errcode != REG_OK) ERROR_EXIT(errcode); + if (tnfa->have_backrefs || !(cflags & REG_NOSUB)) + { + + /* Figure out how many tags we will need. */ + errcode = tre_add_tags(NULL, stack, tree, tnfa); + if (errcode != REG_OK) + ERROR_EXIT(errcode); + + if (tnfa->num_tags > 0) + { + tag_directions = xmalloc(sizeof(*tag_directions) + * (tnfa->num_tags + 1)); + if (tag_directions == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->tag_directions = tag_directions; + memset(tag_directions, -1, + sizeof(*tag_directions) * (tnfa->num_tags + 1)); + } + tnfa->minimal_tags = xcalloc((unsigned)tnfa->num_tags * 2 + 1, + sizeof(*tnfa->minimal_tags)); + if (tnfa->minimal_tags == NULL) + ERROR_EXIT(REG_ESPACE); + + submatch_data = xcalloc((unsigned)parse_ctx.submatch_id, + sizeof(*submatch_data)); + if (submatch_data == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->submatch_data = submatch_data; + + errcode = tre_add_tags(mem, stack, tree, tnfa); + if (errcode != REG_OK) + ERROR_EXIT(errcode); - if (tnfa->num_tags > 0) { - tag_directions = malloc(sizeof(*tag_directions) * (tnfa->num_tags + 1)); - if (tag_directions == NULL) ERROR_EXIT(REG_ESPACE); - tnfa->tag_directions = tag_directions; - memset(tag_directions, -1, - sizeof(*tag_directions) * (tnfa->num_tags + 1)); } - tnfa->minimal_tags = - calloc((unsigned)tnfa->num_tags * 2 + 1, sizeof(*tnfa->minimal_tags)); - if (tnfa->minimal_tags == NULL) ERROR_EXIT(REG_ESPACE); - - submatch_data = - calloc((unsigned)parse_ctx.submatch_id, sizeof(*submatch_data)); - if (submatch_data == NULL) ERROR_EXIT(REG_ESPACE); - tnfa->submatch_data = submatch_data; - - errcode = tre_add_tags(mem, stack, tree, tnfa); - if (errcode != REG_OK) ERROR_EXIT(errcode); - } /* Expand iteration nodes. */ - errcode = - tre_expand_ast(mem, stack, tree, &parse_ctx.position, tag_directions); - if (errcode != REG_OK) ERROR_EXIT(errcode); + errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position, + tag_directions); + if (errcode != REG_OK) + ERROR_EXIT(errcode); /* Add a dummy node for the final state. XXX - For certain patterns this dummy node can be optimized away, - for example "a*" or "ab*". Figure out a simple way to detect - this possibility. */ + for example "a*" or "ab*". Figure out a simple way to detect + this possibility. */ tmp_ast_l = tree; tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++); - if (tmp_ast_r == NULL) ERROR_EXIT(REG_ESPACE); + if (tmp_ast_r == NULL) + ERROR_EXIT(REG_ESPACE); tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r); - if (tree == NULL) ERROR_EXIT(REG_ESPACE); + if (tree == NULL) + ERROR_EXIT(REG_ESPACE); errcode = tre_compute_nfl(mem, stack, tree); - if (errcode != REG_OK) ERROR_EXIT(errcode); + if (errcode != REG_OK) + ERROR_EXIT(errcode); - counts = malloc(sizeof(int) * parse_ctx.position); - if (counts == NULL) ERROR_EXIT(REG_ESPACE); + counts = xmalloc(sizeof(int) * parse_ctx.position); + if (counts == NULL) + ERROR_EXIT(REG_ESPACE); - offs = malloc(sizeof(int) * parse_ctx.position); - if (offs == NULL) ERROR_EXIT(REG_ESPACE); + offs = xmalloc(sizeof(int) * parse_ctx.position); + if (offs == NULL) + ERROR_EXIT(REG_ESPACE); - for (i = 0; i < parse_ctx.position; i++) counts[i] = 0; + for (i = 0; i < parse_ctx.position; i++) + counts[i] = 0; tre_ast_to_tnfa(tree, NULL, counts, NULL); add = 0; - for (i = 0; i < parse_ctx.position; i++) { - offs[i] = add; - add += counts[i] + 1; - counts[i] = 0; - } - transitions = calloc((unsigned)add + 1, sizeof(*transitions)); - if (transitions == NULL) ERROR_EXIT(REG_ESPACE); + for (i = 0; i < parse_ctx.position; i++) + { + offs[i] = add; + add += counts[i] + 1; + counts[i] = 0; + } + transitions = xcalloc((unsigned)add + 1, sizeof(*transitions)); + if (transitions == NULL) + ERROR_EXIT(REG_ESPACE); tnfa->transitions = transitions; tnfa->num_transitions = add; errcode = tre_ast_to_tnfa(tree, transitions, counts, offs); - if (errcode != REG_OK) ERROR_EXIT(errcode); + if (errcode != REG_OK) + ERROR_EXIT(errcode); tnfa->firstpos_chars = NULL; p = tree->firstpos; i = 0; - while (p->position >= 0) { - i++; - p++; - } + while (p->position >= 0) + { + i++; + p++; + } - initial = calloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t)); - if (initial == NULL) ERROR_EXIT(REG_ESPACE); + initial = xcalloc((unsigned)i + 1, sizeof(tre_tnfa_transition_t)); + if (initial == NULL) + ERROR_EXIT(REG_ESPACE); tnfa->initial = initial; i = 0; - for (p = tree->firstpos; p->position >= 0; p++) { - initial[i].state = transitions + offs[p->position]; - initial[i].state_id = p->position; - initial[i].tags = NULL; - /* Copy the arrays p->tags, and p->params, they are allocated - from a tre_mem object. */ - if (p->tags) { - int j; - for (j = 0; p->tags[j] >= 0; j++) - ; - initial[i].tags = malloc(sizeof(*p->tags) * (j + 1)); - if (!initial[i].tags) ERROR_EXIT(REG_ESPACE); - memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1)); + for (p = tree->firstpos; p->position >= 0; p++) + { + initial[i].state = transitions + offs[p->position]; + initial[i].state_id = p->position; + initial[i].tags = NULL; + /* Copy the arrays p->tags, and p->params, they are allocated + from a tre_mem object. */ + if (p->tags) + { + int j; + for (j = 0; p->tags[j] >= 0; j++); + initial[i].tags = xmalloc(sizeof(*p->tags) * (j + 1)); + if (!initial[i].tags) + ERROR_EXIT(REG_ESPACE); + memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1)); + } + initial[i].assertions = p->assertions; + i++; } - initial[i].assertions = p->assertions; - i++; - } initial[i].state = NULL; tnfa->num_transitions = add; @@ -2558,23 +2924,29 @@ int regcomp(regex_t *preg, const char *regex, int cflags) { tre_mem_destroy(mem); tre_stack_destroy(stack); - free(counts), counts = NULL; - free(offs), offs = NULL; + xfree(counts); + xfree(offs); preg->TRE_REGEX_T_FIELD = (void *)tnfa; return REG_OK; -error_exit: + error_exit: /* Free everything that was allocated and return the error code. */ tre_mem_destroy(mem); - if (stack != NULL) tre_stack_destroy(stack); - if (counts != NULL) free(counts), counts = NULL; - if (offs != NULL) free(offs), offs = NULL; + if (stack != NULL) + tre_stack_destroy(stack); + if (counts != NULL) + xfree(counts); + if (offs != NULL) + xfree(offs); preg->TRE_REGEX_T_FIELD = (void *)tnfa; regfree(preg); return errcode; } + + + /** * Frees any memory allocated by regcomp(). * @@ -2582,43 +2954,51 @@ error_exit: * 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) { - unsigned int i; +void +regfree(regex_t *preg) +{ tre_tnfa_t *tnfa; + unsigned int i; tre_tnfa_transition_t *trans; - 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); - } + + 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) + xfree(tnfa->transitions[i].tags); + if (tnfa->transitions[i].neg_classes) + xfree(tnfa->transitions[i].neg_classes); } - if (tnfa->transitions) { - free(tnfa->transitions); + if (tnfa->transitions) + xfree(tnfa->transitions); + + if (tnfa->initial) + { + for (trans = tnfa->initial; trans->state; trans++) + { + if (trans->tags) + xfree(trans->tags); + } + xfree(tnfa->initial); } - if (tnfa->initial) { - for (trans = tnfa->initial; trans->state; trans++) { - if (trans->tags) { - free(trans->tags); - } - } - free(tnfa->initial); + + if (tnfa->submatch_data) + { + for (i = 0; i < tnfa->num_submatches; i++) + if (tnfa->submatch_data[i].parents) + xfree(tnfa->submatch_data[i].parents); + xfree(tnfa->submatch_data); } - 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->tag_directions) + xfree(tnfa->tag_directions); + if (tnfa->firstpos_chars) + xfree(tnfa->firstpos_chars); + if (tnfa->minimal_tags) + xfree(tnfa->minimal_tags); + xfree(tnfa); } diff --git a/third_party/regex/regerror.c b/third_party/regex/regerror.c index b922b9b59..dbd4b763d 100644 --- a/third_party/regex/regerror.c +++ b/third_party/regex/regerror.c @@ -1,65 +1,37 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/intrin/safemacros.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "third_party/regex/regex.h" +#include +#include +#include +#include "libc/str/locale.internal.h" /* Error message strings for error codes listed in `regex.h'. This list needs to be in sync with the codes listed there, naturally. */ -static const char kRegexErrors[] = - "No error\0" - "No match\0" - "Invalid regexp\0" - "Unknown collating element\0" - "Unknown character class name\0" - "Trailing backslash\0" - "Invalid back reference\0" - "Missing ']'\0" - "Missing ')'\0" - "Missing '}'\0" - "Invalid contents of {}\0" - "Invalid character range\0" - "Out of memory\0" - "Repetition not preceded by valid expression\0"; +/* Converted to single string by Rich Felker to remove the need for + * data relocations at runtime, 27 Feb 2006. */ -static const char *IndexDoubleNulString(const char *s, unsigned i) { - size_t n; - while (i--) { - if ((n = strlen(s))) { - s += n + 1; - } else { - return NULL; - } - } - return s; -} +static const char messages[] = { + "No error\0" + "No match\0" + "Invalid regexp\0" + "Unknown collating element\0" + "Unknown character class name\0" + "Trailing backslash\0" + "Invalid back reference\0" + "Missing ']'\0" + "Missing ')'\0" + "Missing '}'\0" + "Invalid contents of {}\0" + "Invalid character range\0" + "Out of memory\0" + "Repetition not preceded by valid expression\0" + "\0Unknown error" +}; -/** - * Converts regular expression error code to string. - * - * @param e is error code - * @return number of bytes needed to hold entire string - */ -size_t regerror(int e, const regex_t *preg, char *buf, size_t size) { - return 1 + (snprintf)(buf, size, "%s", - firstnonnull(IndexDoubleNulString(kRegexErrors, e), - "Unknown error")); +size_t regerror(int e, const regex_t *restrict preg, char *restrict buf, size_t size) +{ + const char *s; + for (s=messages; e && *s; e--, s+=strlen(s)+1); + if (!*s) s++; + s = LCTRANS_CUR(s); + return 1+snprintf(buf, size, "%s", s); } diff --git a/third_party/regex/regexec.c b/third_party/regex/regexec.c index fd4b4446f..d99696d8a 100644 --- a/third_party/regex/regexec.c +++ b/third_party/regex/regexec.c @@ -65,86 +65,99 @@ TRE regex (BSD-2 License)\n\ Copyright 2001-2009 Ville Laurikari \n\ Copyright 2016 Szabolcs Nagy"); -static void tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, - const tre_tnfa_t *tnfa, regoff_t *tags, - regoff_t match_eo); +static void +tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, + const tre_tnfa_t *tnfa, regoff_t *tags, regoff_t match_eo); /*********************************************************************** from tre-match-utils.h ***********************************************************************/ -#define GET_NEXT_WCHAR() \ - do { \ - prev_c = next_c; \ - pos += pos_add_next; \ - if ((pos_add_next = mbtowc(&next_c, str_byte, MB_LEN_MAX)) <= 0) { \ - if (pos_add_next < 0) { \ - ret = REG_NOMATCH; \ - goto error_exit; \ - } else \ - pos_add_next++; \ - } \ - str_byte += pos_add_next; \ +#define GET_NEXT_WCHAR() do { \ + prev_c = next_c; pos += pos_add_next; \ + if ((pos_add_next = mbtowc(&next_c, str_byte, MB_LEN_MAX)) <= 0) { \ + if (pos_add_next < 0) { ret = REG_NOMATCH; goto error_exit; } \ + else pos_add_next++; \ + } \ + str_byte += pos_add_next; \ } while (0) -#define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum(c)) +#define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum(c)) + +#define CHECK_ASSERTIONS(assertions) \ + (((assertions & ASSERT_AT_BOL) \ + && (pos > 0 || reg_notbol) \ + && (prev_c != L'\n' || !reg_newline)) \ + || ((assertions & ASSERT_AT_EOL) \ + && (next_c != L'\0' || reg_noteol) \ + && (next_c != L'\n' || !reg_newline)) \ + || ((assertions & ASSERT_AT_BOW) \ + && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) \ + || ((assertions & ASSERT_AT_EOW) \ + && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) \ + || ((assertions & ASSERT_AT_WB) \ + && (pos != 0 && next_c != L'\0' \ + && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) \ + || ((assertions & ASSERT_AT_WB_NEG) \ + && (pos == 0 || next_c == L'\0' \ + || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c)))) + +#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \ + (((trans_i->assertions & ASSERT_CHAR_CLASS) \ + && !(tnfa->cflags & REG_ICASE) \ + && !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) \ + || ((trans_i->assertions & ASSERT_CHAR_CLASS) \ + && (tnfa->cflags & REG_ICASE) \ + && !tre_isctype(tre_tolower((tre_cint_t)prev_c),trans_i->u.class) \ + && !tre_isctype(tre_toupper((tre_cint_t)prev_c),trans_i->u.class)) \ + || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) \ + && tre_neg_char_classes_match(trans_i->neg_classes,(tre_cint_t)prev_c,\ + tnfa->cflags & REG_ICASE))) + -#define CHECK_ASSERTIONS(assertions) \ - (((assertions & ASSERT_AT_BOL) && (pos > 0 || reg_notbol) && \ - (prev_c != L'\n' || !reg_newline)) || \ - ((assertions & ASSERT_AT_EOL) && (next_c != L'\0' || reg_noteol) && \ - (next_c != L'\n' || !reg_newline)) || \ - ((assertions & ASSERT_AT_BOW) && \ - (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) || \ - ((assertions & ASSERT_AT_EOW) && \ - (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) || \ - ((assertions & ASSERT_AT_WB) && \ - (pos != 0 && next_c != L'\0' && \ - IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) || \ - ((assertions & ASSERT_AT_WB_NEG) && \ - (pos == 0 || next_c == L'\0' || \ - IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c)))) -#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \ - (((trans_i->assertions & ASSERT_CHAR_CLASS) && \ - !(tnfa->cflags & REG_ICASE) && \ - !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) || \ - ((trans_i->assertions & ASSERT_CHAR_CLASS) && (tnfa->cflags & REG_ICASE) && \ - !tre_isctype(tre_tolower((tre_cint_t)prev_c), trans_i->u.class) && \ - !tre_isctype(tre_toupper((tre_cint_t)prev_c), trans_i->u.class)) || \ - ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) && \ - tre_neg_char_classes_match(trans_i->neg_classes, (tre_cint_t)prev_c, \ - tnfa->cflags & REG_ICASE))) /* Returns 1 if `t1' wins `t2', 0 otherwise. */ -static int tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions, - regoff_t *t1, regoff_t *t2) { +static int +tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions, + regoff_t *t1, regoff_t *t2) +{ int i; - for (i = 0; i < num_tags; i++) { - if (tag_directions[i] == TRE_TAG_MINIMIZE) { - if (t1[i] < t2[i]) return 1; - if (t1[i] > t2[i]) return 0; - } else { - if (t1[i] > t2[i]) return 1; - if (t1[i] < t2[i]) return 0; + for (i = 0; i < num_tags; i++) + { + if (tag_directions[i] == TRE_TAG_MINIMIZE) + { + if (t1[i] < t2[i]) + return 1; + if (t1[i] > t2[i]) + return 0; + } + else + { + if (t1[i] > t2[i]) + return 1; + if (t1[i] < t2[i]) + return 0; + } } - } /* assert(0);*/ return 0; } -static int tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, - int icase) { +static int +tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase) +{ while (*classes != (tre_ctype_t)0) - if ((!icase && tre_isctype(wc, *classes)) || - (icase && (tre_isctype(tre_toupper(wc), *classes) || - tre_isctype(tre_tolower(wc), *classes)))) + if ((!icase && tre_isctype(wc, *classes)) + || (icase && (tre_isctype(tre_toupper(wc), *classes) + || tre_isctype(tre_tolower(wc), *classes)))) return 1; /* Match. */ else classes++; return 0; /* No match. */ } + /*********************************************************************** from tre-match-parallel.c ***********************************************************************/ @@ -175,10 +188,12 @@ typedef struct { regoff_t **tags; } tre_reach_pos_t; -static reg_errcode_t tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, - const void *string, - regoff_t *match_tags, int eflags, - regoff_t *match_end_ofs) { + +static reg_errcode_t +tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, + regoff_t *match_tags, int eflags, + regoff_t *match_end_ofs) +{ /* State variables required by GET_NEXT_WCHAR. */ tre_char_t prev_c = 0, next_c = 0; const char *str_byte = string; @@ -199,13 +214,13 @@ static reg_errcode_t tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, int *tag_i; int num_tags, i; - regoff_t match_eo = -1; /* end offset of match (-1 if no match found yet) */ + regoff_t match_eo = -1; /* end offset of match (-1 if no match found yet) */ int new_match = 0; regoff_t *tmp_tags = NULL; regoff_t *tmp_iptr; #ifdef TRE_MBSTATE - bzero(&mbstate, sizeof(mbstate)); + memset(&mbstate, '\0', sizeof(mbstate)); #endif /* TRE_MBSTATE */ if (!match_tags) @@ -222,15 +237,15 @@ static reg_errcode_t tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, /* Ensure that tbytes and xbytes*num_states cannot overflow, and that * they don't contribute more than 1/8 of SIZE_MAX to total_bytes. */ - if (num_tags > SIZE_MAX / (8 * sizeof(regoff_t) * tnfa->num_states)) + if (num_tags > SIZE_MAX/(8 * sizeof(regoff_t) * tnfa->num_states)) return REG_ESPACE; /* Likewise check rbytes. */ - if (tnfa->num_states + 1 > SIZE_MAX / (8 * sizeof(*reach_next))) + if (tnfa->num_states+1 > SIZE_MAX/(8 * sizeof(*reach_next))) return REG_ESPACE; /* Likewise check pbytes. */ - if (tnfa->num_states > SIZE_MAX / (8 * sizeof(*reach_pos))) + if (tnfa->num_states > SIZE_MAX/(8 * sizeof(*reach_pos))) return REG_ESPACE; /* Compute the length of the block we need. */ @@ -238,12 +253,14 @@ static reg_errcode_t tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, rbytes = sizeof(*reach_next) * (tnfa->num_states + 1); pbytes = sizeof(*reach_pos) * tnfa->num_states; xbytes = sizeof(regoff_t) * num_tags; - total_bytes = (sizeof(long) - 1) * 4 /* for alignment paddings */ - + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes; + total_bytes = + (sizeof(long) - 1) * 4 /* for alignment paddings */ + + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes; /* Allocate the memory. */ buf = calloc(total_bytes, 1); - if (buf == NULL) return REG_ESPACE; + if (buf == NULL) + return REG_ESPACE; /* Get the various pointers within tmp_buf (properly aligned). */ tmp_tags = (void *)buf; @@ -258,177 +275,216 @@ static reg_errcode_t tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, reach_pos = (void *)tmp_buf; tmp_buf += pbytes; tmp_buf += ALIGN(tmp_buf, long); - for (i = 0; i < tnfa->num_states; i++) { - reach[i].tags = (void *)tmp_buf; - tmp_buf += xbytes; - reach_next[i].tags = (void *)tmp_buf; - tmp_buf += xbytes; - } + for (i = 0; i < tnfa->num_states; i++) + { + reach[i].tags = (void *)tmp_buf; + tmp_buf += xbytes; + reach_next[i].tags = (void *)tmp_buf; + tmp_buf += xbytes; + } } - for (i = 0; i < tnfa->num_states; i++) reach_pos[i].pos = -1; + for (i = 0; i < tnfa->num_states; i++) + reach_pos[i].pos = -1; GET_NEXT_WCHAR(); pos = 0; reach_next_i = reach_next; - while (1) { - /* If no match found yet, add the initial states to `reach_next'. */ - if (match_eo < 0) { - trans_i = tnfa->initial; - while (trans_i->state != NULL) { - if (reach_pos[trans_i->state_id].pos < pos) { - if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions)) { - trans_i++; - continue; - } + while (1) + { + /* If no match found yet, add the initial states to `reach_next'. */ + if (match_eo < 0) + { + trans_i = tnfa->initial; + while (trans_i->state != NULL) + { + if (reach_pos[trans_i->state_id].pos < pos) + { + if (trans_i->assertions + && CHECK_ASSERTIONS(trans_i->assertions)) + { + trans_i++; + continue; + } - reach_next_i->state = trans_i->state; - for (i = 0; i < num_tags; i++) reach_next_i->tags[i] = -1; - tag_i = trans_i->tags; - if (tag_i) - while (*tag_i >= 0) { - if (*tag_i < num_tags) reach_next_i->tags[*tag_i] = pos; - tag_i++; - } - if (reach_next_i->state == tnfa->final) { - match_eo = pos; - new_match = 1; - for (i = 0; i < num_tags; i++) - match_tags[i] = reach_next_i->tags[i]; - } - reach_pos[trans_i->state_id].pos = pos; - reach_pos[trans_i->state_id].tags = &reach_next_i->tags; - reach_next_i++; - } - trans_i++; - } - reach_next_i->state = NULL; - } else { - if (num_tags == 0 || reach_next_i == reach_next) - /* We have found a match. */ - break; - } + reach_next_i->state = trans_i->state; + for (i = 0; i < num_tags; i++) + reach_next_i->tags[i] = -1; + tag_i = trans_i->tags; + if (tag_i) + while (*tag_i >= 0) + { + if (*tag_i < num_tags) + reach_next_i->tags[*tag_i] = pos; + tag_i++; + } + if (reach_next_i->state == tnfa->final) + { + match_eo = pos; + new_match = 1; + for (i = 0; i < num_tags; i++) + match_tags[i] = reach_next_i->tags[i]; + } + reach_pos[trans_i->state_id].pos = pos; + reach_pos[trans_i->state_id].tags = &reach_next_i->tags; + reach_next_i++; + } + trans_i++; + } + reach_next_i->state = NULL; + } + else + { + if (num_tags == 0 || reach_next_i == reach_next) + /* We have found a match. */ + break; + } - /* Check for end of string. */ - if (!next_c) break; + /* Check for end of string. */ + if (!next_c) break; - GET_NEXT_WCHAR(); - - /* Swap `reach' and `reach_next'. */ - reach_i = reach; - reach = reach_next; - reach_next = reach_i; - - /* For each state in `reach', weed out states that don't fulfill the - minimal matching conditions. */ - if (tnfa->num_minimals && new_match) { - new_match = 0; - reach_next_i = reach_next; - for (reach_i = reach; reach_i->state; reach_i++) { - int skip = 0; - for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2) { - int end = tnfa->minimal_tags[i]; - int start = tnfa->minimal_tags[i + 1]; - if (end >= num_tags) { - skip = 1; - break; - } else if (reach_i->tags[start] == match_tags[start] && - reach_i->tags[end] < match_tags[end]) { - skip = 1; - break; - } - } - if (!skip) { - reach_next_i->state = reach_i->state; - tmp_iptr = reach_next_i->tags; - reach_next_i->tags = reach_i->tags; - reach_i->tags = tmp_iptr; - reach_next_i++; - } - } - reach_next_i->state = NULL; + GET_NEXT_WCHAR(); /* Swap `reach' and `reach_next'. */ reach_i = reach; reach = reach_next; reach_next = reach_i; + + /* For each state in `reach', weed out states that don't fulfill the + minimal matching conditions. */ + if (tnfa->num_minimals && new_match) + { + new_match = 0; + reach_next_i = reach_next; + for (reach_i = reach; reach_i->state; reach_i++) + { + int skip = 0; + for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2) + { + int end = tnfa->minimal_tags[i]; + int start = tnfa->minimal_tags[i + 1]; + if (end >= num_tags) + { + skip = 1; + break; + } + else if (reach_i->tags[start] == match_tags[start] + && reach_i->tags[end] < match_tags[end]) + { + skip = 1; + break; + } + } + if (!skip) + { + reach_next_i->state = reach_i->state; + tmp_iptr = reach_next_i->tags; + reach_next_i->tags = reach_i->tags; + reach_i->tags = tmp_iptr; + reach_next_i++; + } + } + reach_next_i->state = NULL; + + /* Swap `reach' and `reach_next'. */ + reach_i = reach; + reach = reach_next; + reach_next = reach_i; + } + + /* For each state in `reach' see if there is a transition leaving with + the current input symbol to a state not yet in `reach_next', and + add the destination states to `reach_next'. */ + reach_next_i = reach_next; + for (reach_i = reach; reach_i->state; reach_i++) + { + for (trans_i = reach_i->state; trans_i->state; trans_i++) + { + /* Does this transition match the input symbol? */ + if (trans_i->code_min <= (tre_cint_t)prev_c && + trans_i->code_max >= (tre_cint_t)prev_c) + { + if (trans_i->assertions + && (CHECK_ASSERTIONS(trans_i->assertions) + || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) + { + continue; + } + + /* Compute the tags after this transition. */ + for (i = 0; i < num_tags; i++) + tmp_tags[i] = reach_i->tags[i]; + tag_i = trans_i->tags; + if (tag_i != NULL) + while (*tag_i >= 0) + { + if (*tag_i < num_tags) + tmp_tags[*tag_i] = pos; + tag_i++; + } + + if (reach_pos[trans_i->state_id].pos < pos) + { + /* Found an unvisited node. */ + reach_next_i->state = trans_i->state; + tmp_iptr = reach_next_i->tags; + reach_next_i->tags = tmp_tags; + tmp_tags = tmp_iptr; + reach_pos[trans_i->state_id].pos = pos; + reach_pos[trans_i->state_id].tags = &reach_next_i->tags; + + if (reach_next_i->state == tnfa->final + && (match_eo == -1 + || (num_tags > 0 + && reach_next_i->tags[0] <= match_tags[0]))) + { + match_eo = pos; + new_match = 1; + for (i = 0; i < num_tags; i++) + match_tags[i] = reach_next_i->tags[i]; + } + reach_next_i++; + + } + else + { + assert(reach_pos[trans_i->state_id].pos == pos); + /* Another path has also reached this state. We choose + the winner by examining the tag values for both + paths. */ + if (tre_tag_order(num_tags, tnfa->tag_directions, + tmp_tags, + *reach_pos[trans_i->state_id].tags)) + { + /* The new path wins. */ + tmp_iptr = *reach_pos[trans_i->state_id].tags; + *reach_pos[trans_i->state_id].tags = tmp_tags; + if (trans_i->state == tnfa->final) + { + match_eo = pos; + new_match = 1; + for (i = 0; i < num_tags; i++) + match_tags[i] = tmp_tags[i]; + } + tmp_tags = tmp_iptr; + } + } + } + } + } + reach_next_i->state = NULL; } - /* For each state in `reach' see if there is a transition leaving with - the current input symbol to a state not yet in `reach_next', and - add the destination states to `reach_next'. */ - reach_next_i = reach_next; - for (reach_i = reach; reach_i->state; reach_i++) { - for (trans_i = reach_i->state; trans_i->state; trans_i++) { - /* Does this transition match the input symbol? */ - if (trans_i->code_min <= (tre_cint_t)prev_c && - trans_i->code_max >= (tre_cint_t)prev_c) { - if (trans_i->assertions && - (CHECK_ASSERTIONS(trans_i->assertions) || - CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) { - continue; - } - - /* Compute the tags after this transition. */ - for (i = 0; i < num_tags; i++) tmp_tags[i] = reach_i->tags[i]; - tag_i = trans_i->tags; - if (tag_i != NULL) - while (*tag_i >= 0) { - if (*tag_i < num_tags) tmp_tags[*tag_i] = pos; - tag_i++; - } - - if (reach_pos[trans_i->state_id].pos < pos) { - /* Found an unvisited node. */ - reach_next_i->state = trans_i->state; - tmp_iptr = reach_next_i->tags; - reach_next_i->tags = tmp_tags; - tmp_tags = tmp_iptr; - reach_pos[trans_i->state_id].pos = pos; - reach_pos[trans_i->state_id].tags = &reach_next_i->tags; - - if (reach_next_i->state == tnfa->final && - (match_eo == -1 || - (num_tags > 0 && reach_next_i->tags[0] <= match_tags[0]))) { - match_eo = pos; - new_match = 1; - for (i = 0; i < num_tags; i++) - match_tags[i] = reach_next_i->tags[i]; - } - reach_next_i++; - - } else { - unassert(reach_pos[trans_i->state_id].pos == pos); - /* Another path has also reached this state. We choose - the winner by examining the tag values for both - paths. */ - if (tre_tag_order(num_tags, tnfa->tag_directions, tmp_tags, - *reach_pos[trans_i->state_id].tags)) { - /* The new path wins. */ - tmp_iptr = *reach_pos[trans_i->state_id].tags; - *reach_pos[trans_i->state_id].tags = tmp_tags; - if (trans_i->state == tnfa->final) { - match_eo = pos; - new_match = 1; - for (i = 0; i < num_tags; i++) match_tags[i] = tmp_tags[i]; - } - tmp_tags = tmp_iptr; - } - } - } - } - } - reach_next_i->state = NULL; - } - *match_end_ofs = match_eo; ret = match_eo >= 0 ? REG_OK : REG_NOMATCH; error_exit: - free(buf), buf = NULL; + xfree(buf); return ret; } + + /*********************************************************************** from tre-match-backtrack.c ***********************************************************************/ @@ -472,7 +528,7 @@ typedef struct tre_backtrack_struct { tre_backtrack_item_t item; struct tre_backtrack_struct *prev; struct tre_backtrack_struct *next; -} * tre_backtrack_t; +} *tre_backtrack_t; #ifdef TRE_MBSTATE #define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate) @@ -482,67 +538,84 @@ typedef struct tre_backtrack_struct { #define BT_STACK_MBSTATE_OUT #endif /* !TRE_MBSTATE */ -#define tre_bt_mem_new tre_mem_new -#define tre_bt_mem_alloc tre_mem_alloc -#define tre_bt_mem_destroy tre_mem_destroy +#define tre_bt_mem_new tre_mem_new +#define tre_bt_mem_alloc tre_mem_alloc +#define tre_bt_mem_destroy tre_mem_destroy -#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, \ - _tags, _mbstate) \ - do { \ - int i; \ - if (!stack->next) { \ - tre_backtrack_t s; \ - s = tre_bt_mem_alloc(mem, sizeof(*s)); \ - if (!s) { \ - tre_bt_mem_destroy(mem); \ - if (tags) free(tags), tags = NULL; \ - if (pmatch) free(pmatch), pmatch = NULL; \ - if (states_seen) free(states_seen), states_seen = NULL; \ - return REG_ESPACE; \ - } \ - s->prev = stack; \ - s->next = NULL; \ - s->item.tags = tre_bt_mem_alloc(mem, sizeof(*tags) * tnfa->num_tags); \ - if (!s->item.tags) { \ - tre_bt_mem_destroy(mem); \ - if (tags) free(tags), tags = NULL; \ - if (pmatch) free(pmatch), pmatch = NULL; \ - if (states_seen) free(states_seen), states_seen = NULL; \ - return REG_ESPACE; \ - } \ - stack->next = s; \ - stack = s; \ - } else \ - stack = stack->next; \ - stack->item.pos = (_pos); \ - stack->item.str_byte = (_str_byte); \ - stack->item.state = (_state); \ - stack->item.state_id = (_state_id); \ - stack->item.next_c = (_next_c); \ - for (i = 0; i < tnfa->num_tags; i++) stack->item.tags[i] = (_tags)[i]; \ - BT_STACK_MBSTATE_IN; \ - } while (0) -#define BT_STACK_POP() \ - do { \ - int i; \ - unassert(stack->prev); \ - pos = stack->item.pos; \ - str_byte = stack->item.str_byte; \ - state = stack->item.state; \ - next_c = stack->item.next_c; \ - for (i = 0; i < tnfa->num_tags; i++) tags[i] = stack->item.tags[i]; \ - BT_STACK_MBSTATE_OUT; \ - stack = stack->prev; \ - } while (0) +#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \ + do \ + { \ + int i; \ + if (!stack->next) \ + { \ + tre_backtrack_t s; \ + s = tre_bt_mem_alloc(mem, sizeof(*s)); \ + if (!s) \ + { \ + tre_bt_mem_destroy(mem); \ + if (tags) \ + xfree(tags); \ + if (pmatch) \ + xfree(pmatch); \ + if (states_seen) \ + xfree(states_seen); \ + return REG_ESPACE; \ + } \ + s->prev = stack; \ + s->next = NULL; \ + s->item.tags = tre_bt_mem_alloc(mem, \ + sizeof(*tags) * tnfa->num_tags); \ + if (!s->item.tags) \ + { \ + tre_bt_mem_destroy(mem); \ + if (tags) \ + xfree(tags); \ + if (pmatch) \ + xfree(pmatch); \ + if (states_seen) \ + xfree(states_seen); \ + return REG_ESPACE; \ + } \ + stack->next = s; \ + stack = s; \ + } \ + else \ + stack = stack->next; \ + stack->item.pos = (_pos); \ + stack->item.str_byte = (_str_byte); \ + stack->item.state = (_state); \ + stack->item.state_id = (_state_id); \ + stack->item.next_c = (_next_c); \ + for (i = 0; i < tnfa->num_tags; i++) \ + stack->item.tags[i] = (_tags)[i]; \ + BT_STACK_MBSTATE_IN; \ + } \ + while (0) + +#define BT_STACK_POP() \ + do \ + { \ + int i; \ + assert(stack->prev); \ + pos = stack->item.pos; \ + str_byte = stack->item.str_byte; \ + state = stack->item.state; \ + next_c = stack->item.next_c; \ + for (i = 0; i < tnfa->num_tags; i++) \ + tags[i] = stack->item.tags[i]; \ + BT_STACK_MBSTATE_OUT; \ + stack = stack->prev; \ + } \ + while (0) #undef MIN #define MIN(a, b) ((a) <= (b) ? (a) : (b)) -static reg_errcode_t tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, - const void *string, - regoff_t *match_tags, int eflags, - regoff_t *match_end_ofs) { +static reg_errcode_t +tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, + regoff_t *match_tags, int eflags, regoff_t *match_end_ofs) +{ /* State variables required by GET_NEXT_WCHAR. */ tre_char_t prev_c = 0, next_c = 0; const char *str_byte = string; @@ -585,48 +658,60 @@ static reg_errcode_t tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, int ret; #ifdef TRE_MBSTATE - bzero(&mbstate, sizeof(mbstate)); + memset(&mbstate, '\0', sizeof(mbstate)); #endif /* TRE_MBSTATE */ - if (!mem) return REG_ESPACE; + if (!mem) + return REG_ESPACE; stack = tre_bt_mem_alloc(mem, sizeof(*stack)); - if (!stack) { - ret = REG_ESPACE; - goto error_exit; - } + if (!stack) + { + ret = REG_ESPACE; + goto error_exit; + } stack->prev = NULL; stack->next = NULL; - if (tnfa->num_tags) { - tags = malloc(sizeof(*tags) * tnfa->num_tags); - if (!tags) { - ret = REG_ESPACE; - goto error_exit; + if (tnfa->num_tags) + { + tags = xmalloc(sizeof(*tags) * tnfa->num_tags); + if (!tags) + { + ret = REG_ESPACE; + goto error_exit; + } } - } - if (tnfa->num_submatches) { - pmatch = malloc(sizeof(*pmatch) * tnfa->num_submatches); - if (!pmatch) { - ret = REG_ESPACE; - goto error_exit; + if (tnfa->num_submatches) + { + pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches); + if (!pmatch) + { + ret = REG_ESPACE; + goto error_exit; + } } - } - if (tnfa->num_states) { - states_seen = malloc(sizeof(*states_seen) * tnfa->num_states); - if (!states_seen) { - ret = REG_ESPACE; - goto error_exit; + if (tnfa->num_states) + { + states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states); + if (!states_seen) + { + ret = REG_ESPACE; + goto error_exit; + } } - } -retry : { - int i; - for (i = 0; i < tnfa->num_tags; i++) { - tags[i] = -1; - if (match_tags) match_tags[i] = -1; + retry: + { + int i; + for (i = 0; i < tnfa->num_tags; i++) + { + tags[i] = -1; + if (match_tags) + match_tags[i] = -1; + } + for (i = 0; i < tnfa->num_states; i++) + states_seen[i] = 0; } - for (i = 0; i < tnfa->num_states; i++) states_seen[i] = 0; -} state = NULL; pos = pos_start; @@ -640,174 +725,219 @@ retry : { /* Handle initial states. */ next_tags = NULL; - for (trans_i = tnfa->initial; trans_i->state; trans_i++) { - if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions)) { - continue; + for (trans_i = tnfa->initial; trans_i->state; trans_i++) + { + if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions)) + { + continue; + } + if (state == NULL) + { + /* Start from this state. */ + state = trans_i->state; + next_tags = trans_i->tags; + } + else + { + /* Backtrack to this state. */ + BT_STACK_PUSH(pos, str_byte, 0, trans_i->state, + trans_i->state_id, next_c, tags, mbstate); + { + int *tmp = trans_i->tags; + if (tmp) + while (*tmp >= 0) + stack->item.tags[*tmp++] = pos; + } + } } - if (state == NULL) { - /* Start from this state. */ - state = trans_i->state; - next_tags = trans_i->tags; - } else { - /* Backtrack to this state. */ - BT_STACK_PUSH(pos, str_byte, 0, trans_i->state, trans_i->state_id, next_c, - tags, mbstate); - { - int *tmp = trans_i->tags; - if (tmp) - while (*tmp >= 0) stack->item.tags[*tmp++] = pos; - } - } - } if (next_tags) - for (; *next_tags >= 0; next_tags++) tags[*next_tags] = pos; + for (; *next_tags >= 0; next_tags++) + tags[*next_tags] = pos; - if (state == NULL) goto backtrack; - while (1) { - tre_tnfa_transition_t *next_state; - int empty_br_match; + if (state == NULL) + goto backtrack; - if (state == tnfa->final) { - if (match_eo < pos || (match_eo == pos && match_tags && - tre_tag_order(tnfa->num_tags, tnfa->tag_directions, - tags, match_tags))) { - int i; - /* This match wins the previous match. */ - match_eo = pos; - if (match_tags) - for (i = 0; i < tnfa->num_tags; i++) match_tags[i] = tags[i]; - } - /* Our TNFAs never have transitions leaving from the final state, - so we jump right to backtracking. */ - goto backtrack; - } + while (1) + { + tre_tnfa_transition_t *next_state; + int empty_br_match; - /* Go to the next character in the input string. */ - empty_br_match = 0; - trans_i = state; - if (trans_i->state && trans_i->assertions & ASSERT_BACKREF) { - /* This is a back reference state. All transitions leaving from - this state have the same back reference "assertion". Instead - of reading the next character, we match the back reference. */ - regoff_t so, eo; - int bt = trans_i->u.backref; - regoff_t bt_len; - int result; + if (state == tnfa->final) + { + if (match_eo < pos + || (match_eo == pos + && match_tags + && tre_tag_order(tnfa->num_tags, tnfa->tag_directions, + tags, match_tags))) + { + int i; + /* This match wins the previous match. */ + match_eo = pos; + if (match_tags) + for (i = 0; i < tnfa->num_tags; i++) + match_tags[i] = tags[i]; + } + /* Our TNFAs never have transitions leaving from the final state, + so we jump right to backtracking. */ + goto backtrack; + } - /* Get the substring we need to match against. Remember to - turn off REG_NOSUB temporarily. */ - tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB, tnfa, tags, - pos); - so = pmatch[bt].rm_so; - eo = pmatch[bt].rm_eo; - bt_len = eo - so; + /* Go to the next character in the input string. */ + empty_br_match = 0; + trans_i = state; + if (trans_i->state && trans_i->assertions & ASSERT_BACKREF) + { + /* This is a back reference state. All transitions leaving from + this state have the same back reference "assertion". Instead + of reading the next character, we match the back reference. */ + regoff_t so, eo; + int bt = trans_i->u.backref; + regoff_t bt_len; + int result; - result = strncmp((const char *)string + so, str_byte - 1, (size_t)bt_len); + /* Get the substring we need to match against. Remember to + turn off REG_NOSUB temporarily. */ + tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB, + tnfa, tags, pos); + so = pmatch[bt].rm_so; + eo = pmatch[bt].rm_eo; + bt_len = eo - so; - if (result == 0) { - /* Back reference matched. Check for infinite loop. */ - if (bt_len == 0) empty_br_match = 1; - if (empty_br_match && states_seen[trans_i->state_id]) { - goto backtrack; - } + result = strncmp((const char*)string + so, str_byte - 1, + (size_t)bt_len); - states_seen[trans_i->state_id] = empty_br_match; + if (result == 0) + { + /* Back reference matched. Check for infinite loop. */ + if (bt_len == 0) + empty_br_match = 1; + if (empty_br_match && states_seen[trans_i->state_id]) + { + goto backtrack; + } - /* Advance in input string and resync `prev_c', `next_c' - and pos. */ - str_byte += bt_len - 1; - pos += bt_len - 1; - GET_NEXT_WCHAR(); - } else { - goto backtrack; - } - } else { - /* Check for end of string. */ - if (next_c == L'\0') goto backtrack; + states_seen[trans_i->state_id] = empty_br_match; - /* Read the next character. */ - GET_NEXT_WCHAR(); - } + /* Advance in input string and resync `prev_c', `next_c' + and pos. */ + str_byte += bt_len - 1; + pos += bt_len - 1; + GET_NEXT_WCHAR(); + } + else + { + goto backtrack; + } + } + else + { + /* Check for end of string. */ + if (next_c == L'\0') + goto backtrack; - next_state = NULL; - for (trans_i = state; trans_i->state; trans_i++) { - if (trans_i->code_min <= (tre_cint_t)prev_c && - trans_i->code_max >= (tre_cint_t)prev_c) { - if (trans_i->assertions && - (CHECK_ASSERTIONS(trans_i->assertions) || - CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) { - continue; - } + /* Read the next character. */ + GET_NEXT_WCHAR(); + } - if (next_state == NULL) { - /* First matching transition. */ - next_state = trans_i->state; - next_tags = trans_i->tags; - } else { - /* Second matching transition. We may need to backtrack here - to take this transition instead of the first one, so we - push this transition in the backtracking stack so we can - jump back here if needed. */ - BT_STACK_PUSH(pos, str_byte, 0, trans_i->state, trans_i->state_id, - next_c, tags, mbstate); - { - int *tmp; - for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++) - stack->item.tags[*tmp] = pos; - } -#if 0 /* XXX - it's important not to look at all transitions here to keep \ - the stack small! */ + next_state = NULL; + for (trans_i = state; trans_i->state; trans_i++) + { + if (trans_i->code_min <= (tre_cint_t)prev_c + && trans_i->code_max >= (tre_cint_t)prev_c) + { + if (trans_i->assertions + && (CHECK_ASSERTIONS(trans_i->assertions) + || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) + { + continue; + } + + if (next_state == NULL) + { + /* First matching transition. */ + next_state = trans_i->state; + next_tags = trans_i->tags; + } + else + { + /* Second matching transition. We may need to backtrack here + to take this transition instead of the first one, so we + push this transition in the backtracking stack so we can + jump back here if needed. */ + BT_STACK_PUSH(pos, str_byte, 0, trans_i->state, + trans_i->state_id, next_c, tags, mbstate); + { + int *tmp; + for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++) + stack->item.tags[*tmp] = pos; + } +#if 0 /* XXX - it's important not to look at all transitions here to keep + the stack small! */ break; #endif - } - } - } + } + } + } - if (next_state != NULL) { - /* Matching transitions were found. Take the first one. */ - state = next_state; + if (next_state != NULL) + { + /* Matching transitions were found. Take the first one. */ + state = next_state; - /* Update the tag values. */ - if (next_tags) - while (*next_tags >= 0) tags[*next_tags++] = pos; - } else { - backtrack: - /* A matching transition was not found. Try to backtrack. */ - if (stack->prev) { - if (stack->item.state->assertions & ASSERT_BACKREF) { - states_seen[stack->item.state_id] = 0; - } + /* Update the tag values. */ + if (next_tags) + while (*next_tags >= 0) + tags[*next_tags++] = pos; + } + else + { + backtrack: + /* A matching transition was not found. Try to backtrack. */ + if (stack->prev) + { + if (stack->item.state->assertions & ASSERT_BACKREF) + { + states_seen[stack->item.state_id] = 0; + } - BT_STACK_POP(); - } else if (match_eo < 0) { - /* Try starting from a later position in the input string. */ - /* Check for end of string. */ - if (next_c == L'\0') { - break; - } - next_c = next_c_start; + BT_STACK_POP(); + } + else if (match_eo < 0) + { + /* Try starting from a later position in the input string. */ + /* Check for end of string. */ + if (next_c == L'\0') + { + break; + } + next_c = next_c_start; #ifdef TRE_MBSTATE - mbstate = mbstate_start; + mbstate = mbstate_start; #endif /* TRE_MBSTATE */ - str_byte = str_byte_start; - goto retry; - } else { - break; - } + str_byte = str_byte_start; + goto retry; + } + else + { + break; + } + } } - } ret = match_eo >= 0 ? REG_OK : REG_NOMATCH; *match_end_ofs = match_eo; -error_exit: + error_exit: tre_bt_mem_destroy(mem); #ifndef TRE_USE_ALLOCA - if (tags) free(tags), tags = NULL; - if (pmatch) free(pmatch), pmatch = NULL; - if (states_seen) free(states_seen), states_seen = NULL; + if (tags) + xfree(tags); + if (pmatch) + xfree(pmatch); + if (states_seen) + xfree(states_seen); #endif /* !TRE_USE_ALLOCA */ return ret; @@ -819,60 +949,72 @@ error_exit: /* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match endpoint values. */ -static void tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, - const tre_tnfa_t *tnfa, regoff_t *tags, - regoff_t match_eo) { +static void +tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, + const tre_tnfa_t *tnfa, regoff_t *tags, regoff_t match_eo) +{ tre_submatch_data_t *submatch_data; unsigned int i, j; int *parents; i = 0; - if (match_eo >= 0 && !(cflags & REG_NOSUB)) { - /* Construct submatch offsets from the tags. */ - submatch_data = tnfa->submatch_data; - while (i < tnfa->num_submatches && i < nmatch) { - if (submatch_data[i].so_tag == tnfa->end_tag) - pmatch[i].rm_so = match_eo; - else - pmatch[i].rm_so = tags[submatch_data[i].so_tag]; + if (match_eo >= 0 && !(cflags & REG_NOSUB)) + { + /* Construct submatch offsets from the tags. */ + submatch_data = tnfa->submatch_data; + while (i < tnfa->num_submatches && i < nmatch) + { + if (submatch_data[i].so_tag == tnfa->end_tag) + pmatch[i].rm_so = match_eo; + else + pmatch[i].rm_so = tags[submatch_data[i].so_tag]; - if (submatch_data[i].eo_tag == tnfa->end_tag) - pmatch[i].rm_eo = match_eo; - else - pmatch[i].rm_eo = tags[submatch_data[i].eo_tag]; + if (submatch_data[i].eo_tag == tnfa->end_tag) + pmatch[i].rm_eo = match_eo; + else + pmatch[i].rm_eo = tags[submatch_data[i].eo_tag]; - /* If either of the endpoints were not used, this submatch - was not part of the match. */ - if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) - pmatch[i].rm_so = pmatch[i].rm_eo = -1; + /* If either of the endpoints were not used, this submatch + was not part of the match. */ + if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + i++; + } + /* Reset all submatches that are not within all of their parent + submatches. */ + i = 0; + while (i < tnfa->num_submatches && i < nmatch) + { + if (pmatch[i].rm_eo == -1) + assert(pmatch[i].rm_so == -1); + assert(pmatch[i].rm_so <= pmatch[i].rm_eo); + + parents = submatch_data[i].parents; + if (parents != NULL) + for (j = 0; parents[j] >= 0; j++) + { + if (pmatch[i].rm_so < pmatch[parents[j]].rm_so + || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + } + i++; + } + } + + while (i < nmatch) + { + pmatch[i].rm_so = -1; + pmatch[i].rm_eo = -1; i++; } - /* Reset all submatches that are not within all of their parent - submatches. */ - i = 0; - while (i < tnfa->num_submatches && i < nmatch) { - if (pmatch[i].rm_eo == -1) unassert(pmatch[i].rm_so == -1); - unassert(pmatch[i].rm_so <= pmatch[i].rm_eo); - - parents = submatch_data[i].parents; - if (parents != NULL) - for (j = 0; parents[j] >= 0; j++) { - if (pmatch[i].rm_so < pmatch[parents[j]].rm_so || - pmatch[i].rm_eo > pmatch[parents[j]].rm_eo) - pmatch[i].rm_so = pmatch[i].rm_eo = -1; - } - i++; - } - } - - while (i < nmatch) { - pmatch[i].rm_so = -1; - pmatch[i].rm_eo = -1; - i++; - } } + +/* + Wrapper functions for POSIX compatible regexp matching. +*/ + /** * Executes regular expression. * @@ -880,26 +1022,37 @@ static void tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, * @param eflags can have REG_NOTBOL, REG_NOTEOL * @return 0 or REG_NOMATCH */ -int regexec(const regex_t *preg, const char *string, size_t nmatch, - regmatch_t *pmatch, int eflags) { +int +regexec(const regex_t *restrict preg, const char *restrict string, + size_t nmatch, regmatch_t pmatch[restrict], int eflags) +{ tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; reg_errcode_t status; regoff_t *tags = NULL, eo; if (tnfa->cflags & REG_NOSUB) nmatch = 0; - if (tnfa->num_tags > 0 && nmatch > 0) { - tags = malloc(sizeof(*tags) * tnfa->num_tags); - if (tags == NULL) return REG_ESPACE; - } + if (tnfa->num_tags > 0 && nmatch > 0) + { + tags = xmalloc(sizeof(*tags) * tnfa->num_tags); + if (tags == NULL) + return REG_ESPACE; + } + /* Dispatch to the appropriate matcher. */ - if (tnfa->have_backrefs) { - /* The regex has back references, use the backtracking matcher. */ - status = tre_tnfa_run_backtrack(tnfa, string, tags, eflags, &eo); - } else { - /* Exact matching, no back references, use the parallel matcher. */ - status = tre_tnfa_run_parallel(tnfa, string, tags, eflags, &eo); - } - if (status == REG_OK) /* A match was found, so fill the submatch registers. */ + if (tnfa->have_backrefs) + { + /* The regex has back references, use the backtracking matcher. */ + status = tre_tnfa_run_backtrack(tnfa, string, tags, eflags, &eo); + } + else + { + /* Exact matching, no back references, use the parallel matcher. */ + status = tre_tnfa_run_parallel(tnfa, string, tags, eflags, &eo); + } + + if (status == REG_OK) + /* A match was found, so fill the submatch registers. */ tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo); - if (tags) free(tags), tags = NULL; + if (tags) + xfree(tags); return status; } diff --git a/third_party/regex/tre-mem.c b/third_party/regex/tre-mem.c index 971900a08..ce70ec6f1 100644 --- a/third_party/regex/tre-mem.c +++ b/third_party/regex/tre-mem.c @@ -56,7 +56,6 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/regex/tre.inc" /* This memory allocator is for allocating small memory blocks efficiently @@ -65,6 +64,11 @@ allocators, though. */ +#include +#include + +#include "tre.inc" + /* This memory allocator is for allocating small memory blocks efficiently in terms of memory overhead and execution speed. The allocated blocks @@ -73,79 +77,110 @@ */ /* Returns a new memory allocator or NULL if out of memory. */ -tre_mem_t tre_mem_new_impl(int provided, void *provided_block) { +tre_mem_t +tre_mem_new_impl(int provided, void *provided_block) +{ tre_mem_t mem; - if (provided) { - mem = provided_block; - bzero(mem, sizeof(*mem)); - } else - mem = calloc(1, sizeof(*mem)); - if (mem == NULL) return NULL; + if (provided) + { + mem = provided_block; + memset(mem, 0, sizeof(*mem)); + } + else + mem = xcalloc(1, sizeof(*mem)); + if (mem == NULL) + return NULL; return mem; } + /* Frees the memory allocator and all memory allocated with it. */ -void tre_mem_destroy(tre_mem_t mem) { +void +tre_mem_destroy(tre_mem_t mem) +{ tre_list_t *tmp, *l = mem->blocks; - while (l != NULL) { - free(l->data), l->data = NULL; - tmp = l->next; - free(l), l = tmp; - } - free(mem), mem = NULL; + + while (l != NULL) + { + xfree(l->data); + tmp = l->next; + xfree(l); + l = tmp; + } + xfree(mem); } + /* Allocates a block of `size' bytes from `mem'. Returns a pointer to the allocated block or NULL if an underlying malloc() failed. */ -void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, - int zero, size_t size) { +void * +tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, + int zero, size_t size) +{ void *ptr; - if (mem->failed) { - return NULL; - } - if (mem->n < size) { - /* We need more memory than is available in the current block. - Allocate a new block. */ - tre_list_t *l; - if (provided) { - if (provided_block == NULL) { - mem->failed = 1; - return NULL; - } - mem->ptr = provided_block; - mem->n = TRE_MEM_BLOCK_SIZE; - } else { - int block_size; - if (size * 8 > TRE_MEM_BLOCK_SIZE) - block_size = size * 8; - else - block_size = TRE_MEM_BLOCK_SIZE; - l = malloc(sizeof(*l)); - if (l == NULL) { - mem->failed = 1; - return NULL; - } - l->data = malloc(block_size); - if (l->data == NULL) { - free(l), l = NULL; - mem->failed = 1; - return NULL; - } - l->next = NULL; - if (mem->current != NULL) mem->current->next = l; - if (mem->blocks == NULL) mem->blocks = l; - mem->current = l; - mem->ptr = l->data; - mem->n = block_size; + + if (mem->failed) + { + return NULL; } - } + + if (mem->n < size) + { + /* We need more memory than is available in the current block. + Allocate a new block. */ + tre_list_t *l; + if (provided) + { + if (provided_block == NULL) + { + mem->failed = 1; + return NULL; + } + mem->ptr = provided_block; + mem->n = TRE_MEM_BLOCK_SIZE; + } + else + { + int block_size; + if (size * 8 > TRE_MEM_BLOCK_SIZE) + block_size = size * 8; + else + block_size = TRE_MEM_BLOCK_SIZE; + l = xmalloc(sizeof(*l)); + if (l == NULL) + { + mem->failed = 1; + return NULL; + } + l->data = xmalloc(block_size); + if (l->data == NULL) + { + xfree(l); + mem->failed = 1; + return NULL; + } + l->next = NULL; + if (mem->current != NULL) + mem->current->next = l; + if (mem->blocks == NULL) + mem->blocks = l; + mem->current = l; + mem->ptr = l->data; + mem->n = block_size; + } + } + /* Make sure the next pointer will be aligned. */ size += ALIGN(mem->ptr + size, long); + /* Allocate from current block. */ ptr = mem->ptr; mem->ptr += size; mem->n -= size; + /* Set to zero if needed. */ - if (zero) bzero(ptr, size); + if (zero) + memset(ptr, 0, size); + return ptr; } diff --git a/third_party/regex/tre.inc b/third_party/regex/tre.inc index a86cb46b7..b33e19973 100644 --- a/third_party/regex/tre.inc +++ b/third_party/regex/tre.inc @@ -1,79 +1,52 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╚──────────────────────────────────────────────────────────────────────────────╝ -│ │ -│ Musl Libc │ -│ Copyright © 2005-2014 Rich Felker, et al. │ -│ │ -│ Permission is hereby granted, free of charge, to any person obtaining │ -│ a copy of this software and associated documentation files (the │ -│ "Software"), to deal in the Software without restriction, including │ -│ without limitation the rights to use, copy, modify, merge, publish, │ -│ distribute, sublicense, and/or sell copies of the Software, and to │ -│ permit persons to whom the Software is furnished to do so, subject to │ -│ the following conditions: │ -│ │ -│ The above copyright notice and this permission notice shall be │ -│ included in all copies or substantial portions of the Software. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ -│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ -│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ -│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ -│ │ -│──────────────────────────────────────────────────────────────────────────────│ -│ │ -│ tre-internal.h - TRE internal definitions │ -│ │ -│ Copyright (c) 2001-2009 Ville Laurikari │ -│ All rights reserved. │ -│ │ -│ Redistribution and use in source and binary forms, with or without │ -│ modification, are permitted provided that the following conditions │ -│ are met: │ -│ │ -│ 1. Redistributions of source code must retain the above copyright │ -│ notice, this list of conditions and the following disclaimer. │ -│ │ -│ 2. Redistributions in binary form must reproduce the above copyright │ -│ notice, this list of conditions and the following disclaimer in │ -│ the documentation and/or other materials provided with the │ -│ distribution. │ -│ │ -│ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS │ -│ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT │ -│ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR │ -│ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT │ -│ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, │ -│ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT │ -│ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, │ -│ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY │ -│ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT │ -│ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE │ -│ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. │ -│ │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/mem/alg.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "libc/wctype.h" -#include "third_party/regex/regex.h" +/* + tre-internal.h - TRE internal definitions -#undef TRE_MBSTATE + Copyright (c) 2001-2009 Ville Laurikari + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#undef TRE_MBSTATE + +#ifndef NDEBUG +#define NDEBUG +#endif #define TRE_REGEX_T_FIELD __opaque typedef int reg_errcode_t; + typedef wchar_t tre_char_t; -#define DPRINT(msg) \ - do { \ - } while (0) +#define DPRINT(msg) do { } while(0) -#define elementsof(x) (sizeof(x) / sizeof(x[0])) +#define elementsof(x) ( sizeof(x) / sizeof(x[0]) ) #define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n))) @@ -81,17 +54,17 @@ typedef wchar_t tre_char_t; typedef wint_t tre_cint_t; #define TRE_CHAR_MAX 0x10ffff -#define tre_isalnum iswalnum -#define tre_isalpha iswalpha -#define tre_isblank iswblank -#define tre_iscntrl iswcntrl -#define tre_isdigit iswdigit -#define tre_isgraph iswgraph -#define tre_islower iswlower -#define tre_isprint iswprint -#define tre_ispunct iswpunct -#define tre_isspace iswspace -#define tre_isupper iswupper +#define tre_isalnum iswalnum +#define tre_isalpha iswalpha +#define tre_isblank iswblank +#define tre_iscntrl iswcntrl +#define tre_isdigit iswdigit +#define tre_isgraph iswgraph +#define tre_islower iswlower +#define tre_isprint iswprint +#define tre_ispunct iswpunct +#define tre_isspace iswspace +#define tre_isupper iswupper #define tre_isxdigit iswxdigit #define tre_tolower towlower @@ -105,10 +78,10 @@ typedef wctype_t tre_ctype_t; /* Returns number of bytes to add to (char *)ptr to make it properly aligned for the type. */ -#define ALIGN(ptr, type) \ - ((((long)ptr) % sizeof(type)) \ - ? (sizeof(type) - (((long)ptr) % sizeof(type))) \ - : 0) +#define ALIGN(ptr, type) \ + ((((long)ptr) % sizeof(type)) \ + ? (sizeof(type) - (((long)ptr) % sizeof(type))) \ + : 0) #undef MAX #undef MIN @@ -142,20 +115,24 @@ struct tnfa_transition { tre_ctype_t *neg_classes; }; + /* Assertions. */ -#define ASSERT_AT_BOL 1 /* Beginning of line. */ -#define ASSERT_AT_EOL 2 /* End of line. */ -#define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */ -#define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */ -#define ASSERT_AT_BOW 16 /* Beginning of word. */ -#define ASSERT_AT_EOW 32 /* End of word. */ -#define ASSERT_AT_WB 64 /* Word boundary. */ -#define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */ -#define ASSERT_BACKREF 256 /* A back reference in `backref'. */ -#define ASSERT_LAST 256 +#define ASSERT_AT_BOL 1 /* Beginning of line. */ +#define ASSERT_AT_EOL 2 /* End of line. */ +#define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */ +#define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */ +#define ASSERT_AT_BOW 16 /* Beginning of word. */ +#define ASSERT_AT_EOW 32 /* End of word. */ +#define ASSERT_AT_WB 64 /* Word boundary. */ +#define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */ +#define ASSERT_BACKREF 256 /* A back reference in `backref'. */ +#define ASSERT_LAST 256 /* Tag directions. */ -typedef enum { TRE_TAG_MINIMIZE = 0, TRE_TAG_MAXIMIZE = 1 } tre_tag_direction_t; +typedef enum { + TRE_TAG_MINIMIZE = 0, + TRE_TAG_MAXIMIZE = 1 +} tre_tag_direction_t; /* Instructions to compute submatch register values from tag values after a successful match. */ @@ -170,6 +147,7 @@ struct tre_submatch_data { typedef struct tre_submatch_data tre_submatch_data_t; + /* TNFA definition. */ typedef struct tnfa tre_tnfa_t; @@ -209,7 +187,7 @@ typedef struct tre_mem_struct { size_t n; int failed; void **provided; -} * tre_mem_t; +} *tre_mem_t; #define tre_mem_new_impl __tre_mem_new_impl #define tre_mem_alloc_impl __tre_mem_alloc_impl @@ -217,10 +195,10 @@ typedef struct tre_mem_struct { tre_mem_t tre_mem_new_impl(int provided, void *provided_block); void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, - int zero, size_t size); + int zero, size_t size); /* Returns a new memory allocator or NULL if out of memory. */ -#define tre_mem_new() tre_mem_new_impl(0, NULL) +#define tre_mem_new() tre_mem_new_impl(0, NULL) /* Allocates a block of `size' bytes from `mem'. Returns a pointer to the allocated block or NULL if an underlying malloc() failed. */ @@ -238,11 +216,18 @@ void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, #define tre_mem_newa() \ tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct))) -#define tre_mem_alloca(mem, size) \ - ((mem)->n >= (size) \ - ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \ - : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size))) +#define tre_mem_alloca(mem, size) \ + ((mem)->n >= (size) \ + ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \ + : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size))) #endif /* TRE_USE_ALLOCA */ + /* Frees the memory allocator and all memory allocated with it. */ void tre_mem_destroy(tre_mem_t mem); + +#define xmalloc malloc +#define xcalloc calloc +#define xfree free +#define xrealloc realloc + diff --git a/third_party/sed/BUILD.mk b/third_party/sed/BUILD.mk index 37cf771b4..cb708a5b8 100644 --- a/third_party/sed/BUILD.mk +++ b/third_party/sed/BUILD.mk @@ -23,6 +23,7 @@ THIRD_PARTY_SED_A_DIRECTDEPS = \ LIBC_STR \ LIBC_LOG \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_MUSL \ THIRD_PARTY_REGEX THIRD_PARTY_SED_A_DEPS := \ diff --git a/third_party/tz/asctime.c b/third_party/tz/asctime.c deleted file mode 100644 index eb257aa31..000000000 --- a/third_party/tz/asctime.c +++ /dev/null @@ -1,135 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" -#include "libc/stdio/stdio.h" -#include "private.h" - -/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */ - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -/* -** Avoid the temptation to punt entirely to strftime; -** the output of strftime is supposed to be locale specific -** whereas the output of asctime is supposed to be constant. -*/ - -/*LINTLIBRARY*/ - -/* -** All years associated with 32-bit time_t values are exactly four digits long; -** some years associated with 64-bit time_t values are not. -** Vintage programs are coded for years that are always four digits long -** and may assume that the newline always lands in the same place. -** For years that are less than four digits, we pad the output with -** leading zeroes to get the newline in the traditional place. -** The -4 ensures that we get four characters of output even if -** we call a strftime variant that produces fewer characters for some years. -** The ISO C and POSIX standards prohibit padding the year, -** but many implementations pad anyway; most likely the standards are buggy. -*/ -static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n"; -/* -** For years that are more than four digits we put extra spaces before the year -** so that code trying to overwrite the newline won't end up overwriting -** a digit within a year and truncating the year (operating on the assumption -** that no output is better than wrong output). -*/ -static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n"; - -enum { STD_ASCTIME_BUF_SIZE = 26 }; -/* -** Big enough for something such as -** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n -** (two three-character abbreviations, five strings denoting integers, -** seven explicit spaces, two explicit colons, a newline, -** and a trailing NUL byte). -** The values above are for systems where an int is 32 bits and are provided -** as an example; the size expression below is a bound for the system at -** hand. -*/ -static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1]; - -/* A similar buffer for ctime. - C89 requires that they be the same buffer. - This requirement was removed in C99, so support it only if requested, - as support is more likely to lead to bugs in badly written programs. */ -#if SUPPORT_C89 -# define buf_ctime buf_asctime -#else -static char buf_ctime[sizeof buf_asctime]; -#endif - -char * -asctime_r(struct tm const *restrict timeptr, char *restrict buf) -{ - static const char wday_name[][4] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char mon_name[][4] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - register const char * wn; - register const char * mn; - char year[INT_STRLEN_MAXIMUM(int) + 2]; - char result[sizeof buf_asctime]; - - if (timeptr == NULL) { - errno = EINVAL; - return strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); - } - if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) - wn = "???"; - else wn = wday_name[timeptr->tm_wday]; - if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) - mn = "???"; - else mn = mon_name[timeptr->tm_mon]; - /* - ** Use strftime's %Y to generate the year, to avoid overflow problems - ** when computing timeptr->tm_year + TM_YEAR_BASE. - ** Assume that strftime is unaffected by other out-of-range members - ** (e.g., timeptr->tm_mday) when processing "%Y". - */ - strftime(year, sizeof year, "%Y", timeptr); - /* - ** We avoid using snprintf since it's not available on all systems. - */ - sprintf(result, - ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), - wn, mn, - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec, - year); - if (strlen(result) < STD_ASCTIME_BUF_SIZE - || buf == buf_ctime || buf == buf_asctime) - return strcpy(buf, result); - else { - errno = EOVERFLOW; - return NULL; - } -} - -char * -asctime(register const struct tm *timeptr) -{ - return asctime_r(timeptr, buf_asctime); -} - -char * -ctime_r(const time_t *timep, char *buf) -{ - struct tm mytm; - struct tm *tmp = localtime_r(timep, &mytm); - return tmp ? asctime_r(tmp, buf) : NULL; -} - -char * -ctime(const time_t *timep) -{ - return ctime_r(timep, buf_ctime); -} diff --git a/third_party/tz/strftime.c b/third_party/tz/strftime.c deleted file mode 100644 index 372b98071..000000000 --- a/third_party/tz/strftime.c +++ /dev/null @@ -1,646 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright (c) 1989 The Regents of the University of California. │ -│ All rights reserved. │ -│ │ -│ Redistribution and use in source and binary forms are permitted │ -│ provided that the above copyright notice and this paragraph are │ -│ duplicated in all such forms and that any documentation, │ -│ advertising materials, and other materials related to such │ -│ distribution and use acknowledge that the software was developed │ -│ by the University of California, Berkeley. The name of the │ -│ University may not be used to endorse or promote products derived │ -│ from this software without specific prior written permission. │ -│ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR │ -│ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/locale.h" -#include "libc/time.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/inttypes.h" -#include "private.h" - -__notice(strftime_notice, "strftime (BSD-3)\n\ -Copyright 1989 The Regents of the University of California"); - -#ifndef DEPRECATE_TWO_DIGIT_YEARS -# define DEPRECATE_TWO_DIGIT_YEARS false -#endif - -struct lc_time_T { - const char * mon[MONSPERYEAR]; - const char * month[MONSPERYEAR]; - const char * wday[DAYSPERWEEK]; - const char * weekday[DAYSPERWEEK]; - const char * X_fmt; - const char * x_fmt; - const char * c_fmt; - const char * am; - const char * pm; - const char * date_fmt; -}; - -static const struct lc_time_T C_time_locale = { - { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }, { - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" - }, { - "Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat" - }, { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday" - }, - - /* X_fmt */ - "%H:%M:%S", - - /* - ** x_fmt - ** C99 and later require this format. - ** Using just numbers (as here) makes Quakers happier; - ** it's also compatible with SVR4. - */ - "%m/%d/%y", - - /* - ** c_fmt - ** C99 and later require this format. - ** Previously this code used "%D %X", but we now conform to C99. - ** Note that - ** "%a %b %d %H:%M:%S %Y" - ** is used by Solaris 2.3. - */ - "%a %b %e %T %Y", - - /* am */ - "AM", - - /* pm */ - "PM", - - /* date_fmt */ - "%a %b %e %H:%M:%S %Z %Y" -}; - -enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL }; - -#ifndef YEAR_2000_NAME -# define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" -#endif /* !defined YEAR_2000_NAME */ - -static char * -_add(const char *str, char *pt, const char *ptlim) -{ - while (pt < ptlim && (*pt = *str++) != '\0') - ++pt; - return pt; -} - -static char * -_conv(int n, const char *format, char *pt, const char *ptlim) -{ - char buf[INT_STRLEN_MAXIMUM(int) + 1]; - - sprintf(buf, format, n); - return _add(buf, pt, ptlim); -} - -/* -** POSIX and the C Standard are unclear or inconsistent about -** what %C and %y do if the year is negative or exceeds 9999. -** Use the convention that %C concatenated with %y yields the -** same output as %Y, and that %Y contains at least 4 bytes, -** with more only if necessary. -*/ - -static char * -_yconv(int a, int b, bool convert_top, bool convert_yy, - char *pt, const char *ptlim) -{ - register int lead; - register int trail; - - int DIVISOR = 100; - trail = a % DIVISOR + b % DIVISOR; - lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; - trail %= DIVISOR; - if (trail < 0 && lead > 0) { - trail += DIVISOR; - --lead; - } else if (lead < 0 && trail > 0) { - trail -= DIVISOR; - ++lead; - } - if (convert_top) { - if (lead == 0 && trail < 0) - pt = _add("-0", pt, ptlim); - else pt = _conv(lead, "%02d", pt, ptlim); - } - if (convert_yy) - pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); - return pt; -} - -static char * -_fmt(const char *format, const struct tm *t, char *pt, - const char *ptlim, enum warn *warnp) -{ - struct lc_time_T const *Locale = &C_time_locale; - - for ( ; *format; ++format) { - if (*format == '%') { -label: - switch (*++format) { - case '\0': - --format; - break; - case 'A': - pt = _add((t->tm_wday < 0 || - t->tm_wday >= DAYSPERWEEK) ? - "?" : Locale->weekday[t->tm_wday], - pt, ptlim); - continue; - case 'a': - pt = _add((t->tm_wday < 0 || - t->tm_wday >= DAYSPERWEEK) ? - "?" : Locale->wday[t->tm_wday], - pt, ptlim); - continue; - case 'B': - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? - "?" : Locale->month[t->tm_mon], - pt, ptlim); - continue; - case 'b': - case 'h': - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? - "?" : Locale->mon[t->tm_mon], - pt, ptlim); - continue; - case 'C': - /* - ** %C used to do a... - ** _fmt("%a %b %e %X %Y", t); - ** ...whereas now POSIX 1003.2 calls for - ** something completely different. - ** (ado, 1993-05-24) - */ - pt = _yconv(t->tm_year, TM_YEAR_BASE, - true, false, pt, ptlim); - continue; - case 'c': - { - enum warn warn2 = IN_SOME; - - pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); - if (warn2 == IN_ALL) - warn2 = IN_THIS; - if (warn2 > *warnp) - *warnp = warn2; - } - continue; - case 'D': - pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); - continue; - case 'd': - pt = _conv(t->tm_mday, "%02d", pt, ptlim); - continue; - case 'E': - case 'O': - /* - ** Locale modifiers of C99 and later. - ** The sequences - ** %Ec %EC %Ex %EX %Ey %EY - ** %Od %oe %OH %OI %Om %OM - ** %OS %Ou %OU %OV %Ow %OW %Oy - ** are supposed to provide alternative - ** representations. - */ - goto label; - case 'e': - pt = _conv(t->tm_mday, "%2d", pt, ptlim); - continue; - case 'F': - pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); - continue; - case 'H': - pt = _conv(t->tm_hour, "%02d", pt, ptlim); - continue; - case 'I': - pt = _conv((t->tm_hour % 12) ? - (t->tm_hour % 12) : 12, - "%02d", pt, ptlim); - continue; - case 'j': - pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); - continue; - case 'k': - /* - ** This used to be... - ** _conv(t->tm_hour % 12 ? - ** t->tm_hour % 12 : 12, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbins' - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 1993-05-24) - */ - pt = _conv(t->tm_hour, "%2d", pt, ptlim); - continue; -#ifdef KITCHEN_SINK - case 'K': - /* - ** After all this time, still unclaimed! - */ - pt = _add("kitchen sink", pt, ptlim); - continue; -#endif /* defined KITCHEN_SINK */ - case 'l': - /* - ** This used to be... - ** _conv(t->tm_hour, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbin's - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 1993-05-24) - */ - pt = _conv((t->tm_hour % 12) ? - (t->tm_hour % 12) : 12, - "%2d", pt, ptlim); - continue; - case 'M': - pt = _conv(t->tm_min, "%02d", pt, ptlim); - continue; - case 'm': - pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); - continue; - case 'n': - pt = _add("\n", pt, ptlim); - continue; - case 'p': - pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? - Locale->pm : - Locale->am, - pt, ptlim); - continue; - case 'R': - pt = _fmt("%H:%M", t, pt, ptlim, warnp); - continue; - case 'r': - pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); - continue; - case 'S': - pt = _conv(t->tm_sec, "%02d", pt, ptlim); - continue; - case 's': - { - struct tm tm; - char buf[INT_STRLEN_MAXIMUM( - time_t) + 1]; - time_t mkt; - - tm.tm_sec = t->tm_sec; - tm.tm_min = t->tm_min; - tm.tm_hour = t->tm_hour; - tm.tm_mday = t->tm_mday; - tm.tm_mon = t->tm_mon; - tm.tm_year = t->tm_year; -#ifdef TM_GMTOFF - mkt = timeoff(&tm, t->TM_GMTOFF); -#else - tm.tm_isdst = t->tm_isdst; - mkt = mktime(&tm); -#endif - /* If mktime fails, %s expands to the - value of (time_t) -1 as a failure - marker; this is better in practice - than strftime failing. */ - if (TYPE_SIGNED(time_t)) { - intmax_t n = mkt; - sprintf(buf, "%"PRIdMAX, n); - } else { - uintmax_t n = mkt; - sprintf(buf, "%"PRIuMAX, n); - } - pt = _add(buf, pt, ptlim); - } - continue; - case 'T': - pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); - continue; - case 't': - pt = _add("\t", pt, ptlim); - continue; - case 'U': - pt = _conv((t->tm_yday + DAYSPERWEEK - - t->tm_wday) / DAYSPERWEEK, - "%02d", pt, ptlim); - continue; - case 'u': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "ISO 8601: Weekday as a decimal number - ** [1 (Monday) - 7]" - ** (ado, 1993-05-24) - */ - pt = _conv((t->tm_wday == 0) ? - DAYSPERWEEK : t->tm_wday, - "%d", pt, ptlim); - continue; - case 'V': /* ISO 8601 week number */ - case 'G': /* ISO 8601 year (four digits) */ - case 'g': /* ISO 8601 year (two digits) */ -/* -** From Arnold Robbins' strftime version 3.0: "the week number of the -** year (the first Monday as the first day of week 1) as a decimal number -** (01-53)." -** (ado, 1993-05-24) -** -** From by Markus Kuhn: -** "Week 01 of a year is per definition the first week which has the -** Thursday in this year, which is equivalent to the week which contains -** the fourth day of January. In other words, the first week of a new year -** is the week which has the majority of its days in the new year. Week 01 -** might also contain days from the previous year and the week before week -** 01 of a year is the last week (52 or 53) of the previous year even if -** it contains days from the new year. A week starts with Monday (day 1) -** and ends with Sunday (day 7). For example, the first week of the year -** 1997 lasts from 1996-12-30 to 1997-01-05..." -** (ado, 1996-01-02) -*/ - { - int year; - int base; - int yday; - int wday; - int w; - - year = t->tm_year; - base = TM_YEAR_BASE; - yday = t->tm_yday; - wday = t->tm_wday; - for ( ; ; ) { - int len; - int bot; - int top; - - len = isleap_sum(year, base) ? - DAYSPERLYEAR : - DAYSPERNYEAR; - /* - ** What yday (-3 ... 3) does - ** the ISO year begin on? - */ - bot = ((yday + 11 - wday) % - DAYSPERWEEK) - 3; - /* - ** What yday does the NEXT - ** ISO year begin on? - */ - top = bot - - (len % DAYSPERWEEK); - if (top < -3) - top += DAYSPERWEEK; - top += len; - if (yday >= top) { - ++base; - w = 1; - break; - } - if (yday >= bot) { - w = 1 + ((yday - bot) / - DAYSPERWEEK); - break; - } - --base; - yday += isleap_sum(year, base) ? - DAYSPERLYEAR : - DAYSPERNYEAR; - } -#ifdef XPG4_1994_04_09 - if ((w == 52 && - t->tm_mon == TM_JANUARY) || - (w == 1 && - t->tm_mon == TM_DECEMBER)) - w = 53; -#endif /* defined XPG4_1994_04_09 */ - if (*format == 'V') - pt = _conv(w, "%02d", - pt, ptlim); - else if (*format == 'g') { - *warnp = IN_ALL; - pt = _yconv(year, base, - false, true, - pt, ptlim); - } else pt = _yconv(year, base, - true, true, - pt, ptlim); - } - continue; - case 'v': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "date as dd-bbb-YYYY" - ** (ado, 1993-05-24) - */ - pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); - continue; - case 'W': - pt = _conv((t->tm_yday + DAYSPERWEEK - - (t->tm_wday ? - (t->tm_wday - 1) : - (DAYSPERWEEK - 1))) / DAYSPERWEEK, - "%02d", pt, ptlim); - continue; - case 'w': - pt = _conv(t->tm_wday, "%d", pt, ptlim); - continue; - case 'X': - pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); - continue; - case 'x': - { - enum warn warn2 = IN_SOME; - - pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); - if (warn2 == IN_ALL) - warn2 = IN_THIS; - if (warn2 > *warnp) - *warnp = warn2; - } - continue; - case 'y': - *warnp = IN_ALL; - pt = _yconv(t->tm_year, TM_YEAR_BASE, - false, true, - pt, ptlim); - continue; - case 'Y': - pt = _yconv(t->tm_year, TM_YEAR_BASE, - true, true, - pt, ptlim); - continue; - case 'Z': -#ifdef TM_ZONE - pt = _add(t->TM_ZONE, pt, ptlim); -#elif HAVE_TZNAME - if (t->tm_isdst >= 0) - pt = _add(tzname[t->tm_isdst != 0], - pt, ptlim); -#endif - /* - ** C99 and later say that %Z must be - ** replaced by the empty string if the - ** time zone abbreviation is not - ** determinable. - */ - continue; - case 'z': -#if defined TM_GMTOFF || USG_COMPAT || ALTZONE - { - long diff; - char const * sign; - bool negative; - -# ifdef TM_GMTOFF - diff = t->TM_GMTOFF; -# else - /* - ** C99 and later say that the UT offset must - ** be computed by looking only at - ** tm_isdst. This requirement is - ** incorrect, since it means the code - ** must rely on magic (in this case - ** altzone and timezone), and the - ** magic might not have the correct - ** offset. Doing things correctly is - ** tricky and requires disobeying the standard; - ** see GNU C strftime for details. - ** For now, punt and conform to the - ** standard, even though it's incorrect. - ** - ** C99 and later say that %z must be replaced by - ** the empty string if the time zone is not - ** determinable, so output nothing if the - ** appropriate variables are not available. - */ - if (t->tm_isdst < 0) - continue; - if (t->tm_isdst == 0) -# if USG_COMPAT - diff = -timezone; -# else - continue; -# endif - else -# if ALTZONE - diff = -altzone; -# else - continue; -# endif -# endif - negative = diff < 0; - if (diff == 0) { -# ifdef TM_ZONE - negative = t->TM_ZONE[0] == '-'; -# else - negative = t->tm_isdst < 0; -# if HAVE_TZNAME - if (tzname[t->tm_isdst != 0][0] == '-') - negative = true; -# endif -# endif - } - if (negative) { - sign = "-"; - diff = -diff; - } else sign = "+"; - pt = _add(sign, pt, ptlim); - diff /= SECSPERMIN; - diff = (diff / MINSPERHOUR) * 100 + - (diff % MINSPERHOUR); - pt = _conv(diff, "%04d", pt, ptlim); - } -#endif - continue; - case '+': - pt = _fmt(Locale->date_fmt, t, pt, ptlim, - warnp); - continue; - case '%': - /* - ** X311J/88-090 (4.12.3.5): if conversion char is - ** undefined, behavior is undefined. Print out the - ** character itself as printf(3) also does. - */ - default: - break; - } - } - if (pt == ptlim) - break; - *pt++ = *format; - } - return pt; -} - -/** - * Converts time to string, e.g. - * - * char b[64]; - * int64_t sec; - * struct tm tm; - * time(&sec); - * localtime_r(&sec, &tm); - * strftime(b, sizeof(b), "%Y-%m-%dT%H:%M:%S%z", &tm); // ISO8601 - * strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123 - * - * @return bytes copied excluding nul, or 0 on error - * @see FormatHttpDateTime() - */ -size_t -strftime(char *restrict s, size_t maxsize, char const *restrict format, - struct tm const *restrict t) -{ - char * p; - int saved_errno = errno; - enum warn warn = IN_NONE; - - tzset(); - p = _fmt(format, t, s, s + maxsize, &warn); - if (!p) { - errno = EOVERFLOW; - return 0; - } - if (DEPRECATE_TWO_DIGIT_YEARS - && warn != IN_NONE && getenv(YEAR_2000_NAME)) { - fprintf(stderr, "\n"); - fprintf(stderr, "strftime format \"%s\" ", format); - fprintf(stderr, "yields only two digits of years in "); - if (warn == IN_SOME) - fprintf(stderr, "some locales"); - else if (warn == IN_THIS) - fprintf(stderr, "the current locale"); - else fprintf(stderr, "all locales"); - fprintf(stderr, "\n"); - } - if (p == s + maxsize) { - errno = ERANGE; - return 0; - } - *p = '\0'; - errno = saved_errno; - return p - s; -} - -__weak_reference(strftime, strftime_l); diff --git a/third_party/unzip/BUILD.mk b/third_party/unzip/BUILD.mk index b1db75282..a55ca2ff6 100644 --- a/third_party/unzip/BUILD.mk +++ b/third_party/unzip/BUILD.mk @@ -24,7 +24,8 @@ THIRD_PARTY_UNZIP_A_DIRECTDEPS = \ LIBC_STR \ LIBC_SYSV \ THIRD_PARTY_BZIP2 \ - THIRD_PARTY_TZ + THIRD_PARTY_MUSL \ + THIRD_PARTY_TZ \ THIRD_PARTY_UNZIP_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_UNZIP_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/zip/BUILD.mk b/third_party/zip/BUILD.mk index d0a163fc3..a2567c970 100644 --- a/third_party/zip/BUILD.mk +++ b/third_party/zip/BUILD.mk @@ -92,8 +92,9 @@ THIRD_PARTY_ZIP_DIRECTDEPS = \ LIBC_SYSV \ LIBC_X \ THIRD_PARTY_BZIP2 \ + THIRD_PARTY_MUSL \ THIRD_PARTY_TZ \ - THIRD_PARTY_ZLIB + THIRD_PARTY_ZLIB \ THIRD_PARTY_ZIP_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_ZIP_DIRECTDEPS),$($(x)))) diff --git a/tool/decode/BUILD.mk b/tool/decode/BUILD.mk index 00318107c..292a7c016 100644 --- a/tool/decode/BUILD.mk +++ b/tool/decode/BUILD.mk @@ -37,6 +37,7 @@ TOOL_DECODE_DIRECTDEPS = \ LIBC_X \ THIRD_PARTY_GDTOA \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_MUSL \ THIRD_PARTY_TZ \ THIRD_PARTY_XED \ TOOL_DECODE_LIB diff --git a/tool/lambda/BUILD.mk b/tool/lambda/BUILD.mk index a22b63aea..aca58f545 100644 --- a/tool/lambda/BUILD.mk +++ b/tool/lambda/BUILD.mk @@ -28,6 +28,7 @@ TOOL_LAMBDA_DIRECTDEPS = \ LIBC_SYSV \ LIBC_X \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_MUSL \ TOOL_LAMBDA_LIB TOOL_LAMBDA_DEPS := \