From 95173645a1af617730d266566f473e2ec4f2e3e0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 30 Jan 2021 08:54:12 -0800 Subject: [PATCH] Implement getcwd() for XNU --- examples/x86split.c | 24 +++++++++-- libc/calls/calls.h | 8 ++-- libc/calls/getcwd-nt.c | 4 +- libc/calls/getcwd-xnu.c | 57 +++++++++++++++++++++++++ libc/calls/getcwd.c | 30 ++++--------- libc/calls/hefty/get_current_dir_name.c | 1 - libc/calls/internal.h | 5 ++- libc/runtime/finddebugbinary.c | 39 +++++++++++------ libc/stdio/stdio.h | 2 - libc/str/bcmp.c | 2 +- libc/str/str.h | 4 +- libc/str/strcpy.c | 1 - libc/str/strcpy16.c | 2 +- libc/{stdio/fputhex.c => str/wcscat.c} | 17 +++++--- libc/{stdio/fgethex.c => str/wcscpy.c} | 30 ++++++------- test/libc/calls/getcwd_test.c | 55 ++++++++++++++++++++++++ test/libc/str/strcasecmp_test.c | 35 +++++++++++++++ 17 files changed, 239 insertions(+), 77 deletions(-) create mode 100644 libc/calls/getcwd-xnu.c rename libc/{stdio/fputhex.c => str/wcscat.c} (86%) rename libc/{stdio/fgethex.c => str/wcscpy.c} (82%) create mode 100644 test/libc/calls/getcwd_test.c create mode 100644 test/libc/str/strcasecmp_test.c diff --git a/examples/x86split.c b/examples/x86split.c index a5b423d52..a5bf01052 100644 --- a/examples/x86split.c +++ b/examples/x86split.c @@ -11,12 +11,31 @@ #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/errfuns.h" #include "third_party/xed/x86.h" /** * @fileoverview x86 instruction length decoder by way of hex pipe. */ +int fgethex(FILE *f) { + int o, t = -1; + while (!((o = fgetc(f)) & ~0xFF)) { + switch (t) { + case -1: + t = isxdigit(o) ? hextoint(o) : -1; + break; + default: + if (isxdigit(o)) { + return t * 16 + hextoint(o); + } + break; + } + } + if (t >= 0) return einval(); + return -1; +} + int main(int argc, char *argv[argc]) { unsigned c, i, j, l; enum XedError err; @@ -42,9 +61,8 @@ int main(int argc, char *argv[argc]) { l = xedd.length; if (l <= 0 || l > i) abort(); for (j = 0; j < l; ++j) { - if (fputhex(buf[j], stdout) == -1) { - return errno; - } + fputc("0123456789ABCDEF"[(buf[j] & 0xf0) >> 4], stdout); + fputc("0123456789ABCDEF"[(buf[j] & 0x0f) >> 0], stdout); } putchar('\n'); memcpy(&buf[0], &buf[l], i -= l); diff --git a/libc/calls/calls.h b/libc/calls/calls.h index ec73c2b3f..c57a840c1 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -232,10 +232,6 @@ int prctl(); int sysctl(const int *, unsigned, void *, size_t *, void *, size_t); int fchdir(int); -#define getcwd(BUF, SIZE) \ - (__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \ - : getcwd(BUF, SIZE)) - /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § system calls » formatting ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ @@ -248,6 +244,10 @@ int vdprintf(int, const char *, va_list) paramsnonnull(); ╚────────────────────────────────────────────────────────────────────────────│*/ #if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define getcwd(BUF, SIZE) \ + (__builtin_constant_p(BUF) && !(BUF) ? get_current_dir_name() \ + : getcwd(BUF, SIZE)) + void _init_onntconsoleevent(void); void _init_wincrash(void); diff --git a/libc/calls/getcwd-nt.c b/libc/calls/getcwd-nt.c index e0aba7fa8..5a2fcaa2e 100644 --- a/libc/calls/getcwd-nt.c +++ b/libc/calls/getcwd-nt.c @@ -23,12 +23,12 @@ #include "libc/sysv/errfuns.h" textwindows char *getcwd$nt(char *buf, size_t size) { - uint16_t name16[PATH_MAX + 1]; + uint16_t name16[PATH_MAX]; if (GetCurrentDirectory(ARRAYLEN(name16), name16)) { tprecode16to8(buf, size, name16); return buf; } else { __winerr(); + return NULL; } - return NULL; } diff --git a/libc/calls/getcwd-xnu.c b/libc/calls/getcwd-xnu.c new file mode 100644 index 000000000..c0029e65e --- /dev/null +++ b/libc/calls/getcwd-xnu.c @@ -0,0 +1,57 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/stat.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/errfuns.h" + +#define XNU_F_GETPATH 50 +#define XNU_MAXPATHLEN 1024 + +char *getcwd$xnu(char *res, size_t size) { + int fd; + struct stat st[2]; + char buf[XNU_MAXPATHLEN], *ret = NULL; + if ((fd = openat$sysv(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY)) != -1) { + if (fstat$sysv(fd, &st[0]) != -1) { + if (st[0].st_dev && st[0].st_ino) { + if (fcntl$sysv(fd, XNU_F_GETPATH, buf) != -1) { + if (fstatat$sysv(AT_FDCWD, buf, &st[1], 0) != -1) { + if (st[0].st_dev == st[1].st_dev && st[0].st_ino == st[1].st_ino) { + if (memccpy(res, buf, '\0', size)) { + ret = res; + } else { + erange(); + } + } else { + einval(); + } + } + } + } else { + einval(); + } + } + close(fd); + } + return ret; +} diff --git a/libc/calls/getcwd.c b/libc/calls/getcwd.c index bcb99a014..43f3c1a1e 100644 --- a/libc/calls/getcwd.c +++ b/libc/calls/getcwd.c @@ -19,8 +19,6 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/dce.h" -#include "libc/errno.h" -#include "libc/sysv/errfuns.h" /** * Returns current working directory. @@ -34,28 +32,16 @@ * @error ERANGE, EINVAL */ char *(getcwd)(char *buf, size_t size) { - if (buf) { - buf[0] = '\0'; - if (!IsWindows()) { - int olderr = errno; - if (getcwd$sysv(buf, size) != NULL) { - return buf; - } else if (IsXnu() && errno == ENOSYS) { - if (size >= 2) { - buf[0] = '.'; /* XXX: could put forth more effort */ - buf[1] = '\0'; - errno = olderr; - return buf; - } else { - erange(); - } - } - return NULL; + if (buf && size) buf[0] = '\0'; + if (!IsWindows()) { + if (IsXnu()) { + return getcwd$xnu(buf, size); + } else if (getcwd$sysv(buf, size) != (void *)-1) { + return buf; } else { - return getcwd$nt(buf, size); + return NULL; } } else { - efault(); - return NULL; + return getcwd$nt(buf, size); } } diff --git a/libc/calls/hefty/get_current_dir_name.c b/libc/calls/hefty/get_current_dir_name.c index de7142416..005973750 100644 --- a/libc/calls/hefty/get_current_dir_name.c +++ b/libc/calls/hefty/get_current_dir_name.c @@ -32,7 +32,6 @@ */ nodiscard char *get_current_dir_name(void) { char *buf, *res; - if ((res = getenv("PWD"))) return strdup(res); if (!(buf = malloc(PATH_MAX))) return NULL; if (!(res = (getcwd)(buf, PATH_MAX))) free(buf); return res; diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 4b44da8ad..7c0c89dd9 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -99,6 +99,7 @@ forceinline size_t clampio(size_t size) { ╚────────────────────────────────────────────────────────────────────────────│*/ char *getcwd$sysv(char *, u64) hidden; +char *getcwd$xnu(char *, u64) hidden; i32 __dup3$sysv(i32, i32, i32) hidden; i32 __execve$sysv(const char *, char *const[], char *const[]) hidden; i32 __fstat$sysv(i32, struct stat *) hidden; @@ -120,7 +121,7 @@ i32 fchmod$sysv(i32, u32) hidden; i32 fchmodat$sysv(i32, const char *, u32, u32) hidden; i32 fchown$sysv(i64, u32, u32) hidden; i32 fchownat$sysv(i32, const char *, u32, u32, u32) hidden; -i32 fcntl$sysv(i32, i32, i32) hidden; +i32 fcntl$sysv(i32, i32, ...) hidden; i32 fdatasync$sysv(i32) hidden; i32 flock$sysv(i32, i32) hidden; i32 fork$sysv(void) hidden; @@ -151,7 +152,7 @@ i32 mprotect$sysv(void *, u64, i32) hidden; i32 msync$sysv(void *, u64, i32) hidden; i32 munmap$sysv(void *, u64) hidden; i32 nanosleep$sysv(const struct timespec *, struct timespec *) hidden; -i32 openat$sysv(i32, const char *, i32, i32) hidden; +i32 openat$sysv(i32, const char *, i32, ...) hidden; i32 pause$sysv(void) hidden; i32 pipe$sysv(i32[hasatleast 2]) hidden; i32 pipe2$sysv(i32[hasatleast 2], u32) hidden; diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index 1c1ab0464..eb8fb05fe 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/bits/safemacros.h" #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/macros.h" @@ -31,19 +32,31 @@ * @return path to debug binary, or -1 w/ errno */ const char *FindDebugBinary(void) { - static char buf[PATH_MAX]; - if (buf[0]) return &buf[0]; - const char *const trybins[] = {program_invocation_name, - (const char *)getauxval(AT_EXECFN)}; - for (unsigned i = 0; i < ARRAYLEN(trybins); ++i) { - const char *res = trybins[i]; - unsigned len = strlen(res); - if (4 < len && len < sizeof(buf) - 5) { - if (strcmp(res + len - 4, ".dbg") == 0) return res; - /* try suffixing extension, e.g. .com → .com.dbg */ - memcpy(mempcpy(buf, res, len), ".dbg", 5); - if (fileexists(buf)) return &buf[0]; - buf[0] = '\0'; + unsigned i, len; + char buf[2][PATH_MAX]; + static char res[PATH_MAX]; + const char *bins[4], *pwd; + bins[0] = program_invocation_name; + bins[1] = (const char *)getauxval(AT_EXECFN); + pwd = emptytonull(getenv("PWD")); + for (i = 0; i < 2; ++i) { + if (pwd && bins[i] && bins[i][0] != '/' && bins[i][0] != '\\' && + strlen(pwd) + 1 + strlen(bins[i]) + 1 <= ARRAYLEN(buf[0])) { + strcpy(buf[i], pwd); + strcat(buf[i], "/"); + strcat(buf[i], bins[i]); + bins[i + 2] = buf[i]; + } + } + for (i = 0; i < 4; ++i) { + if (!bins[i]) continue; + len = strlen(bins[i]); + memcpy(res, bins[i], len + 1); + if (!endswith(res, ".dbg") && len + 3 + 1 <= ARRAYLEN(res)) { + strcat(res, ".dbg"); + } + if (fileexists(res)) { + return res; } } errno = ENOENT; diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index a0f4f1f18..7fc71fff4 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -52,8 +52,6 @@ int putchar(int); int puts(const char *) paramsnonnull(); ssize_t getline(char **, size_t *, FILE *) paramsnonnull(); ssize_t getdelim(char **, size_t *, int, FILE *) paramsnonnull(); -int fputhex(int, FILE *) paramsnonnull(); -int fgethex(FILE *) paramsnonnull(); FILE *fopen(const char *, const char *) paramsnonnull() nodiscard; FILE *fdopen(int, const char *) paramsnonnull() nodiscard; FILE *fmemopen(void *, size_t, const char *) paramsnonnull((3)) nodiscard; diff --git a/libc/str/bcmp.c b/libc/str/bcmp.c index 6bf0ba8a6..1731be191 100644 --- a/libc/str/bcmp.c +++ b/libc/str/bcmp.c @@ -24,7 +24,7 @@ * This API was thought to be nearly extinct until recent versions * of Clang (c. 2019) started generating synthetic calls to it. * - * @return unsigned char subtraction at stop index + * @return 0 if a and b have equal contents, otherwise non-zero * @asyncsignalsafe */ int bcmp(const void *a, const void *b, size_t n) { diff --git a/libc/str/str.h b/libc/str/str.h index 9a6dbdc58..81d3e6e7d 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -151,11 +151,13 @@ int strcasecmpzbw(const uint16_t *, const char *) strlenesque; char *stpcpy(char *, const char *) memcpyesque; char *stpncpy(char *, const char *, size_t) memcpyesque; char *strcat(char *, const char *) memcpyesque; -char16_t *strcat16(char16_t *, const char16_t *); +char16_t *strcat16(char16_t *, const char16_t *) memcpyesque; +wchar_t *wcscat(wchar_t *, const wchar_t *) memcpyesque; size_t strlcpy(char *, const char *, size_t); size_t strlcat(char *, const char *, size_t); char *strcpy(char *, const char *) memcpyesque; char16_t *strcpy16(char16_t *, const char16_t *) memcpyesque; +wchar_t *wcscpy(wchar_t *, const wchar_t *) memcpyesque; char *strncat(char *, const char *, size_t) memcpyesque; char *strncpy(char *, const char *, size_t) memcpyesque; char *strtok(char *, const char *) paramsnonnull((2)) libcesque; diff --git a/libc/str/strcpy.c b/libc/str/strcpy.c index e3e81aeb1..723caa33f 100644 --- a/libc/str/strcpy.c +++ b/libc/str/strcpy.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/pcmpeqb.h" #include "libc/intrin/pmovmskb.h" -#include "libc/limits.h" #include "libc/str/str.h" /** diff --git a/libc/str/strcpy16.c b/libc/str/strcpy16.c index 03d4b58bc..0b014ff6d 100644 --- a/libc/str/strcpy16.c +++ b/libc/str/strcpy16.c @@ -28,5 +28,5 @@ * @return original dest */ char16_t *strcpy16(char16_t *dest, const char16_t *src) { - return memcpy(dest, src, (strlen16(src) + 1) << 1); + return memcpy(dest, src, (strlen16(src) + 1) * sizeof(char16_t)); } diff --git a/libc/stdio/fputhex.c b/libc/str/wcscat.c similarity index 86% rename from libc/stdio/fputhex.c rename to libc/str/wcscat.c index 26cd845e7..7f5ce765b 100644 --- a/libc/stdio/fputhex.c +++ b/libc/str/wcscat.c @@ -16,11 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/stdio.h" +#include "libc/str/str.h" -int fputhex(int c, FILE *f) { - return (fputc("0123456789ABCDEF"[(c / 16) & 0xF], f) >= 0 && - fputc("0123456789ABCDEF"[(c % 16) & 0xF], f) >= 0) - ? c - : -1; +/** + * Appends 𝑠 to 𝑑. + * + * @param 𝑑 is a NUL-terminated 32-bit string + * @param 𝑠 is a NUL-terminated 32-bit string + * @return 𝑑 + * @asyncsignalsafe + */ +wchar_t *wcscat(wchar_t *d, const wchar_t *s) { + return wcscpy(d + wcslen(d), s); } diff --git a/libc/stdio/fgethex.c b/libc/str/wcscpy.c similarity index 82% rename from libc/stdio/fgethex.c rename to libc/str/wcscpy.c index e43a03e89..ac9b3ab1c 100644 --- a/libc/stdio/fgethex.c +++ b/libc/str/wcscpy.c @@ -16,24 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/stdio.h" #include "libc/str/str.h" -#include "libc/sysv/errfuns.h" -int fgethex(FILE *f) { - int o, t = -1; - while (!((o = fgetc(f)) & ~0xFF)) { - switch (t) { - case -1: - t = isxdigit(o) ? hextoint(o) : -1; - break; - default: - if (isxdigit(o)) { - return t * 16 + hextoint(o); - } - break; - } - } - if (t >= 0) return einval(); - return -1; +/** + * Copies NUL-terminated wide character string. + * + * 𝑑 and 𝑠 must not overlap unless 𝑑 ≤ 𝑠. + * + * @param 𝑑 is destination memory + * @param 𝑠 is a NUL-terminated string + * @return original dest + * @asyncsignalsafe + */ +wchar_t *wcscpy(wchar_t *d, const wchar_t *s) { + return memcpy(d, s, (wcslen(s) + 1) * sizeof(wchar_t)); } diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c new file mode 100644 index 000000000..e7dcee02e --- /dev/null +++ b/test/libc/calls/getcwd_test.c @@ -0,0 +1,55 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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/calls/calls.h" +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/fmt.h" +#include "libc/log/check.h" +#include "libc/macros.h" +#include "libc/runtime/gc.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +char basedir[PATH_MAX]; +char testdir[PATH_MAX]; + +void SetUp(void) { + getcwd(basedir, ARRAYLEN(basedir)); + sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); + makedirs(testdir, 0755); + CHECK_NE(-1, chdir(testdir)); +} + +void TearDown(void) { + CHECK_NE(-1, chdir(basedir)); + CHECK_NE(-1, rmrf(testdir)); +} + +TEST(getcwd, test) { + char buf[PATH_MAX]; + EXPECT_NE(-1, mkdir("subdir", 0755)); + EXPECT_NE(-1, chdir("subdir")); + EXPECT_STREQ("subdir", basename(getcwd(buf, ARRAYLEN(buf)))); +} + +TEST(getcwd, testNullBuf_allocatesResult) { + EXPECT_NE(-1, mkdir("subdir", 0755)); + EXPECT_NE(-1, chdir("subdir")); + EXPECT_STREQ("subdir", basename(gc(getcwd(NULL, 0)))); +} diff --git a/test/libc/str/strcasecmp_test.c b/test/libc/str/strcasecmp_test.c new file mode 100644 index 000000000..e3d91c005 --- /dev/null +++ b/test/libc/str/strcasecmp_test.c @@ -0,0 +1,35 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 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" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +TEST(strcasecmp, test) { + EXPECT_EQ(0, strcasecmp("HELLO", "hello")); + EXPECT_EQ(-17, strcasecmp("HELLO", "yello")); + EXPECT_EQ(-17, strcasecmp("HELLO", "YELLO")); + EXPECT_EQ(+17, strcasecmp("yello", "HELLO")); + EXPECT_EQ(+17, strcasecmp("YELLO", "HELLO")); +} + +BENCH(strcasecmp, bench) { + EZBENCH2("strcasecmp 16 eq", donothing, + EXPROPRIATE( + strcasecmp(VEIL("r", "abcdefghijklmnop"), "ABCDEFGHIJKLMNOP"))); +}