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.
This commit is contained in:
Justine Tunney 2023-09-21 08:13:50 -07:00
parent 0c5dd7b342
commit c88f95a892
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
19 changed files with 125 additions and 610 deletions

View file

@ -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;
}

View file

@ -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:

View file

@ -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"

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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();

View file

@ -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),

View file

@ -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;
}
}

View file

@ -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;
}

20
libc/str/path.h Normal file → Executable file
View file

@ -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_ */

View file

@ -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"

View file

@ -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);

View file

@ -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)));
}

View file

@ -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));

View file

@ -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"));
}

View file

@ -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(""));
}

View file

@ -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:/"));
}

View file

@ -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"