From 04d39d47f112ec0189f17ddd5525f4b302d9052b Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 6 Apr 2022 00:13:44 -0700 Subject: [PATCH] Fix basename() and dirname() --- examples/ispell.c | 4 +- examples/nesemu1.cc | 2 +- libc/calls/lseek-nt.c | 16 ++- libc/calls/open-nt.c | 23 ++-- libc/calls/openanon.c | 4 +- libc/calls/program_executable_name.c | 1 + libc/fmt/basename.c | 35 +++++- libc/fmt/basename_n.c | 49 -------- libc/fmt/conv.h | 18 ++- libc/fmt/dirname.c | 50 ++++---- libc/fmt/isslash.internal.h | 10 -- libc/intrin/kdos2errno.S | 1 + libc/mem/get_current_dir_name.c | 1 + libc/stdio/fgets.c | 2 +- libc/stdio/getdelim.c | 2 +- libc/stdio/getline.c | 2 +- libc/str/chomp.c | 2 +- libc/str/chomp16.c | 13 +- libc/str/classifypath.c | 154 +++++++++++++++++++++++ libc/str/escapedos.c | 4 +- libc/str/isabspath.c | 34 ++--- libc/{fmt/isabspath.c => str/isdirsep.c} | 23 +--- libc/str/path.h | 20 +++ libc/str/str.h | 21 ++-- libc/str/wchomp.c | 13 +- libc/x/xdirname.c | 3 +- libc/x/xgetline.c | 4 +- libc/x/xjoinpaths.c | 1 + net/https/logcertificate.c | 2 +- test/libc/calls/mkdir_test.c | 4 +- test/libc/fmt/basename_test.c | 32 +++-- test/libc/fmt/dirname_test.c | 5 +- test/libc/str/classifypath_test.c | 116 +++++++++++++++++ third_party/quickjs/quickjs-libc.c | 10 +- tool/build/pstrace.c | 2 +- tool/decode/scrubdox.c | 2 +- tool/emacs/cosmo-stuff.el | 3 + tool/viz/ntmaster.c | 2 +- tool/viz/rgbtoxterm.c | 2 +- tool/viz/tabalign.c | 2 +- tool/viz/xterm256effective2.c | 2 +- 41 files changed, 489 insertions(+), 207 deletions(-) delete mode 100644 libc/fmt/basename_n.c delete mode 100644 libc/fmt/isslash.internal.h create mode 100644 libc/str/classifypath.c rename libc/{fmt/isabspath.c => str/isdirsep.c} (75%) create mode 100644 libc/str/path.h create mode 100644 test/libc/str/classifypath_test.c diff --git a/examples/ispell.c b/examples/ispell.c index e8e72746c..0a90fef64 100644 --- a/examples/ispell.c +++ b/examples/ispell.c @@ -121,7 +121,7 @@ void SpellChecker(void) { printf("word: "); fflush(stdout); if (getline(&line, &linesize, stdin) > 0) { - query = strtolower(chomp(line)); + query = strtolower(_chomp(line)); if (critbit0_contains(&words, query)) { printf("ok\r\n"); } else { @@ -147,7 +147,7 @@ void SpellChecker(void) { void LoadWords(void) { CHECK_NOTNULL((f = fopen("/zip/usr/share/dict/words", "r"))); while (getline(&line, &linesize, f) > 0) { - critbit0_insert(&words, strtolower(chomp(line))); + critbit0_insert(&words, strtolower(_chomp(line))); } CHECK_NE(-1, fclose(f)); } diff --git a/examples/nesemu1.cc b/examples/nesemu1.cc index 59d23afa2..f195eb508 100644 --- a/examples/nesemu1.cc +++ b/examples/nesemu1.cc @@ -1673,7 +1673,7 @@ char* GetLine(void) { static char* line; static size_t linesize; if (getline(&line, &linesize, stdin) > 0) { - return chomp(line); + return _chomp(line); } else { return NULL; } diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index dc1954787..da27f64cf 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -17,15 +17,23 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/sysv/errfuns.h" textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) { int64_t res; - if (!__isfdkind(fd, kFdFile)) return ebadf(); - if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) { - return res; + if (__isfdkind(fd, kFdFile)) { + if (GetFileType(g_fds.p[fd].handle) != kNtFileTypePipe) { + if (SetFilePointerEx(g_fds.p[fd].handle, offset, &res, whence)) { + return res; + } else { + return __winerr(); + } + } else { + return espipe(); + } } else { - return __winerr(); + return ebadf(); } } diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 5a1b0f707..f3beff7eb 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -53,8 +53,8 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, uint32_t flags, int32_t mode) { - uint32_t br; int64_t handle; + uint32_t br, err; char16_t path16[PATH_MAX]; uint32_t perm, share, disp, attr; if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1; @@ -96,6 +96,18 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, } else { attr = kNtFileAttributeNormal; if (flags & _O_DIRECTORY) attr |= kNtFileFlagBackupSemantics; + if (~mode & 0200) { + attr |= kNtFileAttributeReadonly; + if (!IsTiny() && disp == kNtCreateAlways) { + // iron out esoteric unix/win32 inconsistency (golang #38225) + if ((handle = CreateFile(path16, perm, share, &kNtIsInheritable, + kNtTruncateExisting, kNtFileAttributeNormal, + 0)) != -1 || + (errno != ENOENT && errno != ENOTDIR)) { + return handle; + } + } + } } flags |= kNtFileFlagOverlapped; if (~flags & _O_INDEXED) attr |= kNtFileAttributeNotContentIndexed; @@ -105,14 +117,7 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering; if (flags & _O_NDELAY) attr |= kNtFileFlagWriteThrough; - if ((handle = CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, - 0)) != -1) { - } else if (GetLastError() == kNtErrorFileExists && - ((flags & _O_CREAT) && - (flags & _O_TRUNC))) { /* TODO(jart): What was this? */ - handle = eisdir(); - } - return handle; + return CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, 0); } static textwindows ssize_t sys_open_nt_console(int dirfd, diff --git a/libc/calls/openanon.c b/libc/calls/openanon.c index 056f68653..aa36cf475 100644 --- a/libc/calls/openanon.c +++ b/libc/calls/openanon.c @@ -21,13 +21,13 @@ #include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/fmt/conv.h" -#include "libc/fmt/isslash.internal.h" #include "libc/fmt/itoa.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/creationdisposition.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filesharemode.h" +#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" @@ -44,7 +44,7 @@ static void openanon_genpath(const char *name, struct OpenAnon *state, if (!name) name = "openanon"; for (i = 0; p < pe; ++i) { if (!(c = name[i])) break; - if (isslash(c)) c = '_'; + if (_isdirsep(c)) c = '_'; *p++ = c; } *p++ = '.'; diff --git a/libc/calls/program_executable_name.c b/libc/calls/program_executable_name.c index 81262e2a6..8845cac64 100644 --- a/libc/calls/program_executable_name.c +++ b/libc/calls/program_executable_name.c @@ -29,6 +29,7 @@ #include "libc/mem/alloca.h" #include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" +#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/str/utf16.h" diff --git a/libc/fmt/basename.c b/libc/fmt/basename.c index d1191354c..4c9671490 100644 --- a/libc/fmt/basename.c +++ b/libc/fmt/basename.c @@ -17,19 +17,42 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/fmt/isslash.internal.h" +#include "libc/str/path.h" #include "libc/str/str.h" /** - * Returns pointer to last filename component in path. + * Returns pointer to last filename component in path, e.g. + * + * path │ dirname() │ basename() + * ───────────────────────────────── + * . │ . │ . + * .. │ . │ .. + * / │ / │ / + * usr │ . │ usr + * /usr/ │ / │ usr + * /usr/lib │ /usr │ lib * * Both / and \ are are considered valid component separators on all * platforms. Trailing slashes are ignored. We don't grant special * consideration to things like foo/., c:/, \\?\Volume, etc. * - * @param path is NUL-terminated UTF-8 path - * @return pointer inside path or path itself + * @param path is UTF-8 and may be mutated, but not expanded in length + * @return pointer to path, or inside path, or to a special r/o string + * @see dirname() + * @see SUSv2 */ -textstartup char *basename(const char *path) { - return basename_n(path, strlen(path)); +char *basename(char *path) { + size_t i; + if (path && *path) { + i = strlen(path) - 1; + for (; i && _isdirsep(path[i]); i--) { + path[i] = 0; + } + for (; i && !_isdirsep(path[i - 1]);) { + i--; + } + return path + i; + } else { + return "."; + } } diff --git a/libc/fmt/basename_n.c b/libc/fmt/basename_n.c deleted file mode 100644 index c7570e2d5..000000000 --- a/libc/fmt/basename_n.c +++ /dev/null @@ -1,49 +0,0 @@ -/*-*- 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 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/fmt/conv.h" -#include "libc/fmt/isslash.internal.h" - -/** - * Returns pointer to last filename component in path. - * - * Both / and \ are are considered valid component separators on all - * platforms. Trailing slashes are ignored. We don't grant special - * consideration to things like foo/., c:/, \\?\Volume, etc. - * - * @param path is UTF-8 path - * @param size is byte length of path - * @return pointer inside path or path itself - */ -textstartup char *basename_n(const char *path, size_t size) { - size_t i, l; - if (size) { - if (isslash(path[size - 1])) { - l = size - 1; - while (l && isslash(path[l - 1])) --l; - if (!l) return (/*unconst*/ char *)&path[size - 1]; - size = l; - } - for (i = size; i > 0; --i) { - if (isslash(path[i - 1])) { - return (/*unconst*/ char *)&path[i]; - } - } - } - return (/*unconst*/ char *)path; -} diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index 834275b61..c16567951 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -42,13 +42,13 @@ size_t wcsxfrm(wchar_t *, const wchar_t *, size_t); │ cosmopolitan § conversion » time ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -int64_t DosDateTimeToUnix(unsigned, unsigned) dontthrow; -struct timeval WindowsTimeToTimeVal(int64_t) dontthrow; -struct timespec WindowsTimeToTimeSpec(int64_t) dontthrow; -int64_t TimeSpecToWindowsTime(struct timespec) dontthrow; -int64_t TimeValToWindowsTime(struct timeval) dontthrow; -struct timeval WindowsDurationToTimeVal(int64_t) dontthrow; -struct timespec WindowsDurationToTimeSpec(int64_t) dontthrow; +int64_t DosDateTimeToUnix(unsigned, unsigned) libcesque nosideeffect; +struct timeval WindowsTimeToTimeVal(int64_t) libcesque nosideeffect; +struct timespec WindowsTimeToTimeSpec(int64_t) libcesque nosideeffect; +int64_t TimeSpecToWindowsTime(struct timespec) libcesque nosideeffect; +int64_t TimeValToWindowsTime(struct timeval) libcesque nosideeffect; +struct timeval WindowsDurationToTimeVal(int64_t) libcesque nosideeffect; +struct timespec WindowsDurationToTimeSpec(int64_t) libcesque nosideeffect; static inline struct NtFileTime MakeFileTime(int64_t x) { return (struct NtFileTime){(uint32_t)x, (uint32_t)(x >> 32)}; @@ -69,9 +69,7 @@ static inline int64_t ReadFileTime(struct NtFileTime t) { ╚────────────────────────────────────────────────────────────────────────────│*/ char *dirname(char *); -char *basename(const char *) nosideeffect; -char *basename_n(const char *, size_t) nosideeffect; -bool isabspath(const char *) paramsnonnull() nosideeffect; +char *basename(char *); char *stripext(char *); char *stripexts(char *); diff --git a/libc/fmt/dirname.c b/libc/fmt/dirname.c index 7810829a8..234f7f111 100644 --- a/libc/fmt/dirname.c +++ b/libc/fmt/dirname.c @@ -17,34 +17,42 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" +#include "libc/str/path.h" #include "libc/str/str.h" -#define ISSLASH(c) (c == '/' || c == '\\') -#define ISDELIM(c) (ISSLASH(c) || c == '.') - /** - * Returns directory portion of path. + * Returns directory portion of path, e.g. * - * This returns "." if path doesn't have slashes. If path is empty then - * this returns empty string. + * path │ dirname() │ basename() + * ───────────────────────────────── + * . │ . │ . + * .. │ . │ .. + * / │ / │ / + * usr │ . │ usr + * /usr/ │ / │ usr + * /usr/lib │ /usr │ lib * - * @param s is mutated and must not be NULL + * @param path is UTF-8 and may be mutated, but not expanded in length + * @return pointer to path, or inside path, or to a special r/o string + * @see basename() + * @see SUSv2 */ -char *dirname(char *s) { - size_t i, n; - if (!(n = strlen(s))) return s; - while (n && ISDELIM(s[n - 1])) --n; - if (n) { - while (n && !ISSLASH(s[n - 1])) --n; - if (n) { - while (n && ISDELIM(s[n - 1])) --n; - if (!n) ++n; - } else { - s[n++] = '.'; +char *dirname(char *path) { + size_t i; + if (path && *path) { + i = strlen(path) - 1; + for (; _isdirsep(path[i]); i--) { + if (!i) return "/"; } + for (; !_isdirsep(path[i]); i--) { + if (!i) return "."; + } + for (; _isdirsep(path[i]); i--) { + if (!i) return "/"; + } + path[i + 1] = 0; + return path; } else { - ++n; + return "."; } - s[n] = '\0'; - return s; } diff --git a/libc/fmt/isslash.internal.h b/libc/fmt/isslash.internal.h deleted file mode 100644 index 4ee549e46..000000000 --- a/libc/fmt/isslash.internal.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_FMT_ISSLASH_H_ -#define COSMOPOLITAN_LIBC_FMT_ISSLASH_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) - -forceinline bool isslash(int c) { - return c == '/' || c == '\\'; -} - -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_FMT_ISSLASH_H_ */ diff --git a/libc/intrin/kdos2errno.S b/libc/intrin/kdos2errno.S index c8eb4354c..9976cda72 100644 --- a/libc/intrin/kdos2errno.S +++ b/libc/intrin/kdos2errno.S @@ -36,6 +36,7 @@ kDos2Errno: .e kNtErrorBadNetName,ENOENT .e kNtErrorBadNetResp,ENETDOWN .e kNtErrorBadPathname,ENOENT + .e kNtErrorFileExists,EEXIST .e kNtErrorCannotMake,EACCES .e kNtErrorCommitmentLimit,ENOMEM .e kNtErrorConnectionAborted,ECONNABORTED diff --git a/libc/mem/get_current_dir_name.c b/libc/mem/get_current_dir_name.c index 7c00f569a..403bffb0c 100644 --- a/libc/mem/get_current_dir_name.c +++ b/libc/mem/get_current_dir_name.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" +#include "libc/str/path.h" /** * Returns current working directory. diff --git a/libc/stdio/fgets.c b/libc/stdio/fgets.c index 195880e62..7e7d77427 100644 --- a/libc/stdio/fgets.c +++ b/libc/stdio/fgets.c @@ -25,7 +25,7 @@ * * This function is similar to getline() except it'll truncate lines * exceeding size. The line ending marker is included and may be removed - * using chomp(). + * using _chomp(). */ char *fgets(char *s, int size, FILE *f) { int c; diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index 28001bc47..d936d05d3 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -36,7 +36,7 @@ * @return number of bytes read >0, including delim, excluding NUL, * or -1 w/ errno on EOF or error; see ferror() and feof() * @note this function can't punt EINTR to caller - * @see getline(), chomp(), gettok_r() + * @see getline(), _chomp(), gettok_r() */ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) { char *p; diff --git a/libc/stdio/getline.c b/libc/stdio/getline.c index 4a5c2594d..6f0fd43b8 100644 --- a/libc/stdio/getline.c +++ b/libc/stdio/getline.c @@ -23,7 +23,7 @@ * * This function delegates to getdelim(), which provides further * documentation. Concerning lines, please note the \n or \r\n are - * included in results, and can be removed with chomp(). + * included in results, and can be removed with _chomp(). * * @param line is the caller's buffer (in/out) which is extended * automatically. *line may be NULL but only if *n is 0; diff --git a/libc/str/chomp.c b/libc/str/chomp.c index 41c97b371..d1abfd5a2 100644 --- a/libc/str/chomp.c +++ b/libc/str/chomp.c @@ -24,7 +24,7 @@ * @param line is NULL-propagating * @see getline */ -char *chomp(char *line) { +char *_chomp(char *line) { size_t i; if (line) { for (i = strlen(line); i--;) { diff --git a/libc/str/chomp16.c b/libc/str/chomp16.c index cdd558acd..5b777af0c 100644 --- a/libc/str/chomp16.c +++ b/libc/str/chomp16.c @@ -24,7 +24,16 @@ * @param line is NULL-propagating * @see getline */ -char16_t *chomp16(char16_t *line) { - if (line) line[strcspn16(line, u"\r\n")] = '\0'; +char16_t *_chomp16(char16_t *line) { + size_t i; + if (line) { + for (i = strlen16(line); i--;) { + if (line[i] == '\r' || line[i] == '\n') { + line[i] = '\0'; + } else { + break; + } + } + } return line; } diff --git a/libc/str/classifypath.c b/libc/str/classifypath.c new file mode 100644 index 000000000..9f26cc410 --- /dev/null +++ b/libc/str/classifypath.c @@ -0,0 +1,154 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/str/path.h" +#include "libc/str/str.h" + +/** + * Classifies file path name. + * + * For the purposes of this function, we always consider backslash + * interchangeable with forward slash, even though the underlying + * operating system might not. Therefore, for the sake of clarity, + * remaining documentation will only use the forward slash. + * + * This function behaves the same on all platforms. For instance, this + * function will categorize `C:/FOO.BAR` as a DOS path, even if you're + * running on UNIX rather than DOS. + * + * If you wish to check if a pathname is absolute, in a manner that's + * inclusive of DOS drive paths, DOS rooted paths, in addition to the + * New Technology UNC paths, then you may do the following: + * + * if (_classifypath(str) & _PATH_ABS) { ... } + * + * To check if path is a relative path: + * + * if (~_classifypath(str) & _PATH_ABS) { ... } + * + * Please note the above check includes rooted paths such as `\foo` + * which is considered absolute by MSDN and we consider it absolute + * although, it's technically relative to the current drive letter. + * + * Please note that `/foo/bar` is an absolute path on Windows, even + * though it's actually a rooted path that's considered relative to + * current drive by WIN32. + * + * @return integer value that's one of following: + * - `0` if non-weird relative path e.g. `c` + * - `_PATH_ABS` if absolute (or rooted dos) path e.g. `/⋯` + * - `_PATH_DOS` if `c:`, `d:foo` i.e. drive-relative path + * - `_PATH_ABS|_PATH_DOS` if proper dos path e.g. `c:/foo` + * - `_PATH_DOS|_PATH_DEV` if dos device path e.g. `nul`, `conin$` + * - `_PATH_ABS|_PATH_WIN` if `//c`, `//?c`, etc. + * - `_PATH_ABS|_PATH_WIN|_PATH_DEV` if `//./⋯`, `//?/⋯` + * - `_PATH_ABS|_PATH_WIN|_PATH_DEV|_PATH_ROOT` if `//.` or `//?` + * - `_PATH_ABS|_PATH_NT` e.g. `\??\\⋯` (undoc. strict backslash) + * @see "The Definitive Guide on Win32 to NT Path Conversion", James + * Forshaw, Google Project Zero Blog, 2016-02-29 + * @see "Naming Files, Paths, and Namespaces", MSDN 01/04/2021 + */ +int _classifypath(const char *s) { + if (s) { + switch (s[0]) { + case 0: // "" + return 0; + default: + if (!SupportsWindows()) { + return 0; + } + if (((((s[0] == 'a' || s[0] == 'A') && // aux + (s[1] == 'u' || s[1] == 'U') && // + (s[2] == 'x' || s[2] == 'X')) || // + ((s[0] == 'p' || s[0] == 'P') && // prn + (s[1] == 'r' || s[1] == 'R') && // + (s[2] == 'n' || s[2] == 'N')) || // + ((s[0] == 'n' || s[0] == 'N') && // nul + (s[1] == 'u' || s[1] == 'U') && // + (s[2] == 'l' || s[2] == 'L')) || // + ((s[0] == 'c' || s[0] == 'C') && // con + (s[1] == 'o' || s[1] == 'O') && // + (s[2] == 'n' || s[2] == 'N'))) && // + !s[3]) || + ((((s[0] == 'l' || s[0] == 'L') && // lpt + (s[1] == 'p' || s[1] == 'P') && // + (s[2] == 't' || s[2] == 'T')) || // + ((s[0] == 'c' || s[0] == 'C') && // com + (s[1] == 'o' || s[1] == 'O') && // + (s[2] == 'm' || s[2] == 'M'))) && // + ('1' <= s[3] && s[3] <= '9') && // + !s[4])) { + return _PATH_DOS | _PATH_DEV; + } + switch (s[1]) { + case ':': + switch (s[2]) { + case 0: // c: + default: // c:wut⋯ + return _PATH_DOS; + case '/': // c:/⋯ + case '\\': // c:\⋯ + return _PATH_ABS | _PATH_DOS; + } + default: + return 0; + } + case '\\': + if (SupportsWindows()) { + if (s[1] == '?' && s[2] == '?') { + if (!s[3]) { + return _PATH_ABS | _PATH_NT | _PATH_ROOT; // \??\⋯ + } else if (s[3] == '\\') { + return _PATH_ABS | _PATH_NT; // \??\⋯ + } + } + } + // fallthrough + case '/': + if (!SupportsWindows()) { + return _PATH_ABS; + } + switch (s[1]) { + case 0: // / + default: // /⋯ + return _PATH_ABS; + case '/': + case '\\': + switch (s[2]) { + case 0: // // + default: // //⋯ + return _PATH_ABS | _PATH_WIN; + case '.': + case '?': + switch (s[3]) { + case 0: // //? or //. + return _PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT; + default: // //?⋯ or //.⋯ + return _PATH_ABS | _PATH_WIN; + case '/': + case '\\': // //?/⋯ or //./⋯ + return _PATH_ABS | _PATH_WIN | _PATH_DEV; + } + } + } + } + } else { + return 0; + } +} diff --git a/libc/str/escapedos.c b/libc/str/escapedos.c index f4945a65f..b815ed75a 100644 --- a/libc/str/escapedos.c +++ b/libc/str/escapedos.c @@ -43,8 +43,8 @@ static textwindows bool shouldquotedos(const char16_t c) { * Escapes command so DOS can run it. * @see Iain Patterson's NSSM for original code in public domain */ -textwindows bool escapedos(char16_t *buffer, unsigned buflen, - const char16_t *unquoted, unsigned len) { +textwindows bool _escapedos(char16_t *buffer, unsigned buflen, + const char16_t *unquoted, unsigned len) { unsigned i, j, n; if (len > buflen - 1) return false; bool escape = false; diff --git a/libc/str/isabspath.c b/libc/str/isabspath.c index 3cba9103e..4e40be341 100644 --- a/libc/str/isabspath.c +++ b/libc/str/isabspath.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -16,33 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" +#include "libc/str/path.h" #include "libc/str/str.h" /** - * Returns true if pathname is absolute, e.g. + * Returns true if pathname is considered absolute. * * - `/home/jart/foo.txt` is absolute - * - `C:/Users/jart/foo.txt` is absolute on NT - * - `C:\Users\jart\foo.txt` is absolute on NT - * - `\??\C:\Users\jart\foo.txt` is absolute on NT - * - `\\.\C:\Users\jart\foo.txt` is absolute on NT - * - `/Users/jart/foo.txt` we consider it absolute enough on NT - * - `\Users\jart\foo.txt` we consider it absolute enough on NT + * - `C:/Users/jart/foo.txt` is absolute on Windows + * - `C:\Users\jart\foo.txt` is absolute on Windows + * - `\??\C:\Users\jart\foo.txt` is absolute on Windows + * - `\\.\C:\Users\jart\foo.txt` is absolute on Windows + * - `/Users/jart/foo.txt` is effectively absolute on Windows + * - `\Users\jart\foo.txt` is effectively absolute on Windows * - * Please note that the recommended approach to using Cosmopolitan is to - * not use absolute paths at all. If you do use absolute paths then it's - * a good idea on Windows to stay within the C: drive. This is because - * Cosmopolitan Libc doesn't create a virtual filesystem layer and - * instead just replaces `\` characters with `/`. */ -bool _isabspath(const char *p) { - if (*p == '/') { - return true; - } - if (IsWindows() && - (*p == '/' || *p == '\\' || (isalpha(p[0]) && p[1] == ':'))) { - return true; - } - return false; +bool _isabspath(const char *path) { + return _classifypath(path) & _PATH_ABS; } diff --git a/libc/fmt/isabspath.c b/libc/str/isdirsep.c similarity index 75% rename from libc/fmt/isabspath.c rename to libc/str/isdirsep.c index 877e22a5e..548d8924e 100644 --- a/libc/fmt/isabspath.c +++ b/libc/str/isdirsep.c @@ -1,7 +1,7 @@ /*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,23 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" -#include "libc/fmt/isslash.internal.h" +#include "libc/dce.h" +#include "libc/str/str.h" /** - * Returns true if pathname could be absolute on any known platform. - * - * The ones we know about are System V (/foo/bar), DOS (C:\foo\bar), - * Windows NT (\\.\C:\foo\bar), Google Cloud (gs://bucket/foo/bar), etc. + * Returns true if character is directory separator slash. */ -bool isabspath(const char *path) { - if (isslash(*path)) return true; - for (; *path; ++path) { - if (isslash(*path)) return false; - if (*path == ':') { - ++path; - if (isslash(*path)) return true; - } - } - return false; +bool _isdirsep(int c) { + return c == '/' || c == '\\'; } diff --git a/libc/str/path.h b/libc/str/path.h new file mode 100644 index 000000000..10b1fca62 --- /dev/null +++ b/libc/str/path.h @@ -0,0 +1,20 @@ +#ifndef COSMOPOLITAN_LIBC_STR_PATH_H_ +#define COSMOPOLITAN_LIBC_STR_PATH_H_ + +#define _PATH_ABS 1 +#define _PATH_DEV 2 +#define _PATH_ROOT 4 +#define _PATH_DOS 8 +#define _PATH_WIN 16 +#define _PATH_NT 32 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int _classifypath(const char *) libcesque nosideeffect; +bool _isabspath(const char *) libcesque strlenesque; +bool _isdirsep(int) libcesque pureconst; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_STR_PATH_H_ */ diff --git a/libc/str/str.h b/libc/str/str.h index f79c75283..3b06b430b 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -196,17 +196,16 @@ char *strerror(int) returnsnonnull dontthrow nocallback; long a64l(const char *); char *l64a(long); -char *strntolower(char *, size_t); -char *strtolower(char *) paramsnonnull(); -char *strntoupper(char *, size_t); -char *strtoupper(char *) paramsnonnull(); -char *chomp(char *); -char16_t *chomp16(char16_t *); -wchar_t *wchomp(wchar_t *); -bool _istext(const void *, size_t); -bool _isutf8(const void *, size_t); -bool _isabspath(const char *) strlenesque; -bool escapedos(char16_t *, unsigned, const char16_t *, unsigned); +char *strntolower(char *, size_t) libcesque; +char *strtolower(char *) libcesque paramsnonnull(); +char *strntoupper(char *, size_t) libcesque; +char *strtoupper(char *) libcesque paramsnonnull(); +char *_chomp(char *) libcesque; +char16_t *_chomp16(char16_t *) libcesque; +wchar_t *_wchomp(wchar_t *) libcesque; +bool _istext(const void *, size_t) libcesque; +bool _isutf8(const void *, size_t) libcesque; +bool _escapedos(char16_t *, unsigned, const char16_t *, unsigned) libcesque; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § strings » multibyte ─╬─│┼ diff --git a/libc/str/wchomp.c b/libc/str/wchomp.c index 79e7190aa..1c9ce046b 100644 --- a/libc/str/wchomp.c +++ b/libc/str/wchomp.c @@ -24,7 +24,16 @@ * @param line is NULL-propagating * @see getline */ -wchar_t *wchomp(wchar_t *line) { - if (line) line[wcscspn(line, L"\r\n")] = '\0'; +wchar_t *_wchomp(wchar_t *line) { + size_t i; + if (line) { + for (i = wcslen(line); i--;) { + if (line[i] == '\r' || line[i] == '\n') { + line[i] = '\0'; + } else { + break; + } + } + } return line; } diff --git a/libc/x/xdirname.c b/libc/x/xdirname.c index 28c336ada..8b483c6e8 100644 --- a/libc/x/xdirname.c +++ b/libc/x/xdirname.c @@ -17,11 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" +#include "libc/runtime/gc.internal.h" #include "libc/x/x.h" /** * Returns directory portion of path. */ char *xdirname(const char *path) { - return dirname(xstrdup(path)); + return xstrdup(dirname(gc(xstrdup(path)))); } diff --git a/libc/x/xgetline.c b/libc/x/xgetline.c index 56123ffef..d5a16e16a 100644 --- a/libc/x/xgetline.c +++ b/libc/x/xgetline.c @@ -23,10 +23,10 @@ /** * Reads line from stream. * - * @return allocated line that needs free() and usually chomp() too, + * @return allocated line that needs free() and usually _chomp() too, * or NULL on ferror() or feof() * @see getdelim() for a more difficult api - * @see chomp() + * @see _chomp() */ char *xgetline(FILE *f) { char *p; diff --git a/libc/x/xjoinpaths.c b/libc/x/xjoinpaths.c index be058d02e..b7b21021e 100644 --- a/libc/x/xjoinpaths.c +++ b/libc/x/xjoinpaths.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" +#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/x/x.h" diff --git a/net/https/logcertificate.c b/net/https/logcertificate.c index cf46521e4..ea306fe05 100644 --- a/net/https/logcertificate.c +++ b/net/https/logcertificate.c @@ -25,7 +25,7 @@ void LogCertificate(const char *msg, mbedtls_x509_crt *cert) { if (LOGGABLE(kLogDebug)) { if ((s = malloc((n = 15000)))) { if (mbedtls_x509_crt_info(s, n, " ", cert) > 0) { - DEBUGF("%s\n%s", msg, chomp(s)); + DEBUGF("%s\n%s", msg, _chomp(s)); } free(s); } diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index 997ac9742..4b38b4567 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -54,9 +54,9 @@ TEST(mkdir, testPathIsDirectory_EEXIST) { EXPECT_EQ(EEXIST, errno); } -TEST(makedirs, testEmptyString_ENOENT) { +TEST(makedirs, testEmptyString_EEXIST) { EXPECT_EQ(-1, makedirs("", 0755)); - EXPECT_EQ(ENOENT, errno); + EXPECT_EQ(EEXIST, errno); } TEST(mkdirat, testRelativePath_opensRelativeToDirFd) { diff --git a/test/libc/fmt/basename_test.c b/test/libc/fmt/basename_test.c index be234e6d8..60eb8ae42 100644 --- a/test/libc/fmt/basename_test.c +++ b/test/libc/fmt/basename_test.c @@ -21,25 +21,33 @@ #include "libc/mem/mem.h" #include "libc/testlib/testlib.h" -TEST(basename, test) { - EXPECT_STREQ("", basename("")); - EXPECT_STREQ("/", basename("/")); - EXPECT_STREQ("hello", basename("hello")); - EXPECT_STREQ("there", basename("hello/there")); - EXPECT_STREQ("yo", basename("hello/there/yo")); +#define BASENAME(x) basename(gc(strdup(x))) + +TEST(basename, testRegularExamples) { + EXPECT_STREQ("lib", BASENAME("/usr/lib")); + EXPECT_STREQ("lib", BASENAME("usr/lib")); + EXPECT_STREQ("usr", BASENAME("/usr/")); + EXPECT_STREQ("usr", BASENAME("usr")); + EXPECT_STREQ("/", BASENAME("/")); + EXPECT_STREQ(".", BASENAME(".")); + EXPECT_STREQ("..", BASENAME("..")); +} + +TEST(basename, testIrregularExamples) { + EXPECT_STREQ(".", basename(0)); + EXPECT_STREQ(".", basename("")); } TEST(basename, testTrailingSlash_isIgnored) { - /* should be "foo" but basename() doesn't allocate memory */ - EXPECT_STREQ("foo/", basename("foo/")); - EXPECT_STREQ("foo//", basename("foo//")); + EXPECT_STREQ("foo", BASENAME("foo/")); + EXPECT_STREQ("foo", BASENAME("foo//")); } TEST(basename, testOnlySlashes_oneSlashOnlyVasily) { - EXPECT_STREQ("/", basename("///")); + EXPECT_STREQ("/", BASENAME("///")); } TEST(basename, testWindows_isGrantedRespect) { - EXPECT_STREQ("there", basename("hello\\there")); - EXPECT_STREQ("yo", basename("hello\\there\\yo")); + EXPECT_STREQ("there", BASENAME("hello\\there")); + EXPECT_STREQ("yo", BASENAME("hello\\there\\yo")); } diff --git a/test/libc/fmt/dirname_test.c b/test/libc/fmt/dirname_test.c index 02542b87e..42e089209 100644 --- a/test/libc/fmt/dirname_test.c +++ b/test/libc/fmt/dirname_test.c @@ -26,12 +26,11 @@ TEST(dirname, test) { EXPECT_STREQ("/usr/lib", dirname(gc(strdup("/usr/lib/foo.bar")))); EXPECT_STREQ("/usr", dirname(gc(strdup("/usr/lib")))); - EXPECT_STREQ("/usr", dirname(gc(strdup("/usr/lib")))); EXPECT_STREQ("usr", dirname(gc(strdup("usr/lib")))); EXPECT_STREQ("/", dirname(gc(strdup("/usr/")))); + EXPECT_STREQ(".", dirname(gc(strdup("usr")))); EXPECT_STREQ("/", dirname(gc(strdup("/")))); - EXPECT_STREQ(".", dirname(gc(strdup("hello")))); EXPECT_STREQ(".", dirname(gc(strdup(".")))); EXPECT_STREQ(".", dirname(gc(strdup("..")))); - EXPECT_STREQ("", dirname(gc(strdup("")))); + EXPECT_STREQ(".", dirname(gc(strdup("")))); } diff --git a/test/libc/str/classifypath_test.c b/test/libc/str/classifypath_test.c new file mode 100644 index 000000000..f0c1b06e2 --- /dev/null +++ b/test/libc/str/classifypath_test.c @@ -0,0 +1,116 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/path.h" +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +TEST(isabspath, testUniversal) { + ASSERT_TRUE(_isabspath("/home/jart/foo.txt")); +} + +TEST(isabspath, testDosPaths) { + ASSERT_FALSE(_isabspath("C:")); + ASSERT_FALSE(_isabspath("C:foo.txt")); + ASSERT_TRUE(_isabspath("C:/")); + ASSERT_TRUE(_isabspath("C:/Users/jart/foo.txt")); + ASSERT_TRUE(_isabspath("C:\\Users\\jart\\foo.txt")); + ASSERT_TRUE(_isabspath("\\Users\\jart\\foo.txt")); +} + +TEST(isabspath, testWin32Paths) { + ASSERT_TRUE(_isabspath("\\\\?\\C:\\..")); + ASSERT_TRUE(_isabspath("\\\\.\\C:\\Users\\jart\\foo.txt")); +} + +TEST(isabspath, testNtPaths) { + ASSERT_TRUE(_isabspath("\\??\\C:\\Users\\jart\\foo.txt")); +} + +TEST(_classifypath, test) { + EXPECT_EQ(0, _classifypath("")); + EXPECT_EQ(0, _classifypath("xyz")); + EXPECT_EQ(_PATH_DOS | _PATH_DEV, _classifypath("CON")); + EXPECT_EQ(_PATH_DOS | _PATH_DEV, _classifypath("NUL")); + EXPECT_EQ(0, _classifypath(":")); + EXPECT_EQ(_PATH_DOS, _classifypath("::")); + EXPECT_EQ(_PATH_DOS, _classifypath(":::")); + EXPECT_EQ(_PATH_DOS, _classifypath("::::")); + EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("::\\")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\:")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\C:")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\C:\\")); + EXPECT_EQ(_PATH_ABS, _classifypath("/")); + EXPECT_EQ(_PATH_ABS, _classifypath("/:")); + EXPECT_EQ(_PATH_ABS, _classifypath("/C:")); + EXPECT_EQ(_PATH_ABS, _classifypath("/C:/")); + EXPECT_EQ(0, _classifypath("C")); + EXPECT_EQ(_PATH_DOS, _classifypath("C:")); + EXPECT_EQ(_PATH_DOS, _classifypath("C:a")); + EXPECT_EQ(_PATH_DOS, _classifypath("C:a\\")); + EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\")); + EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:/")); + EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\a")); + EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:/a")); + EXPECT_EQ(_PATH_ABS | _PATH_DOS, _classifypath("C:\\\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\;")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\b\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\b")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\f")); + EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\")); + EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\UNC")); + EXPECT_EQ(_PATH_ABS | _PATH_NT, _classifypath("\\??\\UNC\\")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\?")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\UNC")); + EXPECT_EQ(_PATH_ABS, _classifypath("\\?\\UNC\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\?\\UNC\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("\\\\?")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\\\??\\C:\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("\\\\.")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\.\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("\\\\.\\C:\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("\\/")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("/\\")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("///")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//;")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("//?")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("/\\?")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("\\/?")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN, _classifypath("//??")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("//.")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("\\/.")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV | _PATH_ROOT, + _classifypath("/\\.")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("//./")); + EXPECT_EQ(_PATH_ABS | _PATH_WIN | _PATH_DEV, _classifypath("//./C:/")); +} diff --git a/third_party/quickjs/quickjs-libc.c b/third_party/quickjs/quickjs-libc.c index 3c2ed80c7..d3598fb2b 100644 --- a/third_party/quickjs/quickjs-libc.c +++ b/third_party/quickjs/quickjs-libc.c @@ -3063,7 +3063,7 @@ typedef struct { typedef struct { char *filename; /* module filename */ - char *basename; /* module base name */ + char *basename_; /* module base name */ JSWorkerMessagePipe *recv_pipe, *send_pipe; } WorkerFuncArgs; @@ -3231,10 +3231,10 @@ static void *worker_func(void *opaque) js_std_add_helpers(ctx, -1, NULL); - if (!JS_RunModule(ctx, args->basename, args->filename)) + if (!JS_RunModule(ctx, args->basename_, args->filename)) js_std_dump_error(ctx); free(args->filename); - free(args->basename); + free(args->basename_); free(args); js_std_loop(ctx); @@ -3315,7 +3315,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, goto oom_fail; bzero(args, sizeof(*args)); args->filename = strdup(filename); - args->basename = strdup(basename); + args->basename_ = strdup(basename); /* ports */ args->recv_pipe = js_new_message_pipe(); @@ -3349,7 +3349,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, JS_FreeCString(ctx, filename); if (args) { free(args->filename); - free(args->basename); + free(args->basename_); js_free_message_pipe(args->recv_pipe); js_free_message_pipe(args->send_pipe); free(args); diff --git a/tool/build/pstrace.c b/tool/build/pstrace.c index ea00c0d31..039c15027 100644 --- a/tool/build/pstrace.c +++ b/tool/build/pstrace.c @@ -960,7 +960,7 @@ int main(int argc, char *argv[]) { fin = fdopen(pipefds[0], "r"); t = NewTrace(); for (ev = 0, lineno = 1; !interrupted && (line = xgetline(fin)); ++lineno) { - chomp(line); + _chomp(line); Parse(t, line, lineno); free(line); for (; ev < t->events.n && !t->events.p[ev].is_interrupted; ++ev) { diff --git a/tool/decode/scrubdox.c b/tool/decode/scrubdox.c index 5a778a00a..ce8909d62 100644 --- a/tool/decode/scrubdox.c +++ b/tool/decode/scrubdox.c @@ -152,7 +152,7 @@ int main(int argc, char *argv[]) { int y, x; ShowCrashReports(); f = stdin; - while ((s = chomp(xgetline(f)))) { + while ((s = _chomp(xgetline(f)))) { n = strwidth(s, 0); xn = MAX(xn, n); T = xrealloc(T, ++yn * sizeof(*T)); diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index a9f9bbc07..beb020a22 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -113,6 +113,7 @@ (let ((stab (copy-syntax-table))) (with-syntax-table stab (modify-syntax-entry ?+ " ") + (modify-syntax-entry ?* " ") (let ((thing (thing-at-point 'symbol no-properties))) (when thing (intern thing)))))) @@ -146,6 +147,7 @@ ;; M-3 C-c C-c Compile w/ MODE=rel ;; M-4 C-c C-c Compile w/ MODE=dbg ;; M-5 C-c C-c Compile w/ MODE="" +;; M-7 C-c C-c Compile w/ MODE=tinylinux ;; M-8 C-c C-c Compile w/ llvm ;; M-9 C-c C-c Compile w/ chibicc @@ -162,6 +164,7 @@ ((eq arg 3) "rel") ((eq arg 4) "dbg") ((eq arg 5) "") + ((eq arg 7) "tinylinux") ((eq arg 8) "llvm") (default default) ((cosmo-intest) "dbg") diff --git a/tool/viz/ntmaster.c b/tool/viz/ntmaster.c index 349d3fc9e..05152ea3b 100644 --- a/tool/viz/ntmaster.c +++ b/tool/viz/ntmaster.c @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { ShowCrashReports(); f = fopen("/tmp/syms.txt", "r"); memset(tabs, '\t', 64); - while ((sym = chomp(xgetline(f)))) { + while ((sym = _chomp(xgetline(f)))) { if (strlen(sym)) { printf("imp\t"); diff --git a/tool/viz/rgbtoxterm.c b/tool/viz/rgbtoxterm.c index 569abd092..cb9b405bb 100644 --- a/tool/viz/rgbtoxterm.c +++ b/tool/viz/rgbtoxterm.c @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) { } } else { while ((getline(&line_, &linecap_, stdin)) != -1) { - processarg(chomp(line_)); + processarg(_chomp(line_)); } free_s(&line_); } diff --git a/tool/viz/tabalign.c b/tool/viz/tabalign.c index 6c0e4fddd..9c1f52e0b 100644 --- a/tool/viz/tabalign.c +++ b/tool/viz/tabalign.c @@ -131,7 +131,7 @@ void processfile(void) { int col, s; size_t off, len; while ((getline(&line_, &linecap_, fi_)) != -1) { - chomp(line_); + _chomp(line_); len = strlen(line_); s = concat(&pool_, line_, len + 1); if (len < USHRT_MAX) { diff --git a/tool/viz/xterm256effective2.c b/tool/viz/xterm256effective2.c index 9bc23fe4c..0ba3216a2 100644 --- a/tool/viz/xterm256effective2.c +++ b/tool/viz/xterm256effective2.c @@ -148,7 +148,7 @@ void ProcessFile(void) { fg1 = -1u; glyph1 = -1u; while ((getline(&line_, &linecap_, in_)) != -1) { - p = chomp(line_); + p = _chomp(line_); sscanf(p, "%x, %u,%u,%u", &color, &bg, &fg, &glyph); if (color != color1) { if (color1 != -1u) {