Further improve cocmd interpreter

This commit is contained in:
Justine Tunney 2022-10-12 10:44:54 -07:00
parent 0cee831da3
commit 0f89140882
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
14 changed files with 278 additions and 181 deletions

View file

@ -129,8 +129,9 @@ int linkat(int, const char *, int, const char *, int);
int madvise(void *, uint64_t, int); int madvise(void *, uint64_t, int);
int memfd_create(const char *, unsigned int); int memfd_create(const char *, unsigned int);
int mincore(void *, size_t, unsigned char *); int mincore(void *, size_t, unsigned char *);
int mkdir(const char *, uint32_t); int mkdir(const char *, unsigned);
int mkdirat(int, const char *, uint32_t); int mkdirat(int, const char *, unsigned);
int makedirs(const char *, unsigned);
int mkfifo(const char *, uint32_t); int mkfifo(const char *, uint32_t);
int mkfifoat(int, const char *, uint32_t); int mkfifoat(int, const char *, uint32_t);
int mknod(const char *, uint32_t, uint64_t); int mknod(const char *, uint32_t, uint64_t);

View file

@ -75,10 +75,10 @@
* @return 0 on success, or errno on error * @return 0 on success, or errno on error
* @raise EINTR when a signal got delivered while we were waiting * @raise EINTR when a signal got delivered while we were waiting
* @raise ENOTSUP if `clock` is known but we can't use it here * @raise ENOTSUP if `clock` is known but we can't use it here
* @raise EFAULT if `req` or null or bad memory was passed
* @raise EINVAL if `clock` is unknown to current platform * @raise EINVAL if `clock` is unknown to current platform
* @raise EINVAL if `flags` has an unrecognized value * @raise EINVAL if `flags` has an unrecognized value
* @raise EINVAL if `req->tv_nsec [0,1000000000)` * @raise EINVAL if `req->tv_nsec [0,1000000000)`
* @raise EFAULT if bad memory was passed
* @raise ENOSYS on bare metal * @raise ENOSYS on bare metal
* @returnserrno * @returnserrno
* @norestart * @norestart

View file

