mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Improve system() / popen() builtin shell (#753)
* cocmd: stop splitting redirect expressions * cocmd: allow passing unpaired } for passing Perl test * cocmd: propagate exit status from semicolon
This commit is contained in:
parent
d5b8b644c2
commit
d484e1dbd4
2 changed files with 111 additions and 3 deletions
|
@ -399,7 +399,7 @@ static int Fake(int main(int, char **)) {
|
|||
}
|
||||
|
||||
static int TryBuiltin(void) {
|
||||
if (!n) return 0;
|
||||
if (!n) return exitstatus;
|
||||
if (!strcmp(args[0], "exit")) Exit();
|
||||
if (!strcmp(args[0], "cd")) return Cd();
|
||||
if (!strcmp(args[0], "rm")) return Rm();
|
||||
|
@ -570,9 +570,14 @@ static char *Tokenize(void) {
|
|||
if (q > r) {
|
||||
return Finish();
|
||||
} else {
|
||||
Run();
|
||||
exitstatus = Run();
|
||||
t = STATE_WHITESPACE;
|
||||
}
|
||||
} else if (*p == '>') {
|
||||
Append(*p);
|
||||
if (p[1] == '&') {
|
||||
Append(*++p);
|
||||
}
|
||||
} else if (*p == '&') {
|
||||
if (q > r) {
|
||||
return Finish();
|
||||
|
@ -680,7 +685,8 @@ int _cocmd(int argc, char **argv, char **envp) {
|
|||
unsupported['('] = true;
|
||||
unsupported[')'] = true;
|
||||
unsupported['{'] = true;
|
||||
unsupported['}'] = true;
|
||||
unsupported['}'] = false; // Perl t/op/exec.t depends on unpaired } being
|
||||
// passed from the shell to Perl
|
||||
if (!_weaken(glob)) {
|
||||
unsupported['*'] = true;
|
||||
unsupported['?'] = true;
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
STATIC_YOINK("glob");
|
||||
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
TEST(system, haveShell) {
|
||||
|
@ -56,6 +58,43 @@ TEST(system, testStdoutRedirect_withSpacesInFilename) {
|
|||
EXPECT_STREQ("hello\n", _gc(xslurp("hello there.txt", 0)));
|
||||
}
|
||||
|
||||
TEST(system, testStderrRedirect_toStdout) {
|
||||
int pipefd[2];
|
||||
int stdoutBack = dup(1);
|
||||
ASSERT_NE(-1, stdoutBack);
|
||||
ASSERT_EQ(0, pipe(pipefd));
|
||||
ASSERT_NE(-1, dup2(pipefd[1], 1));
|
||||
int stderrBack = dup(2);
|
||||
ASSERT_NE(-1, stderrBack);
|
||||
char buf[5] = {0};
|
||||
|
||||
ASSERT_NE(-1, dup2(1, 2));
|
||||
bool success = false;
|
||||
if (WEXITSTATUS(system("echo aaa 2>&1")) == 0) {
|
||||
success = read(pipefd[0], buf, sizeof(buf) - 1) == (sizeof(buf) - 1);
|
||||
}
|
||||
ASSERT_NE(-1, dup2(stderrBack, 2));
|
||||
ASSERT_EQ(true, success);
|
||||
ASSERT_STREQ("aaa\n", buf);
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = 0;
|
||||
buf[3] = 0;
|
||||
testlib_extract("/zip/echo.com", "echo.com", 0755);
|
||||
ASSERT_NE(-1, dup2(1, 2));
|
||||
success = false;
|
||||
if (WEXITSTATUS(system("./echo.com aaa 2>&1")) == 0) {
|
||||
success = read(pipefd[0], buf, sizeof(buf) - 1) == (sizeof(buf) - 1);
|
||||
}
|
||||
ASSERT_NE(-1, dup2(stderrBack, 2));
|
||||
ASSERT_EQ(true, success);
|
||||
ASSERT_STREQ("aaa\n", buf);
|
||||
|
||||
ASSERT_NE(-1, dup2(stdoutBack, 1));
|
||||
ASSERT_EQ(0, close(pipefd[1]));
|
||||
ASSERT_EQ(0, close(pipefd[0]));
|
||||
}
|
||||
|
||||
BENCH(system, bench) {
|
||||
testlib_extract("/zip/echo.com", "echo.com", 0755);
|
||||
EZBENCH2("system cmd", donothing, system("./echo.com hi >/dev/null"));
|
||||
|
@ -116,3 +155,66 @@ TEST(system, kill) {
|
|||
int ws = system("kill -TERM $$; usleep");
|
||||
if (!IsWindows()) ASSERT_EQ(SIGTERM, WTERMSIG(ws));
|
||||
}
|
||||
|
||||
TEST(system, exitStatusPreservedAfterSemiColon) {
|
||||
ASSERT_EQ(1, WEXITSTATUS(system("false;")));
|
||||
ASSERT_EQ(1, WEXITSTATUS(system("false; ")));
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
ASSERT_EQ(1, WEXITSTATUS(system("/bin/false;")));
|
||||
ASSERT_EQ(1, WEXITSTATUS(system("/bin/false;")));
|
||||
}
|
||||
|
||||
int pipefd[2];
|
||||
int stdoutBack = dup(1);
|
||||
ASSERT_NE(-1, stdoutBack);
|
||||
ASSERT_EQ(0, pipe(pipefd));
|
||||
ASSERT_NE(-1, dup2(pipefd[1], 1));
|
||||
|
||||
ASSERT_EQ(0, WEXITSTATUS(system("false; echo $?")));
|
||||
char buf[3] = {0};
|
||||
ASSERT_EQ(2, read(pipefd[0], buf, 2));
|
||||
ASSERT_STREQ("1\n", buf);
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
ASSERT_EQ(0, WEXITSTATUS(system("/bin/false; echo $?")));
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
ASSERT_EQ(2, read(pipefd[0], buf, 2));
|
||||
ASSERT_STREQ("1\n", buf);
|
||||
}
|
||||
|
||||
ASSERT_NE(-1, dup2(stdoutBack, 1));
|
||||
ASSERT_EQ(0, close(pipefd[1]));
|
||||
ASSERT_EQ(0, close(pipefd[0]));
|
||||
}
|
||||
|
||||
TEST(system, allowsLoneCloseCurlyBrace) {
|
||||
int pipefd[2];
|
||||
int stdoutBack = dup(1);
|
||||
ASSERT_NE(-1, stdoutBack);
|
||||
ASSERT_EQ(0, pipe(pipefd));
|
||||
ASSERT_NE(-1, dup2(pipefd[1], 1));
|
||||
char buf[6] = {0};
|
||||
|
||||
ASSERT_EQ(0, WEXITSTATUS(system("echo \"aaa\"}")));
|
||||
ASSERT_EQ(sizeof(buf) - 1, read(pipefd[0], buf, sizeof(buf) - 1));
|
||||
ASSERT_STREQ("aaa}\n", buf);
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = 0;
|
||||
buf[3] = 0;
|
||||
buf[4] = 0;
|
||||
testlib_extract("/zip/echo.com", "echo.com", 0755);
|
||||
ASSERT_EQ(0, WEXITSTATUS(system("./echo.com \"aaa\"}")));
|
||||
ASSERT_EQ(sizeof(buf) - 1, read(pipefd[0], buf, sizeof(buf) - 1));
|
||||
ASSERT_STREQ("aaa}\n", buf);
|
||||
|
||||
ASSERT_NE(-1, dup2(stdoutBack, 1));
|
||||
ASSERT_EQ(0, close(pipefd[1]));
|
||||
ASSERT_EQ(0, close(pipefd[0]));
|
||||
}
|
||||
|
||||
TEST(system, glob) {
|
||||
testlib_extract("/zip/echo.com", "echo.com", 0755);
|
||||
ASSERT_EQ(0, WEXITSTATUS(system("./ec*.com aaa")));
|
||||
ASSERT_EQ(0, WEXITSTATUS(system("./ec?o.com aaa")));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue