Skip to content

Commit caee314

Browse files
committed
Make PATH search do the right thing w/ empty path
1 parent a5f3456 commit caee314

File tree

7 files changed

+147
-40
lines changed

7 files changed

+147
-40
lines changed

libc/calls/commandv.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ static bool AccessCommand(char path[hasatleast PATH_MAX], const char *name,
2727
size_t namelen, size_t pathlen) {
2828
if (pathlen + 1 + namelen + 1 + 4 + 1 > PATH_MAX) return -1;
2929
if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) {
30-
path[pathlen++] = '/';
30+
path[pathlen] =
31+
!IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/';
32+
pathlen++;
3133
}
3234
memcpy(path + pathlen, name, namelen + 1);
3335
if (isexecutable(path)) return true;
@@ -43,15 +45,18 @@ static bool SearchPath(char path[hasatleast PATH_MAX], const char *name,
4345
size_t i;
4446
const char *p;
4547
p = firstnonnull(emptytonull(getenv("PATH")), "/bin:/usr/local/bin:/usr/bin");
46-
for (;; p += i) {
47-
while (*p == ':' || *p == ';') ++p;
48-
if (!*p) break;
49-
for (i = 0; i < PATH_MAX && p[i] && p[i] != ':' && p[i] != ';'; ++i) {
50-
path[i] = p[i];
48+
for (;;) {
49+
for (i = 0; p[i] && p[i] != ':' && p[i] != ';'; ++i) {
50+
if (i < PATH_MAX) path[i] = p[i];
5151
}
5252
if (AccessCommand(path, name, namelen, i)) {
5353
return true;
5454
}
55+
if (p[i] == ':' || p[i] == ';') {
56+
p += i + 1;
57+
} else {
58+
break;
59+
}
5560
}
5661
return false;
5762
}
@@ -71,17 +76,19 @@ char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
7176
int rc, olderr;
7277
if (!(namelen = strlen(name))) return PROGN(enoent(), NULL);
7378
if (namelen + 1 > PATH_MAX) return PROGN(enametoolong(), NULL);
74-
if (name[0] == '/' || name[0] == '\\') {
75-
memcpy(pathbuf, name, namelen + 1);
76-
return pathbuf;
79+
if (strchr(name, '/') || strchr(name, '\\')) {
80+
if (AccessCommand(strcpy(pathbuf, ""), name, namelen, 0)) {
81+
return pathbuf;
82+
} else {
83+
return NULL;
84+
}
7785
}
7886
olderr = errno;
7987
if ((IsWindows() &&
8088
(AccessCommand(pathbuf, name, namelen,
8189
stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) ||
8290
AccessCommand(pathbuf, name, namelen,
8391
stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf))) ||
84-
AccessCommand(strcpy(pathbuf, ""), name, namelen, 0) ||
8592
SearchPath(pathbuf, name, namelen)) {
8693
errno = olderr;
8794
return pathbuf;

libc/log/backtrace2.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
4646
const struct StackFrame *frame;
4747
const char *debugbin, *p1, *p2, *p3, *addr2line;
4848
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
49+
if (IsOpenbsd()) return -1;
50+
if (IsWindows()) return -1;
4951
if (!(debugbin = FindDebugBinary()) || !(addr2line = GetAddr2linePath())) {
5052
return -1;
5153
}

libc/x/rmrf.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2021 Justine Alexandra Roberts Tunney │
5+
│ │
6+
│ Permission to use, copy, modify, and/or distribute this software for │
7+
│ any purpose with or without fee is hereby granted, provided that the │
8+
│ above copyright notice and this permission notice appear in all copies. │
9+
│ │
10+
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
11+
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
12+
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
13+
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
14+
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
15+
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
16+
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17+
│ PERFORMANCE OF THIS SOFTWARE. │
18+
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/calls/calls.h"
20+
#include "libc/calls/struct/dirent.h"
21+
#include "libc/calls/struct/stat.h"
22+
#include "libc/mem/mem.h"
23+
#include "libc/runtime/runtime.h"
24+
#include "libc/stdio/stdio.h"
25+
#include "libc/str/str.h"
26+
#include "libc/sysv/consts/dt.h"
27+
#include "libc/x/x.h"
28+
29+
static int rmrfdir(const char *dirpath) {
30+
int rc;
31+
DIR *d;
32+
char *path;
33+
struct dirent *e;
34+
if (!(d = opendir(dirpath))) return -1;
35+
for (rc = 0; (e = readdir(d));) {
36+
if (!strcmp(e->d_name, ".")) continue;
37+
if (!strcmp(e->d_name, "..")) continue;
38+
if (strchr(e->d_name, '/')) abort();
39+
path = xjoinpaths(dirpath, e->d_name);
40+
if ((e->d_type == DT_DIR ? rmrfdir(path) : unlink(path)) == -1) {
41+
free(path);
42+
closedir(d);
43+
return -1;
44+
}
45+
free(path);
46+
}
47+
return closedir(d);
48+
}
49+
50+
/**
51+
* Recursively removes file or directory.
52+
*/
53+
int rmrf(const char *path) {
54+
struct stat st;
55+
if (stat(path, &st) == -1) return -1;
56+
if (!S_ISDIR(st.st_mode)) return unlink(path);
57+
return rmrfdir(path);
58+
}

libc/x/x.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,15 @@ char *xstrdup(const char *) _XPNN _XMAL;
4545
char *xstrndup(const char *, size_t) _XPNN _XMAL;
4646
char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL;
4747
char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL;
48+
char *xinet_ntop(int, const void *) _XPNN _XMAL;
49+
50+
/*───────────────────────────────────────────────────────────────────────────│─╗
51+
│ cosmopolitan § eXtended apis » files ─╬─│┼
52+
╚────────────────────────────────────────────────────────────────────────────│*/
53+
54+
int rmrf(const char *);
4855
char *xdirname(const char *) paramsnonnull() _XMAL;
4956
char *xjoinpaths(const char *, const char *) paramsnonnull() _XMAL;
50-
char *xinet_ntop(int, const void *) _XPNN _XMAL;
5157

5258
/*───────────────────────────────────────────────────────────────────────────│─╗
5359
│ cosmopolitan § eXtended apis » time ─╬─│┼

libc/x/x.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ LIBC_X_A_CHECKS = \
3434

3535
LIBC_X_A_DIRECTDEPS = \
3636
LIBC_CALLS \
37+
LIBC_CALLS_HEFTY \
3738
LIBC_FMT \
3839
LIBC_INTRIN \
3940
LIBC_MEM \

test/libc/calls/commandv_test.c

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,52 +16,81 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/bits/safemacros.h"
1920
#include "libc/calls/calls.h"
21+
#include "libc/calls/struct/dirent.h"
22+
#include "libc/calls/struct/stat.h"
2023
#include "libc/dce.h"
24+
#include "libc/log/check.h"
2125
#include "libc/mem/mem.h"
2226
#include "libc/runtime/gc.h"
2327
#include "libc/runtime/internal.h"
2428
#include "libc/runtime/runtime.h"
2529
#include "libc/stdio/stdio.h"
30+
#include "libc/str/str.h"
31+
#include "libc/sysv/consts/dt.h"
2632
#include "libc/testlib/testlib.h"
2733
#include "libc/x/x.h"
2834

2935
uint64_t i;
3036
char pathbuf[PATH_MAX];
31-
const char *oldpath, *bindir, *homedir, *binsh, *sh;
37+
const char *testdir, *oldpath;
3238

33-
TEST(commandv_00, todo) { /* TODO(jart): Improve this on Windows. */
34-
if (IsWindows()) exit(0);
35-
}
36-
37-
TEST(commandv_001, setupFiles) {
39+
void SetUp(void) {
3840
mkdir("o", 0755);
3941
mkdir("o/tmp", 0755);
40-
oldpath = strdup(getenv("PATH"));
41-
homedir = xasprintf("o/tmp/home.%d", getpid());
42-
bindir = xasprintf("o/tmp/bin.%d", getpid());
43-
binsh = xasprintf("%s/sh.com", bindir);
44-
ASSERT_NE(-1, mkdir(homedir, 0755));
45-
ASSERT_NE(-1, mkdir(bindir, 0755));
46-
ASSERT_NE(-1, touch(binsh, 0755));
47-
ASSERT_NE(-1, setenv("PATH", bindir, true));
42+
testdir = xasprintf("o/tmp/%s.%d", program_invocation_short_name, getpid());
43+
mkdir(testdir, 0755);
44+
CHECK_NE(-1, chdir(testdir));
45+
mkdir("bin", 0755);
46+
mkdir("home", 0755);
47+
oldpath = strdup(nulltoempty(getenv("PATH")));
48+
CHECK_NE(-1, setenv("PATH", "bin", true));
49+
}
50+
51+
void TearDown(void) {
52+
CHECK_NE(-1, setenv("PATH", oldpath, true));
53+
CHECK_NE(-1, chdir("../../.."));
54+
CHECK_NE(-1, rmrf(testdir));
55+
}
56+
57+
TEST(commandv, testPathSearch) {
58+
EXPECT_NE(-1, touch("bin/sh", 0755));
59+
EXPECT_STREQ("bin/sh", commandv("sh", pathbuf));
60+
}
61+
62+
TEST(commandv, testPathSearch_appendsComExtension) {
63+
EXPECT_NE(-1, touch("bin/sh.com", 0755));
64+
EXPECT_STREQ("bin/sh.com", commandv("sh", pathbuf));
4865
}
4966

50-
TEST(commandv_010, testSlashes_wontSearchPath_butChecksAccess) {
51-
sh = defer(unlink, gc(xasprintf("%s/sh.com", homedir)));
52-
EXPECT_NE(-1, touch(sh, 0755));
67+
TEST(commandv, testSlashes_wontSearchPath_butChecksAccess) {
68+
EXPECT_NE(-1, touch("home/sh", 0755));
5369
i = g_syscount;
54-
EXPECT_STREQ(sh, commandv(sh, pathbuf));
55-
if (!IsWindows()) EXPECT_EQ(i + 1 /* access() */, g_syscount);
70+
EXPECT_STREQ("home/sh", commandv("home/sh", pathbuf));
71+
if (!IsWindows()) EXPECT_EQ(i + 1, g_syscount);
72+
}
73+
74+
TEST(commandv, testSlashes_wontSearchPath_butStillAppendsComExtension) {
75+
EXPECT_NE(-1, touch("home/sh.com", 0755));
76+
i = g_syscount;
77+
EXPECT_STREQ("home/sh.com", commandv("home/sh", pathbuf));
78+
if (!IsWindows()) EXPECT_EQ(i + 2, g_syscount);
79+
}
80+
81+
TEST(commandv, testSameDir_doesntHappenByDefault) {
82+
EXPECT_NE(-1, touch("bog", 0755));
83+
EXPECT_EQ(NULL, commandv("bog", pathbuf));
84+
}
85+
86+
TEST(commandv, testSameDir_willHappenWithColonBlank) {
87+
CHECK_NE(-1, setenv("PATH", "bin:", true));
88+
EXPECT_NE(-1, touch("bog", 0755));
89+
EXPECT_STREQ("bog", commandv("bog", pathbuf));
5690
}
5791

58-
TEST(commandv_999, teardown) {
59-
setenv("PATH", oldpath, true);
60-
unlink(binsh);
61-
rmdir(bindir);
62-
rmdir(homedir);
63-
free(bindir);
64-
free(binsh);
65-
free(homedir);
66-
free(oldpath);
92+
TEST(commandv, testSameDir_willHappenWithColonBlank2) {
93+
CHECK_NE(-1, setenv("PATH", ":bin", true));
94+
EXPECT_NE(-1, touch("bog", 0755));
95+
EXPECT_STREQ("bog", commandv("bog", pathbuf));
6796
}

tool/build/runitd.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,11 @@ int Serve(void) {
393393
if (!Poll() && (!g_timeout || g_interrupted)) break;
394394
}
395395
close(g_servfd);
396-
LOGF("timeout expired, shutting down");
396+
if (!g_timeout) {
397+
LOGF("timeout expired, shutting down");
398+
} else {
399+
LOGF("got ctrl-c, shutting down");
400+
}
397401
return 0;
398402
}
399403

0 commit comments

Comments
 (0)