@ -17,58 +17,75 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/conv.h" #include "libc/str/path.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/s.h"
static char *DirName(const char *path) { #include "libc/sysv/errfuns.h"
char *dirp;
if (!(path = strdup(path))) return 0;
dirp = strdup(dirname(path));
free(path);
return dirp;
}
static int MakeDirs(const char *path, unsigned mode, int e) {
int rc;
char *dir;
if (!mkdir(path, mode) || errno == EEXIST) {
errno = e;
return 0;
}
if (errno != ENOENT) {
return -1;
}
if (!(dir = DirName(path))) {
return -1;
}
if (strcmp(dir, path)) {
rc = MakeDirs(dir, mode, e);
} else {
rc = -1;
}
free(dir);
if (rc == -1) return -1;
errno = e;
if (!mkdir(path, mode) || errno == EEXIST) {
errno = e;
return 0;
} else {
return -1;
}
}
/** /**
* Recursively creates directory a.k.a. folder. * Creates directory and parent components.
* *
* This function won't fail if the directory already exists. * This function is similar to mkdir() except it iteratively creates
* parent directories and it won't fail if the directory already exists.
* *
* @param path is a UTF-8 string, preferably relative w/ forward slashes * @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755 * @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno * @return 0 on success or -1 w/ errno
* @raise EEXIST if named file already exists as non-directory
* @raise ENOTDIR if directory component in `path` existed as non-directory
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise EROFS if parent directory is on read-only filesystem
* @raise ENOSPC if file system or parent directory is full
* @raise EACCES if write permission was denied on parent directory
* @raise EACCES if search permission was denied on component in `path`
* @raise ENOENT if `path` is an empty string
* @raise ELOOP if loop was detected resolving components of `path`
* @asyncsignalsafe
* @threadsafe * @threadsafe
*/ */
int makedirs(const char *path, unsigned mode) { int makedirs(const char *path, unsigned mode) {
return MakeDirs(path, mode, errno); int c, e, i, n;
struct stat st;
char buf[PATH_MAX];
e = errno;
n = strlen(path);
if (n >= PATH_MAX) return enametoolong();
memcpy(buf, path, n + 1);
i = n;
// descend
while (i) {
if (!mkdir(buf, mode)) break;
if (errno == EEXIST) {
if (i == n) goto CheckTop;
break;
}
if (errno != ENOENT) return -1;
while (i && _isdirsep(buf[i - 1])) buf[--i] = 0;
while (i && !_isdirsep(buf[i - 1])) buf[--i] = 0;
}
// ascend
for (;;) {
if (mkdir(buf, mode)) {
if (errno != EEXIST) return -1;
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;
}
Finish:
errno = e;
return 0;
CheckTop:
if (stat(path, &st)) return -1;
if (S_ISDIR(st.st_mode)) goto Finish;
return eexist();
} }

View file

@ -17,12 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
/** /**
* Creates directory a.k.a. folder. * Creates directory a.k.a. folder.
@ -36,9 +31,21 @@
* @param path is a UTF-8 string, preferably relative w/ forward slashes * @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755 * @param mode can be, for example, 0755
* @return 0 on success or -1 w/ errno * @return 0 on success or -1 w/ errno
* @error ENAMETOOLONG if >246 characters on NT * @raise EEXIST if named file already exists
* @raise ENOTDIR if directory component in `path` existed as non-directory
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise EROFS if parent directory is on read-only filesystem
* @raise ENOSPC if file system or parent directory is full
* @raise EACCES if write permission was denied on parent directory
* @raise EACCES if search permission was denied on component in `path`
* @raise ENOENT if a component within `path` didn't exist
* @raise ENOENT if `path` is an empty string
* @raise ELOOP if loop was detected resolving components of `path`
* @see makedirs() which is higher-level
* @see mkdirat() for modern call
* @asyncsignalsafe * @asyncsignalsafe
* @see makedirs() * @threadsafe
*/ */
int mkdir(const char *path, unsigned mode) { int mkdir(const char *path, unsigned mode) {
return mkdirat(AT_FDCWD, path, mode); return mkdirat(AT_FDCWD, path, mode);

View file

@ -31,12 +31,23 @@
/** /**
* Creates directory a.k.a. folder. * Creates directory a.k.a. folder.
* *
* @param dirfd is normally AT_FDCWD but if it's an open directory and * @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* path is relative, then path becomes relative to dirfd * path is relative, then path becomes relative to dirfd
* @param path is a UTF-8 string, preferably relative w/ forward slashes * @param path is a UTF-8 string, preferably relative w/ forward slashes
* @param mode can be, for example, 0755 * @param mode is permissions bits, which is usually 0755
* @return 0 on success or -1 w/ errno * @return 0 on success, or -1 w/ errno
* @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES, ENOENT * @raise EEXIST if named file already exists
* @raise EBADF if `path` is relative and `dirfd` isn't `AT_FDCWD` or valid
* @raise ENOTDIR if directory component in `path` existed as non-directory
* @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX`
* @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX`
* @raise EROFS if parent directory is on read-only filesystem
* @raise ENOSPC if file system or parent directory is full
* @raise EACCES if write permission was denied on parent directory
* @raise EACCES if search permission was denied on component in `path`
* @raise ENOENT if a component within `path` didn't exist
* @raise ENOENT if `path` is an empty string
* @raise ELOOP if loop was detected resolving components of `path`
* @asyncsignalsafe * @asyncsignalsafe
* @see makedirs() * @see makedirs()
*/ */

View file

@ -20,22 +20,19 @@
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h" #include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h" #include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/safemacros.internal.h" #include "libc/intrin/bits.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h" #include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/x/x.h" #include "libc/sysv/consts/timer.h"
#include "third_party/double-conversion/wrapper.h"
/** /**
* @fileoverview Cosmopolitan Command Interpreter * @fileoverview Cosmopolitan Command Interpreter
@ -51,15 +48,25 @@
#define STATE_QUOTED_VAR 4 #define STATE_QUOTED_VAR 4
#define STATE_WHITESPACE 5 #define STATE_WHITESPACE 5
#define READ24LE(s) READ32LE(s "\0")
struct Env {
char *s;
int i;
};
static char *p; static char *p;
static char *q; static char *q;
static char *r;
static int envi;
static int vari; static int vari;
static size_t n; static size_t n;
static char *cmd; static char *cmd;
static char var[32]; static char var[32];
static int lastchild; static int lastchild;
static int exitstatus; static int exitstatus;
static char *args[8192]; static char *envs[3000];
static char *args[3000];
static const char *prog; static const char *prog;
static char errbuf[512]; static char errbuf[512];
static char argbuf[ARG_MAX]; static char argbuf[ARG_MAX];
@ -94,13 +101,10 @@ static wontreturn void Wexit(int rc, const char *s, ...) {
} }
static wontreturn void UnsupportedSyntax(unsigned char c) { static wontreturn void UnsupportedSyntax(unsigned char c) {
char cbuf[2]; char ibuf[13], cbuf[2] = {c};
char ibuf[13];
cbuf[0] = c;
cbuf[1] = 0;
FormatOctal32(ibuf, c, true); FormatOctal32(ibuf, c, true);
Wexit(4, prog, ": unsupported shell syntax '", cbuf, "' (", ibuf, "): ", cmd, Wexit(4, prog, ": unsupported syntax '", cbuf, "' (", ibuf, "): ", cmd, "\n",
"\n", 0); 0);
} }
static wontreturn void SysExit(int rc, const char *call, const char *thing) { static wontreturn void SysExit(int rc, const char *call, const char *thing) {
@ -123,9 +127,9 @@ static void Open(const char *path, int fd, int flags) {
} }
static wontreturn void Exec(void) { static wontreturn void Exec(void) {
_npassert(args[0][0]); _unassert(args[0][0]);
if (!n) Wexit(5, prog, ": error: too few args\n", 0); if (!n) Wexit(5, prog, ": error: too few args\n", 0);
execvp(args[0], args); execvpe(args[0], args, envs);
SysExit(127, "execve", args[0]); SysExit(127, "execve", args[0]);
} }
@ -138,6 +142,42 @@ static int GetSignalByName(const char *s) {
return 0; return 0;
} }
static struct Env GetEnv(char **p, const char *k) {
int i, j;
for (i = 0; p[i]; ++i) {
for (j = 0;; ++j) {
if (!k[j] || k[j] == '=') {
if (p[i][j] == '=') {
return (struct Env){p[i] + j + 1, i};
}
break;
}
if (toupper(k[j] & 255) != toupper(p[i][j] & 255)) {
break;
}
}
}
return (struct Env){0, i};
}
static void PutEnv(char **p, const char *kv) {
struct Env e;
e = GetEnv(p, kv);
p[e.i] = kv;
if (!e.s) p[e.i + 1] = 0;
}
static void Append(int c) {
_npassert(q + 1 < argbuf + sizeof(argbuf));
*q++ = c;
}
static char *Finish(void) {
char *s = r;
Append(0);
return r = q, s;
}
static int True(void) { static int True(void) {
return 0; return 0;
} }
@ -194,31 +234,31 @@ static int Echo(void) {
} }
static int Read(void) { static int Read(void) {
char *b = 0;
unsigned char c; unsigned char c;
int a = 1, rc = 1; int i, j, rc = 1;
if (n >= 3 && !strcmp(args[1], "-p")) { for (i = 1; i < n; ++i) {
Write(1, args[2]); if (args[i][0] != '-') break;
a = 3; if (args[i][1] == 'p' && !args[i][2] && i + 1 < n) {
} Write(1, args[++i]);
appendr(&b, 0);
while (read(0, &c, 1) > 0) {
if (c == '\n') {
rc = 0;
break;
} }
appendw(&b, c);
} }
if (a < n) { if (i >= n) return 1;
setenv(args[1], b, true); for (j = 0; args[i][j]; ++j) {
Append(args[i][j]);
} }
free(b); Append('=');
while (read(0, &c, 1) > 0) {
if (c == '\n') break;
Append(c);
rc = 0;
}
PutEnv(envs, Finish());
return rc; return rc;
} }
static int Cd(void) { static int Cd(void) {
const char *s = n > 1 ? args[1] : getenv("HOME"); const char *s;
if (s) { if ((s = n > 1 ? args[1] : GetEnv(envs, "HOME").s)) {
if (!chdir(s)) { if (!chdir(s)) {
return 0; return 0;
} else { } else {
@ -274,36 +314,37 @@ static int Toupper(void) {
} }
static int Usleep(void) { static int Usleep(void) {
struct timespec t, *p = 0; int f;
struct timespec t;
if (n > 1) { if (n > 1) {
f = 0;
t = _timespec_frommicros(atoi(args[1])); t = _timespec_frommicros(atoi(args[1]));
p = &t; } else {
f = TIMER_ABSTIME;
t = _timespec_max;
} }
return clock_nanosleep(0, 0, p, 0); return clock_nanosleep(0, f, &t, 0);
} }
static int Test(void) { static int Test(void) {
int w;
struct stat st; struct stat st;
if (n && !strcmp(args[n - 1], "]")) --n; if (n && READ16LE(args[n - 1]) == READ16LE("]")) --n;
if (n == 4 && !strcmp(args[2], "=")) { if (n == 4) {
return !!strcmp(args[1], args[3]); w = READ32LE(args[2]) & 0x00ffffff;
} else if (n == 4 && !strcmp(args[2], "!=")) { if ((w & 65535) == READ16LE("=")) return !!strcmp(args[1], args[3]);
return !strcmp(args[1], args[3]); if (w == READ24LE("==")) return !!strcmp(args[1], args[3]);
} else if (n == 3 && !strcmp(args[1], "-n")) { if (w == READ24LE("!=")) return !strcmp(args[1], args[3]);
return !(strlen(args[2]) > 0); } else if (n == 3) {
} else if (n == 3 && !strcmp(args[1], "-z")) { w = READ32LE(args[1]) & 0x00ffffff;
return !(strlen(args[2]) == 0); if (w == READ24LE("-n")) return !(strlen(args[2]) > 0);
} else if (n == 3 && !strcmp(args[1], "-e")) { if (w == READ24LE("-z")) return !(strlen(args[2]) == 0);
return !!stat(args[2], &st); if (w == READ24LE("-e")) return !!stat(args[2], &st);
} else if (n == 3 && !strcmp(args[1], "-f")) { if (w == READ24LE("-f")) return !stat(args[2], &st) && S_ISREG(st.st_mode);
return !stat(args[2], &st) && S_ISREG(st.st_mode); if (w == READ24LE("-d")) return !stat(args[2], &st) && S_ISDIR(st.st_mode);
} else if (n == 3 && !strcmp(args[1], "-d")) { if (w == READ24LE("-h")) return !stat(args[2], &st) && S_ISLNK(st.st_mode);
return !stat(args[2], &st) && S_ISDIR(st.st_mode);
} else if (n == 3 && !strcmp(args[1], "-h")) {
return !stat(args[2], &st) && S_ISLNK(st.st_mode);
} else {
return 1;
} }
return 1;
} }
static int TryBuiltin(void) { static int TryBuiltin(void) {
@ -370,7 +411,7 @@ static const char *IntToStr(int x) {
return ibuf; return ibuf;
} }
static const char *GetEnv(const char *key) { static const char *GetVar(const char *key) {
if (key[0] == '$' && !key[1]) { if (key[0] == '$' && !key[1]) {
return IntToStr(getpid()); return IntToStr(getpid());
} else if (key[0] == '!' && !key[1]) { } else if (key[0] == '!' && !key[1]) {
@ -378,7 +419,7 @@ static const char *GetEnv(const char *key) {
} else if (key[0] == '?' && !key[1]) { } else if (key[0] == '?' && !key[1]) {
return IntToStr(exitstatus); return IntToStr(exitstatus);
} else { } else {
return getenv(key); return GetEnv(envs, key).s;
} }
} }
@ -387,16 +428,28 @@ static bool IsVarName(int c) {
(!vari && (c == '?' || c == '!' || c == '$')); (!vari && (c == '?' || c == '!' || c == '$'));
} }
static inline void Append(int c) { static bool CopyVar(void) {
_unassert(q + 1 < argbuf + sizeof(argbuf)); size_t j;
*q++ = c; const char *s;
if (IsVarName(*p)) {
_npassert(vari + 1 < sizeof(var));
var[vari++] = *p;
var[vari] = 0;
return false;
}
if ((s = GetVar(var))) {
if ((j = strlen(s))) {
_npassert(q + j < argbuf + sizeof(argbuf));
q = mempcpy(q, s, j);
}
}
return true;
} }
static char *Tokenize(void) { static char *Tokenize(void) {
char *r;
const char *s; const char *s;
int c, t, j, k, rc; int c, t, j, k, rc;
for (r = q, t = STATE_WHITESPACE;; ++p) { for (t = STATE_WHITESPACE;; ++p) {
switch (t) { switch (t) {
case STATE_WHITESPACE: case STATE_WHITESPACE:
@ -413,8 +466,7 @@ static char *Tokenize(void) {
UnsupportedSyntax(*p); UnsupportedSyntax(*p);
} }
if (!*p || *p == ' ' || *p == '\t') { if (!*p || *p == ' ' || *p == '\t') {
Append(0); return Finish();
return r;
} else if (*p == '"') { } else if (*p == '"') {
t = STATE_QUOTED; t = STATE_QUOTED;
} else if (*p == '\'') { } else if (*p == '\'') {
@ -427,8 +479,7 @@ static char *Tokenize(void) {
Append(*++p); Append(*++p);
} else if (*p == '|') { } else if (*p == '|') {
if (q > r) { if (q > r) {
Append(0); return Finish();
return r;
} else if (p[1] == '|') { } else if (p[1] == '|') {
rc = Run(); rc = Run();
if (!rc) { if (!rc) {
@ -443,16 +494,14 @@ static char *Tokenize(void) {
} }
} else if (*p == ';') { } else if (*p == ';') {
if (q > r) { if (q > r) {
Append(0); return Finish();
return r;
} else { } else {
Run(); Run();
t = STATE_WHITESPACE; t = STATE_WHITESPACE;
} }
} else if (*p == '&') { } else if (*p == '&') {
if (q > r) { if (q > r) {
Append(0); return Finish();
return r;
} else if (p[1] == '&') { } else if (p[1] == '&') {
rc = Run(); rc = Run();
if (!rc) { if (!rc) {
@ -471,22 +520,9 @@ static char *Tokenize(void) {
break; break;
case STATE_VAR: case STATE_VAR:
if (IsVarName(*p)) { // XXX: we need to find a simple elegant way to break up
_unassert(vari + 1 < sizeof(var)); // unquoted variable expansions into multiple args.
var[vari++] = *p; if (CopyVar()) t = STATE_CMD, --p;
var[vari] = 0;
} else {
// XXX: we need to find a simple elegant way to break up
// unquoted variable expansions into multiple args.
if ((s = GetEnv(var))) {
if ((j = strlen(s))) {
_unassert(q + j < argbuf + sizeof(argbuf));
q = mempcpy(q, s, j);
}
}
--p;
t = STATE_CMD;
}
break; break;
case STATE_SINGLE: case STATE_SINGLE:
@ -501,6 +537,11 @@ static char *Tokenize(void) {
UnterminatedString: UnterminatedString:
Wexit(6, "cmd: error: unterminated string\n", 0); Wexit(6, "cmd: error: unterminated string\n", 0);
case STATE_QUOTED_VAR:
if (!*p) goto UnterminatedString;
if (CopyVar()) t = STATE_QUOTED, --p;
break;
case STATE_QUOTED: case STATE_QUOTED:
if (!*p) goto UnterminatedString; if (!*p) goto UnterminatedString;
if (*p == '"') { if (*p == '"') {
@ -529,24 +570,6 @@ static char *Tokenize(void) {
} }
break; break;
case STATE_QUOTED_VAR:
if (!*p) goto UnterminatedString;
if (IsVarName(*p)) {
_unassert(vari + 1 < sizeof(var));
var[vari++] = *p;
var[vari] = 0;
} else {
if ((s = GetEnv(var))) {
if ((j = strlen(s))) {
_unassert(q + j < argbuf + sizeof(argbuf));
q = mempcpy(q, s, j);
}
}
--p;
t = STATE_QUOTED;
}
break;
default: default:
unreachable; unreachable;
} }
@ -563,7 +586,7 @@ static const char *GetRedirectArg(const char *prog, const char *arg, int n) {
} }
} }
int cocmd(int argc, char *argv[]) { int _cocmd(int argc, char **argv, char **envp) {
char *arg; char *arg;
size_t i, j; size_t i, j;
prog = argc > 0 ? argv[0] : "cocmd.com"; prog = argc > 0 ? argv[0] : "cocmd.com";
@ -596,8 +619,19 @@ int cocmd(int argc, char *argv[]) {
Wexit(12, prog, ": error: cmd too long: ", cmd, "\n", 0); Wexit(12, prog, ": error: cmd too long: ", cmd, "\n", 0);
} }
// copy environment variables
envi = 0;
if (envp) {
for (; envp[envi]; ++envi) {
_npassert(envi + 1 < ARRAYLEN(envs));
envs[envi] = envp[envi];
}
}
envs[envi] = 0;
// get command line arguments
n = 0; n = 0;
q = argbuf; r = q = argbuf;
while ((arg = Tokenize())) { while ((arg = Tokenize())) {
if (n + 1 < ARRAYLEN(args)) { if (n + 1 < ARRAYLEN(args)) {
if (isdigit(arg[0]) && arg[1] == '>' && arg[2] == '&' && if (isdigit(arg[0]) && arg[1] == '>' && arg[2] == '&' &&

View file

@ -53,6 +53,7 @@ void mcount(void);
int daemon(int, int); int daemon(int, int);
int _freestack(void *); int _freestack(void *);
void _bt(const char *, ...); void _bt(const char *, ...);
int _cocmd(int, char **, char **);
unsigned long getauxval(unsigned long); unsigned long getauxval(unsigned long);
void *_mapanon(size_t) attributeallocsize((1)) mallocesque; void *_mapanon(size_t) attributeallocsize((1)) mallocesque;
void *_mapshared(size_t) attributeallocsize((1)) mallocesque; void *_mapshared(size_t) attributeallocsize((1)) mallocesque;

View file

@ -1,10 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_STDIO_COCMD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_STDIO_COCMD_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int cocmd(int, char **);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_STDIO_COCMD_INTERNAL_H_ */

View file

@ -21,7 +21,6 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/paths.h" #include "libc/paths.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/cocmd.internal.h"
#include "libc/stdio/internal.h" #include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/f.h"
@ -57,7 +56,7 @@ FILE *popen(const char *cmdline, const char *mode) {
// we can't rely on cloexec because cocmd builtins don't execve // we can't rely on cloexec because cocmd builtins don't execve
if (pipefds[0] != !dir) _unassert(!close(pipefds[0])); if (pipefds[0] != !dir) _unassert(!close(pipefds[0]));
if (pipefds[1] != !dir) _unassert(!close(pipefds[1])); if (pipefds[1] != !dir) _unassert(!close(pipefds[1]));
_Exit(cocmd(3, (char *[]){"popen", "-c", cmdline, 0})); _Exit(_cocmd(3, (char *[]){"popen", "-c", cmdline, 0}, environ));
default: default:
f->pid = pid; f->pid = pid;
_unassert(!close(pipefds[!dir])); _unassert(!close(pipefds[!dir]));

View file

@ -86,7 +86,6 @@ int setvbuf(FILE *, char *, int, size_t);
int pclose(FILE *); int pclose(FILE *);
char *ctermid(char *); char *ctermid(char *);
void perror(const char *) relegated; void perror(const char *) relegated;
int makedirs(const char *, unsigned);
typedef uint64_t fpos_t; typedef uint64_t fpos_t;
char *gets(char *) paramsnonnull(); char *gets(char *) paramsnonnull();

View file

@ -24,8 +24,6 @@
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/paths.h" #include "libc/paths.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/cocmd.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/ok.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
@ -57,7 +55,7 @@ int system(const char *cmdline) {
sigaction(SIGINT, &saveint, 0); sigaction(SIGINT, &saveint, 0);
sigaction(SIGQUIT, &savequit, 0); sigaction(SIGQUIT, &savequit, 0);
sigprocmask(SIG_SETMASK, &savemask, 0); sigprocmask(SIG_SETMASK, &savemask, 0);
_Exit(cocmd(3, (char *[]){"system", "-c", cmdline, 0})); _Exit(_cocmd(3, (char *[]){"system", "-c", cmdline, 0}, environ));
} else if (pid != -1) { } else if (pid != -1) {
while (wait4(pid, &wstatus, 0, 0) == -1) { while (wait4(pid, &wstatus, 0, 0) == -1) {
if (errno != EINTR) { if (errno != EINTR) {

View file

@ -16,12 +16,41 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/mem/gc.internal.h" #include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/thread/spawn.h" #include "libc/thread/spawn.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/x/x.h"
TEST(makedirs, empty) {
ASSERT_SYS(ENOENT, -1, makedirs("", 0755));
}
TEST(makedirs, isfile) {
ASSERT_SYS(0, 0, xbarf("f", "hi", -1));
ASSERT_SYS(EEXIST, -1, makedirs("f", 0755));
}
TEST(makedirs, isdir) {
ASSERT_SYS(0, 0, mkdir("d", 0755));
ASSERT_SYS(0, 0, makedirs("d", 0755));
}
TEST(makedirs, enotdir) {
ASSERT_SYS(0, 0, xbarf("f", "hi", -1));
ASSERT_SYS(ENOTDIR, -1, makedirs("f/d", 0755));
}
TEST(makedirs, basic) {
ASSERT_SYS(0, 0, makedirs("a/b/c", 0755));
ASSERT_TRUE(isdirectory("a/b/c"));
ASSERT_SYS(0, 0, makedirs("a/b/c/d/e/", 0755));
ASSERT_SYS(0, 0, makedirs("a/b/c/d/e/", 0755));
ASSERT_TRUE(isdirectory("a/b/c/d/e"));
}
#define DIR \ #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/" \ "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/" \

View file

@ -17,12 +17,14 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/mem/copyfd.internal.h" #include "libc/mem/copyfd.internal.h"
#include "libc/mem/gc.h" #include "libc/mem/gc.h"
#include "libc/paths.h" #include "libc/paths.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/ezbench.h" #include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -105,3 +107,12 @@ TEST(system, notequals) {
ASSERT_EQ(1, WEXITSTATUS(system("test a != a"))); ASSERT_EQ(1, WEXITSTATUS(system("test a != a")));
ASSERT_EQ(0, WEXITSTATUS(system("test a != b"))); ASSERT_EQ(0, WEXITSTATUS(system("test a != b")));
} }
TEST(system, usleep) {
ASSERT_EQ(0, WEXITSTATUS(system("usleep & kill $!")));
}
TEST(system, kill) {
int ws = system("kill -TERM $$; usleep");
if (!IsWindows()) ASSERT_EQ(SIGTERM, WTERMSIG(ws));
}

View file

@ -16,8 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/stdio/cocmd.internal.h" #include "libc/runtime/runtime.h"
int main(int argc, char *argv[]) { int main(int argc, char **argv, char **envp) {
return cocmd(argc, argv); return _cocmd(argc, argv, envp);
} }