mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-27 23:08:31 +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 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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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] == '&' &&
|
|
@ -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;
|
||||||
|
|
|
@ -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/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]));
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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/" \
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue