mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-02 23:18:44 +00:00
Further improve cocmd interpreter
This commit is contained in:
parent
0cee831da3
commit
0f89140882
14 changed files with 278 additions and 181 deletions
|
@ -129,8 +129,9 @@ int linkat(int, const char *, int, const char *, int);
|
|||
int madvise(void *, uint64_t, int);
|
||||
int memfd_create(const char *, unsigned int);
|
||||
int mincore(void *, size_t, unsigned char *);
|
||||
int mkdir(const char *, uint32_t);
|
||||
int mkdirat(int, const char *, uint32_t);
|
||||
int mkdir(const char *, unsigned);
|
||||
int mkdirat(int, const char *, unsigned);
|
||||
int makedirs(const char *, unsigned);
|
||||
int mkfifo(const char *, uint32_t);
|
||||
int mkfifoat(int, const char *, uint32_t);
|
||||
int mknod(const char *, uint32_t, uint64_t);
|
||||
|
|
|
@ -75,10 +75,10 @@
|
|||
* @return 0 on success, or errno on error
|
||||
* @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 EFAULT if `req` or null or bad memory was passed
|
||||
* @raise EINVAL if `clock` is unknown to current platform
|
||||
* @raise EINVAL if `flags` has an unrecognized value
|
||||
* @raise EINVAL if `req->tv_nsec ∉ [0,1000000000)`
|
||||
* @raise EFAULT if bad memory was passed
|
||||
* @raise ENOSYS on bare metal
|
||||
* @returnserrno
|
||||
* @norestart
|
||||
|
|
|
@ -17,58 +17,75 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/path.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static char *DirName(const char *path) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* 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 mode can be, for example, 0755
|
||||
* @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
|
||||
*/
|
||||
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();
|
||||
}
|
|
@ -17,12 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#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/errfuns.h"
|
||||
|
||||
/**
|
||||
* Creates directory a.k.a. folder.
|
||||
|
@ -36,9 +31,21 @@
|
|||
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
||||
* @param mode can be, for example, 0755
|
||||
* @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
|
||||
* @see makedirs()
|
||||
* @threadsafe
|
||||
*/
|
||||
int mkdir(const char *path, unsigned mode) {
|
||||
return mkdirat(AT_FDCWD, path, mode);
|
||||
|
|
|
@ -31,12 +31,23 @@
|
|||
/**
|
||||
* 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
|
||||
* @param path is a UTF-8 string, preferably relative w/ forward slashes
|
||||
* @param mode can be, for example, 0755
|
||||
* @return 0 on success or -1 w/ errno
|
||||
* @error EEXIST, ENOTDIR, ENAMETOOLONG, EACCES, ENOENT
|
||||
* @param mode is permissions bits, which is usually 0755
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @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
|
||||
* @see makedirs()
|
||||
*/
|
||||
|
|
|
@ -20,22 +20,19 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/itoa.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/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/append.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/double-conversion/wrapper.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Command Interpreter
|
||||
|
@ -51,15 +48,25 @@
|
|||
#define STATE_QUOTED_VAR 4
|
||||
#define STATE_WHITESPACE 5
|
||||
|
||||
#define READ24LE(s) READ32LE(s "\0")
|
||||
|
||||
struct Env {
|
||||
char *s;
|
||||
int i;
|
||||
};
|
||||
|
||||
static char *p;
|
||||
static char *q;
|
||||
static char *r;
|
||||
static int envi;
|
||||
static int vari;
|
||||
static size_t n;
|
||||
static char *cmd;
|
||||
static char var[32];
|
||||
static int lastchild;
|
||||
static int exitstatus;
|
||||
static char *args[8192];
|
||||
static char *envs[3000];
|
||||
static char *args[3000];
|
||||
static const char *prog;
|
||||
static char errbuf[512];
|
||||
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) {
|
||||
char cbuf[2];
|
||||
char ibuf[13];
|
||||
cbuf[0] = c;
|
||||
cbuf[1] = 0;
|
||||
char ibuf[13], cbuf[2] = {c};
|
||||
FormatOctal32(ibuf, c, true);
|
||||
Wexit(4, prog, ": unsupported shell syntax '", cbuf, "' (", ibuf, "): ", cmd,
|
||||
"\n", 0);
|
||||
Wexit(4, prog, ": unsupported syntax '", cbuf, "' (", ibuf, "): ", cmd, "\n",
|
||||
0);
|
||||
}
|
||||
|
||||
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) {
|
||||
_npassert(args[0][0]);
|
||||
_unassert(args[0][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]);
|
||||
}
|
||||
|
||||
|
@ -138,6 +142,42 @@ static int GetSignalByName(const char *s) {
|
|||
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) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -194,31 +234,31 @@ static int Echo(void) {
|
|||
}
|
||||
|
||||
static int Read(void) {
|
||||
char *b = 0;
|
||||
unsigned char c;
|
||||
int a = 1, rc = 1;
|
||||
if (n >= 3 && !strcmp(args[1], "-p")) {
|
||||
Write(1, args[2]);
|
||||
a = 3;
|
||||
}
|
||||
appendr(&b, 0);
|
||||
while (read(0, &c, 1) > 0) {
|
||||
if (c == '\n') {
|
||||
rc = 0;
|
||||
break;
|
||||
int i, j, rc = 1;
|
||||
for (i = 1; i < n; ++i) {
|
||||
if (args[i][0] != '-') break;
|
||||
if (args[i][1] == 'p' && !args[i][2] && i + 1 < n) {
|
||||
Write(1, args[++i]);
|
||||
}
|
||||
appendw(&b, c);
|
||||
}
|
||||
if (a < n) {
|
||||
setenv(args[1], b, true);
|
||||
if (i >= n) return 1;
|
||||
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;
|
||||
}
|
||||
|
||||
static int Cd(void) {
|
||||
const char *s = n > 1 ? args[1] : getenv("HOME");
|
||||
if (s) {
|
||||
const char *s;
|
||||
if ((s = n > 1 ? args[1] : GetEnv(envs, "HOME").s)) {
|
||||
if (!chdir(s)) {
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -274,36 +314,37 @@ static int Toupper(void) {
|
|||
}
|
||||
|
||||
static int Usleep(void) {
|
||||
struct timespec t, *p = 0;
|
||||
int f;
|
||||
struct timespec t;
|
||||
if (n > 1) {
|
||||
f = 0;
|
||||
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) {
|
||||
int w;
|
||||
struct stat st;
|
||||
if (n && !strcmp(args[n - 1], "]")) --n;
|
||||
if (n == 4 && !strcmp(args[2], "=")) {
|
||||
return !!strcmp(args[1], args[3]);
|
||||
} else if (n == 4 && !strcmp(args[2], "!=")) {
|
||||
return !strcmp(args[1], args[3]);
|
||||
} else if (n == 3 && !strcmp(args[1], "-n")) {
|
||||
return !(strlen(args[2]) > 0);
|
||||
} else if (n == 3 && !strcmp(args[1], "-z")) {
|
||||
return !(strlen(args[2]) == 0);
|
||||
} else if (n == 3 && !strcmp(args[1], "-e")) {
|
||||
return !!stat(args[2], &st);
|
||||
} else if (n == 3 && !strcmp(args[1], "-f")) {
|
||||
return !stat(args[2], &st) && S_ISREG(st.st_mode);
|
||||
} else if (n == 3 && !strcmp(args[1], "-d")) {
|
||||
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;
|
||||
if (n && READ16LE(args[n - 1]) == READ16LE("]")) --n;
|
||||
if (n == 4) {
|
||||
w = READ32LE(args[2]) & 0x00ffffff;
|
||||
if ((w & 65535) == READ16LE("=")) return !!strcmp(args[1], args[3]);
|
||||
if (w == READ24LE("==")) return !!strcmp(args[1], args[3]);
|
||||
if (w == READ24LE("!=")) return !strcmp(args[1], args[3]);
|
||||
} else if (n == 3) {
|
||||
w = READ32LE(args[1]) & 0x00ffffff;
|
||||
if (w == READ24LE("-n")) return !(strlen(args[2]) > 0);
|
||||
if (w == READ24LE("-z")) return !(strlen(args[2]) == 0);
|
||||
if (w == READ24LE("-e")) return !!stat(args[2], &st);
|
||||
if (w == READ24LE("-f")) return !stat(args[2], &st) && S_ISREG(st.st_mode);
|
||||
if (w == READ24LE("-d")) return !stat(args[2], &st) && S_ISDIR(st.st_mode);
|
||||
if (w == READ24LE("-h")) return !stat(args[2], &st) && S_ISLNK(st.st_mode);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int TryBuiltin(void) {
|
||||
|
@ -370,7 +411,7 @@ static const char *IntToStr(int x) {
|
|||
return ibuf;
|
||||
}
|
||||
|
||||
static const char *GetEnv(const char *key) {
|
||||
static const char *GetVar(const char *key) {
|
||||
if (key[0] == '$' && !key[1]) {
|
||||
return IntToStr(getpid());
|
||||
} else if (key[0] == '!' && !key[1]) {
|
||||
|
@ -378,7 +419,7 @@ static const char *GetEnv(const char *key) {
|
|||
} else if (key[0] == '?' && !key[1]) {
|
||||
return IntToStr(exitstatus);
|
||||
} else {
|
||||
return getenv(key);
|
||||
return GetEnv(envs, key).s;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -387,16 +428,28 @@ static bool IsVarName(int c) {
|
|||
(!vari && (c == '?' || c == '!' || c == '$'));
|
||||
}
|
||||
|
||||
static inline void Append(int c) {
|
||||
_unassert(q + 1 < argbuf + sizeof(argbuf));
|
||||
*q++ = c;
|
||||
static bool CopyVar(void) {
|
||||
size_t j;
|
||||
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) {
|
||||
char *r;
|
||||
const char *s;
|
||||
int c, t, j, k, rc;
|
||||
for (r = q, t = STATE_WHITESPACE;; ++p) {
|
||||
for (t = STATE_WHITESPACE;; ++p) {
|
||||
switch (t) {
|
||||
|
||||
case STATE_WHITESPACE:
|
||||
|
@ -413,8 +466,7 @@ static char *Tokenize(void) {
|
|||
UnsupportedSyntax(*p);
|
||||
}
|
||||
if (!*p || *p == ' ' || *p == '\t') {
|
||||
Append(0);
|
||||
return r;
|
||||
return Finish();
|
||||
} else if (*p == '"') {
|
||||
t = STATE_QUOTED;
|
||||
} else if (*p == '\'') {
|
||||
|
@ -427,8 +479,7 @@ static char *Tokenize(void) {
|
|||
Append(*++p);
|
||||
} else if (*p == '|') {
|
||||
if (q > r) {
|
||||
Append(0);
|
||||
return r;
|
||||
return Finish();
|
||||
} else if (p[1] == '|') {
|
||||
rc = Run();
|
||||
if (!rc) {
|
||||
|
@ -443,16 +494,14 @@ static char *Tokenize(void) {
|
|||
}
|
||||
} else if (*p == ';') {
|
||||
if (q > r) {
|
||||
Append(0);
|
||||
return r;
|
||||
return Finish();
|
||||
} else {
|
||||
Run();
|
||||
t = STATE_WHITESPACE;
|
||||
}
|
||||
} else if (*p == '&') {
|
||||
if (q > r) {
|
||||
Append(0);
|
||||
return r;
|
||||
return Finish();
|
||||
} else if (p[1] == '&') {
|
||||
rc = Run();
|
||||
if (!rc) {
|
||||
|
@ -471,22 +520,9 @@ static char *Tokenize(void) {
|
|||
break;
|
||||
|
||||
case STATE_VAR:
|
||||
if (IsVarName(*p)) {
|
||||
_unassert(vari + 1 < sizeof(var));
|
||||
var[vari++] = *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;
|
||||
}
|
||||
// XXX: we need to find a simple elegant way to break up
|
||||
// unquoted variable expansions into multiple args.
|
||||
if (CopyVar()) t = STATE_CMD, --p;
|
||||
break;
|
||||
|
||||
case STATE_SINGLE:
|
||||
|
@ -501,6 +537,11 @@ static char *Tokenize(void) {
|
|||
UnterminatedString:
|
||||
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:
|
||||
if (!*p) goto UnterminatedString;
|
||||
if (*p == '"') {
|
||||
|
@ -529,24 +570,6 @@ static char *Tokenize(void) {
|
|||
}
|
||||
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:
|
||||
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;
|
||||
size_t i, j;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
q = argbuf;
|
||||
r = q = argbuf;
|
||||
while ((arg = Tokenize())) {
|
||||
if (n + 1 < ARRAYLEN(args)) {
|
||||
if (isdigit(arg[0]) && arg[1] == '>' && arg[2] == '&' &&
|
|
@ -53,6 +53,7 @@ void mcount(void);
|
|||
int daemon(int, int);
|
||||
int _freestack(void *);
|
||||
void _bt(const char *, ...);
|
||||
int _cocmd(int, char **, char **);
|
||||
unsigned long getauxval(unsigned long);
|
||||
void *_mapanon(size_t) attributeallocsize((1)) mallocesque;
|
||||
void *_mapshared(size_t) attributeallocsize((1)) mallocesque;
|
||||
|
|
|
@ -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_ */
|
|
@ -21,7 +21,6 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/cocmd.internal.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.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
|
||||
if (pipefds[0] != !dir) _unassert(!close(pipefds[0]));
|
||||
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:
|
||||
f->pid = pid;
|
||||
_unassert(!close(pipefds[!dir]));
|
||||
|
|
|
@ -86,7 +86,6 @@ int setvbuf(FILE *, char *, int, size_t);
|
|||
int pclose(FILE *);
|
||||
char *ctermid(char *);
|
||||
void perror(const char *) relegated;
|
||||
int makedirs(const char *, unsigned);
|
||||
|
||||
typedef uint64_t fpos_t;
|
||||
char *gets(char *) paramsnonnull();
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#include "libc/log/log.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/cocmd.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
@ -57,7 +55,7 @@ int system(const char *cmdline) {
|
|||
sigaction(SIGINT, &saveint, 0);
|
||||
sigaction(SIGQUIT, &savequit, 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) {
|
||||
while (wait4(pid, &wstatus, 0, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
|
|
|
@ -16,12 +16,41 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.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 \
|
||||
"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/" \
|
|
@ -17,12 +17,14 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/mem/copyfd.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
@ -105,3 +107,12 @@ TEST(system, notequals) {
|
|||
ASSERT_EQ(1, WEXITSTATUS(system("test a != a")));
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/cocmd.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return cocmd(argc, argv);
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
return _cocmd(argc, argv, envp);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue