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

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