From c88f95a8925ef1d56daa471a1d14f189695eaac8 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 21 Sep 2023 08:13:50 -0700 Subject: [PATCH] Remove Windows executable path guessing logic Unlike CMD.EXE, CreateProcess() doesn't care if an executable name ends with .COM or .EXE. We now have the unbourne shell and bash working well on Windows, so we don't need DOS anymore. Making this change will grant us better performance, particularly for builds, because commandv() will need to make fewer system calls. Path mangling magic still happens with WinMain() and ntspawn() in order to do things like turn \ into / so the interop works well at the borders. But all the code in libraries, which did that, has been removed. It's not possible for libraries to abstract the differences between paths. --- libc/calls/commandv.c | 185 ++++++++---------------------- libc/calls/makedirs.c | 9 +- libc/calls/unveil.c | 1 - libc/fmt/basename.c | 7 +- libc/fmt/dirname.c | 9 +- libc/mem/get_current_dir_name.c | 3 +- libc/proc/ntspawn.c | 35 ++---- libc/sock/pselect.c | 4 +- libc/str/classifypath.c | 154 ------------------------- libc/str/isabspath.c | 36 ------ libc/str/path.h | 20 ---- libc/thread/sem_path_np.c | 1 - libc/x/xjoinpaths.c | 2 +- test/libc/calls/commandv_test.c | 104 ++++++----------- test/libc/calls/makedirs_test.c | 14 +-- test/libc/fmt/basename_test.c | 5 - test/libc/fmt/dirname_test.c | 24 ++-- test/libc/str/classifypath_test.c | 121 ------------------- third_party/lua/lunix.c | 1 - 19 files changed, 125 insertions(+), 610 deletions(-) delete mode 100644 libc/str/classifypath.c delete mode 100644 libc/str/isabspath.c mode change 100644 => 100755 libc/str/path.h delete mode 100644 test/libc/str/classifypath_test.c diff --git a/libc/calls/commandv.c b/libc/calls/commandv.c index 9dd92cbef..566b67778 100644 --- a/libc/calls/commandv.c +++ b/libc/calls/commandv.c @@ -18,121 +18,15 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" -#include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/bits.h" -#include "libc/intrin/safemacros.internal.h" #include "libc/intrin/strace.internal.h" -#include "libc/log/libfatal.internal.h" +#include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/s.h" #include "libc/sysv/errfuns.h" -static bool IsExePath(const char *s, size_t n) { - return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".exe") || - READ32LE(s + n - 4) == READ32LE(".EXE")); -} - -static bool IsComPath(const char *s, size_t n) { - return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".com") || - READ32LE(s + n - 4) == READ32LE(".COM")); -} - -static bool IsComDbgPath(const char *s, size_t n) { - return n >= 8 && (READ64LE(s + n - 8) == READ64LE(".com.dbg") || - READ64LE(s + n - 8) == READ64LE(".COM.DBG")); -} - -static bool AccessCommand(const char *name, char *path, size_t pathsz, - size_t namelen, int *err, const char *suffix, - size_t pathlen) { - size_t suffixlen; - suffixlen = strlen(suffix); - if (IsWindows() && suffixlen == 0 && !IsExePath(name, namelen) && - !IsComPath(name, namelen)) - return false; - if (pathlen + 1 + namelen + suffixlen + 1 > pathsz) return false; - if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) { - path[pathlen] = !IsWindows() ? '/' - : memchr(path, '\\', pathlen) ? '\\' - : '/'; - pathlen++; - } - memcpy(path + pathlen, name, namelen); - memcpy(path + pathlen + namelen, suffix, suffixlen + 1); - if (!access(path, X_OK)) { - struct stat st; - if (!stat(path, &st)) { - if (S_ISREG(st.st_mode)) { - return true; - } else { - errno = EACCES; - } - } - } - if (errno == EACCES || *err != EACCES) *err = errno; - return false; -} - -static bool SearchPath(const char *name, char *path, size_t pathsz, - size_t namelen, int *err, const char *suffix) { - char sep; - size_t i; - const char *p; - if (!(p = getenv("PATH"))) p = "/bin:/usr/local/bin:/usr/bin"; - sep = IsWindows() && strchr(p, ';') ? ';' : ':'; - for (;;) { - for (i = 0; p[i] && p[i] != sep; ++i) { - if (i < pathsz) { - path[i] = p[i]; - } - } - if (AccessCommand(name, path, pathsz, namelen, err, suffix, i)) { - return true; - } - if (p[i] == sep) { - p += i + 1; - } else { - break; - } - } - return false; -} - -static bool FindCommand(const char *name, char *pb, size_t pbsz, size_t namelen, - bool pri, const char *suffix, int *err) { - if (pri && (memchr(name, '/', namelen) || memchr(name, '\\', namelen))) { - pb[0] = 0; - return AccessCommand(name, pb, pbsz, namelen, err, suffix, 0); - } - if (IsWindows() && pri && - pbsz > max(strlen(kNtSystemDirectory), strlen(kNtWindowsDirectory))) { - return AccessCommand(name, pb, pbsz, namelen, err, suffix, - stpcpy(pb, kNtSystemDirectory) - pb) || - AccessCommand(name, pb, pbsz, namelen, err, suffix, - stpcpy(pb, kNtWindowsDirectory) - pb); - } - return (IsWindows() && - (pbsz > 1 && AccessCommand(name, pb, pbsz, namelen, err, suffix, - stpcpy(pb, ".") - pb))) || - SearchPath(name, pb, pbsz, namelen, err, suffix); -} - -static bool FindVerbatim(const char *name, char *pb, size_t pbsz, - size_t namelen, bool pri, int *err) { - return FindCommand(name, pb, pbsz, namelen, pri, "", err); -} - -static bool FindSuffixed(const char *name, char *pb, size_t pbsz, - size_t namelen, bool pri, int *err) { - return !IsExePath(name, namelen) && !IsComPath(name, namelen) && - !IsComDbgPath(name, namelen) && - (FindCommand(name, pb, pbsz, namelen, pri, ".com", err) || - FindCommand(name, pb, pbsz, namelen, pri, ".exe", err)); -} - /** * Resolves full pathname of executable. * @@ -143,35 +37,56 @@ static bool FindSuffixed(const char *name, char *pb, size_t pbsz, * @vforksafe */ char *commandv(const char *name, char *pathbuf, size_t pathbufsz) { - int e, f; - char *res; + + // bounce empty names size_t namelen; - res = 0; - if (!name) { - efault(); - } else if (!(namelen = strlen(name))) { + if (!(namelen = strlen(name))) { enoent(); - } else if (namelen + 1 > pathbufsz) { - enametoolong(); - } else { - e = errno; - f = ENOENT; - if ((IsWindows() && - (FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) || - FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) || - FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f) || - FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f))) || - (!IsWindows() && - (FindVerbatim(name, pathbuf, pathbufsz, namelen, true, &f) || - FindSuffixed(name, pathbuf, pathbufsz, namelen, true, &f) || - FindVerbatim(name, pathbuf, pathbufsz, namelen, false, &f) || - FindSuffixed(name, pathbuf, pathbufsz, namelen, false, &f)))) { - errno = e; - res = pathbuf; - } else { - errno = f; - } + return 0; } - STRACE("commandv(%#s, %p, %'zu) → %#s% m", name, pathbuf, pathbufsz, res); - return res; + + // get system path + const char *syspath; + if (memchr(name, '/', namelen)) { + syspath = ""; + } else if (!(syspath = getenv("PATH"))) { + syspath = _PATH_DEFPATH; + } + + // iterate through directories + int old_errno = errno; + bool seen_eacces = false; + const char *b, *a = syspath; + errno = ENOENT; + do { + b = strchrnul(a, ':'); + size_t dirlen = b - a; + if (dirlen + 1 + namelen < pathbufsz) { + if (dirlen) { + memcpy(pathbuf, a, dirlen); + pathbuf[dirlen] = '/'; + memcpy(pathbuf + dirlen + 1, name, namelen + 1); + } else { + memcpy(pathbuf, name, namelen + 1); + } + if (!access(pathbuf, X_OK)) { + struct stat st; + if (!stat(pathbuf, &st) && S_ISREG(st.st_mode)) { + errno = old_errno; + return pathbuf; + } + } else if (errno == EACCES) { + seen_eacces = true; + } + } else { + enametoolong(); + } + a = b + 1; + } while (*b); + + // return error if not found + if (seen_eacces) { + errno = EACCES; + } + return 0; } diff --git a/libc/calls/makedirs.c b/libc/calls/makedirs.c index b04796d02..989e2a42e 100644 --- a/libc/calls/makedirs.c +++ b/libc/calls/makedirs.c @@ -20,7 +20,6 @@ #include "libc/calls/struct/stat.h" #include "libc/errno.h" #include "libc/limits.h" -#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/sysv/consts/s.h" #include "libc/sysv/errfuns.h" @@ -66,8 +65,8 @@ int makedirs(const char *path, unsigned mode) { break; } if (errno != ENOENT) return -1; - while (i && _isdirsep(buf[i - 1])) buf[--i] = 0; - while (i && !_isdirsep(buf[i - 1])) buf[--i] = 0; + while (i && buf[i - 1] == '/') buf[--i] = 0; + while (i && buf[i - 1] != '/') buf[--i] = 0; } // ascend @@ -77,8 +76,8 @@ int makedirs(const char *path, unsigned mode) { if (i == n) goto CheckTop; } if (i == n) break; - while (i < n && !_isdirsep((c = path[i]))) buf[i++] = c; - while (i < n && _isdirsep((c = path[i]))) buf[i++] = c; + while (i < n && (c = path[i]) != '/') buf[i++] = c; + while (i < n && (c = path[i]) == '/') buf[i++] = c; } Finish: diff --git a/libc/calls/unveil.c b/libc/calls/unveil.c index ca20f6f88..2dd0209d1 100644 --- a/libc/calls/unveil.c +++ b/libc/calls/unveil.c @@ -38,7 +38,6 @@ #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" -#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/audit.h" diff --git a/libc/fmt/basename.c b/libc/fmt/basename.c index d06c8b03e..982a07299 100644 --- a/libc/fmt/basename.c +++ b/libc/fmt/basename.c @@ -16,8 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" -#include "libc/str/path.h" +#include "libc/fmt/libgen.h" #include "libc/str/str.h" /** @@ -46,10 +45,10 @@ char *basename(char *path) { size_t i; if (path && *path) { i = strlen(path) - 1; - for (; i && _isdirsep(path[i]); i--) { + for (; i && path[i] == '/'; i--) { path[i] = 0; } - while (i && !_isdirsep(path[i - 1])) { + while (i && path[i - 1] != '/') { i--; } return path + i; diff --git a/libc/fmt/dirname.c b/libc/fmt/dirname.c index 234f7f111..373735328 100644 --- a/libc/fmt/dirname.c +++ b/libc/fmt/dirname.c @@ -16,8 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" -#include "libc/str/path.h" +#include "libc/fmt/libgen.h" #include "libc/str/str.h" /** @@ -41,13 +40,13 @@ char *dirname(char *path) { size_t i; if (path && *path) { i = strlen(path) - 1; - for (; _isdirsep(path[i]); i--) { + for (; path[i] == '/'; i--) { if (!i) return "/"; } - for (; !_isdirsep(path[i]); i--) { + for (; path[i] != '/'; i--) { if (!i) return "."; } - for (; _isdirsep(path[i]); i--) { + for (; path[i] == '/'; i--) { if (!i) return "/"; } path[i + 1] = 0; diff --git a/libc/mem/get_current_dir_name.c b/libc/mem/get_current_dir_name.c index 1cc50ba57..505d9d318 100644 --- a/libc/mem/get_current_dir_name.c +++ b/libc/mem/get_current_dir_name.c @@ -19,7 +19,6 @@ #include "libc/calls/calls.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/str/path.h" /** * Returns current working directory. @@ -32,7 +31,7 @@ */ char *get_current_dir_name(void) { const char *p; - if ((p = getenv("PWD")) && _isabspath(p)) { + if ((p = getenv("PWD")) && *p == '/') { return strdup(p); } else { return getcwd(0, 0); diff --git a/libc/proc/ntspawn.c b/libc/proc/ntspawn.c index db09594b6..8cb51a0f3 100644 --- a/libc/proc/ntspawn.c +++ b/libc/proc/ntspawn.c @@ -81,12 +81,10 @@ textwindows int ntspawn( const char16_t *opt_lpCurrentDirectory, const struct NtStartupInfo *lpStartupInfo, struct NtProcessInformation *opt_out_lpProcessInformation) { + int rc = -1; int64_t handle; - int i, e, rc = -1; struct SpawnBlock *block = 0; - char16_t prog16[PATH_MAX + 5], *p; - char16_t suffixes[][5] = {u"", u".com", u".exe"}; - + char16_t prog16[PATH_MAX + 5]; if (__mkntpath(prog, prog16) == -1) return -1; if ((handle = CreateFileMapping(-1, 0, pushpop(kNtPageReadwrite), 0, sizeof(*block), 0)) && @@ -94,28 +92,13 @@ textwindows int ntspawn( sizeof(*block), 0)) && mkntcmdline(block->cmdline, argv) != -1 && mkntenvblock(block->envvars, envp, extravar, block->buf) != -1) { - p = prog16 + strlen16(prog16); - for (i = 0; i < ARRAYLEN(suffixes); ++i) { - if (suffixes[i][0] && endswith16(prog16, suffixes[i])) { - p -= strlen16(suffixes[i]); - *p = 0; - } else { - strcpy16(p, suffixes[i]); - } - e = errno; - if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, - opt_lpThreadAttributes, bInheritHandles, - dwCreationFlags | kNtCreateUnicodeEnvironment | - kNtInheritParentAffinity, - block->envvars, opt_lpCurrentDirectory, lpStartupInfo, - opt_out_lpProcessInformation)) { - rc = 0; - break; - } else if (errno == ENOENT) { - errno = e; - } else { - break; - } + if (CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes, + opt_lpThreadAttributes, bInheritHandles, + dwCreationFlags | kNtCreateUnicodeEnvironment | + kNtInheritParentAffinity, + block->envvars, opt_lpCurrentDirectory, lpStartupInfo, + opt_out_lpProcessInformation)) { + rc = 0; } } else if (GetLastError() == kNtErrorSharingViolation) { etxtbsy(); diff --git a/libc/sock/pselect.c b/libc/sock/pselect.c index 2f04ca9d1..0ccdd3a98 100644 --- a/libc/sock/pselect.c +++ b/libc/sock/pselect.c @@ -67,7 +67,6 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const sigset_t *s; size_t n; } ss; - BEGIN_CANCELLATION_POINT; #ifdef SYSDEBUG fd_set old_readfds; @@ -78,6 +77,7 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, fd_set *old_exceptfds_ptr = 0; #endif + BEGIN_CANCELLATION_POINT; if (nfds < 0) { rc = einval(); } else if (IsAsan() && @@ -126,8 +126,8 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask); } } - END_CANCELLATION_POINT; + STRACE("pselect(%d, %s → [%s], %s → [%s], %s → [%s], %s, %s) → %d% m", nfds, DescribeFdSet(rc, nfds, old_readfds_ptr), DescribeFdSet(rc, nfds, readfds), diff --git a/libc/str/classifypath.c b/libc/str/classifypath.c deleted file mode 100644 index 9213b93fa..000000000 --- a/libc/str/classifypath.c +++ /dev/null @@ -1,154 +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 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) & _kPathAbs) { ... } - * - * To check if path is a relative path: - * - * if (~_classifypath(str) & _kPathAbs) { ... } - * - * 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` - * - `_kPathAbs` if absolute (or rooted dos) path e.g. `/⋯` - * - `_kPathDos` if `c:`, `d:foo` i.e. drive-relative path - * - `_kPathAbs|_kPathDos` if proper dos path e.g. `c:/foo` - * - `_kPathDos|_kPathDev` if dos device path e.g. `nul`, `conin$` - * - `_kPathAbs|_kPathWin` if `//c`, `//?c`, etc. - * - `_kPathAbs|_kPathWin|_kPathDev` if `//./⋯`, `//?/⋯` - * - `_kPathAbs|_kPathWin|_kPathDev|_kPathRoot` if `//.` or `//?` - * - `_kPathAbs|_kPathNt` 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 _kPathDos | _kPathDev; - } - switch (s[1]) { - case ':': - switch (s[2]) { - case 0: // c: - default: // c:wut⋯ - return _kPathDos; - case '/': // c:/⋯ - case '\\': // c:\⋯ - return _kPathAbs | _kPathDos; - } - default: - return 0; - } - case '\\': - if (SupportsWindows()) { - if (s[1] == '?' && s[2] == '?') { - if (!s[3]) { - return _kPathAbs | _kPathNt | _kPathRoot; // \??\⋯ - } else if (s[3] == '\\') { - return _kPathAbs | _kPathNt; // \??\⋯ - } - } - } - // fallthrough - case '/': - if (!SupportsWindows()) { - return _kPathAbs; - } - switch (s[1]) { - case 0: // / - default: // /⋯ - return _kPathAbs; - case '/': - case '\\': - switch (s[2]) { - case 0: // // - default: // //⋯ - return _kPathAbs | _kPathWin; - case '.': - case '?': - switch (s[3]) { - case 0: // //? or //. - return _kPathAbs | _kPathWin | _kPathDev | _kPathRoot; - default: // //?⋯ or //.⋯ - return _kPathAbs | _kPathWin; - case '/': - case '\\': // //?/⋯ or //./⋯ - return _kPathAbs | _kPathWin | _kPathDev; - } - } - } - } - } else { - return 0; - } -} diff --git a/libc/str/isabspath.c b/libc/str/isabspath.c deleted file mode 100644 index 60de5b76a..000000000 --- a/libc/str/isabspath.c +++ /dev/null @@ -1,36 +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/str/path.h" -#include "libc/str/str.h" - -/** - * Returns true if pathname is considered absolute. - * - * - `/home/jart/foo.txt` is absolute - * - `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 - * - */ -bool _isabspath(const char *path) { - return _classifypath(path) & _kPathAbs; -} diff --git a/libc/str/path.h b/libc/str/path.h old mode 100644 new mode 100755 index 74c65a91f..e69de29bb --- a/libc/str/path.h +++ b/libc/str/path.h @@ -1,20 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_STR_PATH_H_ -#define COSMOPOLITAN_LIBC_STR_PATH_H_ - -#define _kPathAbs 1 -#define _kPathDev 2 -#define _kPathRoot 4 -#define _kPathDos 8 -#define _kPathWin 16 -#define _kPathNt 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/thread/sem_path_np.c b/libc/thread/sem_path_np.c index 34b86f264..2e71c7068 100644 --- a/libc/thread/sem_path_np.c +++ b/libc/thread/sem_path_np.c @@ -19,7 +19,6 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/str/blake2.h" -#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" #include "libc/thread/thread.h" diff --git a/libc/x/xjoinpaths.c b/libc/x/xjoinpaths.c index 671095a94..c6e67b74f 100644 --- a/libc/x/xjoinpaths.c +++ b/libc/x/xjoinpaths.c @@ -38,7 +38,7 @@ char *xjoinpaths(const char *path, const char *other) { return xstrdup(path); } else if (!*path) { return xstrdup(other); - } else if (_isabspath(other) || !strcmp(path, ".")) { + } else if (*other == '/' || !strcmp(path, ".")) { return xstrdup(other); } else if (endswith(path, "/")) { return xstrcat(path, other); diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index 88067ff6d..85810a5d7 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -17,23 +17,13 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/struct/dirent.h" -#include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/fmt/fmt.h" #include "libc/intrin/safemacros.internal.h" #include "libc/limits.h" -#include "libc/log/check.h" -#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" -#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/dt.h" #include "libc/testlib/testlib.h" -#include "libc/x/x.h" uint64_t i; char *oldpath; @@ -49,93 +39,63 @@ void SetUp(void) { mkdir("bin", 0755); mkdir("home", 0755); oldpath = strdup(nulltoempty(getenv("PATH"))); - CHECK_NE(-1, setenv("PATH", "bin", true)); + ASSERT_NE(-1, setenv("PATH", "bin", true)); } void TearDown(void) { - CHECK_NE(-1, setenv("PATH", oldpath, true)); + ASSERT_NE(-1, setenv("PATH", oldpath, true)); free(oldpath); } TEST(commandv, testPathSearch) { - EXPECT_NE(-1, touch("bin/sh", 0755)); - if (IsWindows()) { - EXPECT_EQ(NULL, commandv("sh", pathbuf, sizeof(pathbuf))); - EXPECT_EQ(errno, ENOENT); - } else { - EXPECT_STREQ("bin/sh", commandv("sh", pathbuf, sizeof(pathbuf))); - } -} - -TEST(commandv, testPathSearch_appendsComExtension) { - EXPECT_NE(-1, touch("bin/sh.com", 0755)); - EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf, sizeof(pathbuf))); + EXPECT_SYS(0, 0, touch("bin/sh", 0755)); + EXPECT_STREQ("bin/sh", commandv("sh", pathbuf, sizeof(pathbuf))); } TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) { - EXPECT_NE(-1, touch("home/sh.com", 0755)); - i = __syscount; + EXPECT_SYS(0, 0, touch("home/sh.com", 0755)); EXPECT_STREQ("home/sh.com", commandv("home/sh.com", pathbuf, sizeof(pathbuf))); -#ifdef __x86_64__ - if (!IsWindows()) EXPECT_EQ(i + 2, __syscount); -#endif -} - -TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) { - EXPECT_NE(-1, touch("home/sh.com", 0755)); - i = __syscount; - EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf, sizeof(pathbuf))); -#ifdef __x86_64__ - if (!IsWindows()) EXPECT_EQ(i + 3, __syscount); -#endif } TEST(commandv, testSameDir_doesntHappenByDefaultUnlessItsWindows) { - EXPECT_NE(-1, touch("bog.com", 0755)); - if (IsWindows()) { - EXPECT_STREQ("./bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); - } else { - EXPECT_EQ(NULL, commandv("bog.com", pathbuf, sizeof(pathbuf))); - EXPECT_EQ(errno, ENOENT); - } + EXPECT_SYS(0, 0, touch("bog.com", 0755)); + EXPECT_STREQ(NULL, commandv("bog.com", pathbuf, sizeof(pathbuf))); + EXPECT_EQ(ENOENT, errno); } TEST(commandv, testSameDir_willHappenWithColonBlank) { - CHECK_NE(-1, setenv("PATH", "bin:", true)); - EXPECT_NE(-1, touch("bog.com", 0755)); - if (IsWindows()) { - EXPECT_STREQ("./bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); - } else { - EXPECT_STREQ("bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); - } + ASSERT_NE(-1, setenv("PATH", "bin:", true)); + EXPECT_SYS(0, 0, touch("bog.com", 0755)); + EXPECT_STREQ("bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); } TEST(commandv, testSameDir_willHappenWithColonBlank2) { - CHECK_NE(-1, setenv("PATH", ":bin", true)); - EXPECT_NE(-1, touch("bog.com", 0755)); - if (IsWindows()) { - EXPECT_STREQ("./bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); - } else { - EXPECT_STREQ("bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); - } + ASSERT_NE(-1, setenv("PATH", ":bin", true)); + EXPECT_SYS(0, 0, touch("bog.com", 0755)); + EXPECT_STREQ("bog.com", commandv("bog.com", pathbuf, sizeof(pathbuf))); } TEST(commandv, test_DirPaths_wontConsiderDirectoriesExecutable) { - CHECK_NE(-1, setenv("PATH", ":bin", true)); - EXPECT_NE(-1, mkdir("Cursors", 0755)); - EXPECT_EQ(NULL, commandv("Cursors", pathbuf, sizeof(pathbuf))); - if (IsWindows()) { - EXPECT_EQ(errno, ENOENT); - } else { - EXPECT_EQ(errno, EACCES); - } + ASSERT_NE(-1, setenv("PATH", ":bin", true)); + EXPECT_SYS(0, 0, mkdir("Cursors", 0755)); + EXPECT_STREQ(NULL, commandv("Cursors", pathbuf, sizeof(pathbuf))); + EXPECT_EQ(ENOENT, errno); } TEST(commandv, test_DirPaths_wontConsiderDirectoriesExecutable2) { - CHECK_NE(-1, setenv("PATH", ":bin", true)); - EXPECT_NE(-1, mkdir("this_is_a_directory.com", 0755)); - EXPECT_EQ(NULL, - commandv("this_is_a_directory.com", pathbuf, sizeof(pathbuf))); - EXPECT_EQ(errno, EACCES); + ASSERT_NE(-1, setenv("PATH", ":bin", true)); + EXPECT_SYS(0, 0, mkdir("this_is_a_directory.com", 0755)); + EXPECT_STREQ(NULL, + commandv("this_is_a_directory.com", pathbuf, sizeof(pathbuf))); + EXPECT_EQ(ENOENT, errno); +} + +TEST(commandv, test_nonExecutableFile_willEacces) { + if (IsWindows()) return; // TODO: fixme + setenv("PATH", "foo", true); + EXPECT_SYS(0, 0, mkdir("foo", 0755)); + EXPECT_SYS(0, 0, touch("foo/bar", 0400)); + EXPECT_SYS(EACCES, NULL, commandv("bar", pathbuf, sizeof(pathbuf))); + EXPECT_SYS(EACCES, NULL, commandv("foo/bar", pathbuf, sizeof(pathbuf))); } diff --git a/test/libc/calls/makedirs_test.c b/test/libc/calls/makedirs_test.c index cf3eb0eca..68239ef6d 100644 --- a/test/libc/calls/makedirs_test.c +++ b/test/libc/calls/makedirs_test.c @@ -25,6 +25,10 @@ #include "libc/thread/thread.h" #include "libc/x/x.h" +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + TEST(makedirs, empty) { ASSERT_SYS(ENOENT, -1, makedirs("", 0755)); } @@ -52,16 +56,11 @@ TEST(makedirs, basic) { ASSERT_TRUE(isdirectory("a/b/c/d/e")); } -#define DIR \ - "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/A/B/C/D/E/F/G/H/I/J/K/" \ - "L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z" +#define DIR \ + "a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/A/B/C/D/E/F/G/H/I/J" pthread_barrier_t barrier; -void SetUpOnce(void) { - testlib_enable_tmp_setup_teardown(); -} - void *Worker(void *arg) { pthread_barrier_wait(&barrier); ASSERT_EQ(0, makedirs(DIR, 0755)); @@ -69,7 +68,6 @@ void *Worker(void *arg) { } TEST(makedirs, test) { - if (IsWindows()) return; // todo: why won't long paths work on windows int i, n = 8; pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); diff --git a/test/libc/fmt/basename_test.c b/test/libc/fmt/basename_test.c index 3dff16909..1bf16fac6 100644 --- a/test/libc/fmt/basename_test.c +++ b/test/libc/fmt/basename_test.c @@ -47,8 +47,3 @@ TEST(basename, testTrailingSlash_isIgnored) { TEST(basename, testOnlySlashes_oneSlashOnlyVasily) { EXPECT_STREQ("/", BASENAME("///")); } - -TEST(basename, testWindows_isGrantedRespect) { - 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 552936573..b4b1a4ea4 100644 --- a/test/libc/fmt/dirname_test.c +++ b/test/libc/fmt/dirname_test.c @@ -17,18 +17,20 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/libgen.h" -#include "libc/mem/gc.internal.h" -#include "libc/mem/mem.h" +#include "libc/str/str.h" #include "libc/testlib/testlib.h" +static char dup[128]; +#define DIRNAME(x) dirname(strcpy(dup, x)) + 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("/", dirname(gc(strdup("/usr/")))); - EXPECT_STREQ(".", dirname(gc(strdup("usr")))); - EXPECT_STREQ("/", dirname(gc(strdup("/")))); - EXPECT_STREQ(".", dirname(gc(strdup(".")))); - EXPECT_STREQ(".", dirname(gc(strdup("..")))); - EXPECT_STREQ(".", dirname(gc(strdup("")))); + EXPECT_STREQ("/usr/lib", DIRNAME("/usr/lib/foo.bar")); + EXPECT_STREQ("/usr", DIRNAME("/usr/lib")); + EXPECT_STREQ("usr", DIRNAME("usr/lib")); + EXPECT_STREQ("/", DIRNAME("/usr/")); + EXPECT_STREQ(".", DIRNAME("usr")); + EXPECT_STREQ("/", DIRNAME("/")); + EXPECT_STREQ(".", DIRNAME(".")); + EXPECT_STREQ(".", DIRNAME("..")); + EXPECT_STREQ(".", DIRNAME("")); } diff --git a/test/libc/str/classifypath_test.c b/test/libc/str/classifypath_test.c deleted file mode 100644 index 666ab280a..000000000 --- a/test/libc/str/classifypath_test.c +++ /dev/null @@ -1,121 +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 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" -#include "libc/testlib/testlib.h" - -TEST(isabspath, testUniversal) { - ASSERT_TRUE(_isabspath("/home/jart/foo.txt")); -} - -TEST(isabspath, testDosPaths) { - if (!SupportsWindows()) return; - 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) { - if (!SupportsWindows()) return; - ASSERT_TRUE(_isabspath("\\\\?\\C:\\..")); - ASSERT_TRUE(_isabspath("\\\\.\\C:\\Users\\jart\\foo.txt")); -} - -TEST(isabspath, testNtPaths) { - if (!SupportsWindows()) return; - ASSERT_TRUE(_isabspath("\\??\\C:\\Users\\jart\\foo.txt")); -} - -TEST(_classifypath, test) { - if (!SupportsWindows()) return; - EXPECT_EQ(0, _classifypath("")); - EXPECT_EQ(0, _classifypath("xyz")); - EXPECT_EQ(_kPathDos | _kPathDev, _classifypath("CON")); - EXPECT_EQ(_kPathDos | _kPathDev, _classifypath("NUL")); - EXPECT_EQ(0, _classifypath(":")); - EXPECT_EQ(_kPathDos, _classifypath("::")); - EXPECT_EQ(_kPathDos, _classifypath(":::")); - EXPECT_EQ(_kPathDos, _classifypath("::::")); - EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("::\\")); - EXPECT_EQ(_kPathAbs, _classifypath("\\")); - EXPECT_EQ(_kPathAbs, _classifypath("\\:")); - EXPECT_EQ(_kPathAbs, _classifypath("\\C:")); - EXPECT_EQ(_kPathAbs, _classifypath("\\C:\\")); - EXPECT_EQ(_kPathAbs, _classifypath("/")); - EXPECT_EQ(_kPathAbs, _classifypath("/:")); - EXPECT_EQ(_kPathAbs, _classifypath("/C:")); - EXPECT_EQ(_kPathAbs, _classifypath("/C:/")); - EXPECT_EQ(0, _classifypath("C")); - EXPECT_EQ(_kPathDos, _classifypath("C:")); - EXPECT_EQ(_kPathDos, _classifypath("C:a")); - EXPECT_EQ(_kPathDos, _classifypath("C:a\\")); - EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:\\")); - EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:/")); - EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:\\a")); - EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:/a")); - EXPECT_EQ(_kPathAbs | _kPathDos, _classifypath("C:\\\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\;")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f\\b\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f\\b")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\f")); - EXPECT_EQ(_kPathAbs | _kPathNt, _classifypath("\\??\\")); - EXPECT_EQ(_kPathAbs | _kPathNt, _classifypath("\\??\\UNC")); - EXPECT_EQ(_kPathAbs | _kPathNt, _classifypath("\\??\\UNC\\")); - EXPECT_EQ(_kPathAbs, _classifypath("\\?")); - EXPECT_EQ(_kPathAbs, _classifypath("\\?\\")); - EXPECT_EQ(_kPathAbs, _classifypath("\\?\\UNC")); - EXPECT_EQ(_kPathAbs, _classifypath("\\?\\UNC\\")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("\\\\?\\UNC\\")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("\\\\?")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\??")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\??\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\\\??\\C:\\")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("\\\\.")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("\\\\.\\")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("\\\\.\\C:\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("\\/")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("/\\")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("//")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("///")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("//;")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("//?")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("/\\?")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("\\/?")); - EXPECT_EQ(_kPathAbs | _kPathWin, _classifypath("//??")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("//.")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("\\/.")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev | _kPathRoot, - _classifypath("/\\.")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("//./")); - EXPECT_EQ(_kPathAbs | _kPathWin | _kPathDev, _classifypath("//./C:/")); -} diff --git a/third_party/lua/lunix.c b/third_party/lua/lunix.c index 1c24bfe91..89e9da785 100644 --- a/third_party/lua/lunix.c +++ b/third_party/lua/lunix.c @@ -67,7 +67,6 @@ #include "libc/sock/syslog.h" #include "libc/stdio/append.h" #include "libc/stdio/stdio.h" -#include "libc/str/path.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/at.h"