aboutsummaryrefslogtreecommitdiff
path: root/toys
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2020-02-04 13:44:20 -0600
committerRob Landley <rob@landley.net>2020-02-04 13:44:20 -0600
commit2f96b8601212ab9d28fc5a8b640ecc9ef7de251b (patch)
tree45fce0ff538728cae7a3343ddef09615f8ef1798 /toys
parent7450b93f9784d4df625f9729a20a61a3d8923854 (diff)
downloadtoybox-2f96b8601212ab9d28fc5a8b640ecc9ef7de251b.tar.gz
More (subshell) work (not finished), fix {block;} | pipes.
Diffstat (limited to 'toys')
-rw-r--r--toys/pending/sh.c86
1 files changed, 66 insertions, 20 deletions
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 7e6f827d..5beebca2 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -626,7 +626,7 @@ static void subshell_callback(void)
// TODO avoid prototype
static int sh_run(char *new);
-// Pass environment and command string to child shell
+// Pass environment and command string to child shell, return PID of child
static int run_subshell(char *str, int len)
{
pid_t pid;
@@ -636,7 +636,7 @@ static int run_subshell(char *str, int len)
char *s;
if ((pid = fork())<0) perror_msg("fork");
- else if (pid>0) {
+ else if (!pid) {
s = xstrndup(str, len);
sh_run(s);
free(s);
@@ -667,6 +667,33 @@ static int run_subshell(char *str, int len)
return pid;
}
+// turn a parsed pipeline back into a string.
+static char *pl2str(struct sh_pipeline *pl)
+{
+ struct sh_pipeline *end = 0;
+ int level = 0, len = 0, i, j;
+ char *s, *ss, *sss;
+
+ // measure, then allocate
+ for (j = 0; ; j++) for (end = pl; end; end = end->next) {
+ if (end->type == 1) level++;
+ else if (end->type == 3 && --level<0) break;
+
+ for (i = 0; i<pl->arg->c; i++)
+ if (j) ss += sprintf(ss, "%s ", pl->arg->v[i]);
+ else len += strlen(pl->arg->v[i])+1;
+
+ sss = pl->arg->v[pl->arg->c];
+ if (!sss) sss = ";";
+ if (j) ss = stpcpy(ss, sss);
+ else len += strlen(sss);
+
+// TODO add HERE documents back in
+ if (j) return s;
+ s = ss = xmalloc(len+1);
+ }
+}
+
// Expand arguments and perform redirections. Return new process object with
// expanded args. This can be called from command or block context.
static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
@@ -1078,7 +1105,7 @@ struct sh_pipeline *block_end(struct sh_pipeline *pl)
pl = pl->next;
}
- return 0;
+ return pl;
}
void free_function(struct sh_function *sp)
@@ -1457,6 +1484,7 @@ void dump_filehandles(char *when)
*/
+// wait for every process in a pipeline to end
static int wait_pipeline(struct sh_process *pp)
{
int rc = 0;
@@ -1474,6 +1502,7 @@ static int wait_pipeline(struct sh_process *pp)
return rc;
}
+// pipe data into and out of this segment, I.E. handle leading and trailing |
static int pipe_segments(char *ctl, int *pipes, int **urd)
{
unredirect(*urd);
@@ -1504,6 +1533,7 @@ static int pipe_segments(char *ctl, int *pipes, int **urd)
return 0;
}
+// Handle && and || traversal in pipeline segments
static struct sh_pipeline *skip_andor(int rc, struct sh_pipeline *pl)
{
char *ctl = pl->arg->v[pl->arg->c];
@@ -1521,9 +1551,9 @@ static struct sh_pipeline *skip_andor(int rc, struct sh_pipeline *pl)
// run a parsed shell function. Handle flow control blocks and characters,
// setup pipes and block redirection, break/continue, call builtins,
// vfork/exec external commands.
-static void run_function(struct sh_function *sp)
+static void run_function(struct sh_pipeline *pl)
{
- struct sh_pipeline *pl = sp->pipeline, *end;
+ struct sh_pipeline *end;
struct blockstack {
struct blockstack *next;
struct sh_pipeline *start, *end;
@@ -1541,12 +1571,11 @@ static void run_function(struct sh_function *sp)
// iterate through pipeline segments
while (pl) {
- char *s = *pl->arg->v, *ss = pl->arg->v[1];
-if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[pl->arg->c]);
+ struct sh_arg *arg = pl->arg;
+ char *s = *arg->v, *ss = arg->v[1], *ctl = arg->v[arg->c];
+if (BUGBUG) dprintf(255, "runtype=%d %s %s\n", pl->type, s, ctl);
// Is this an executable segment?
if (!pl->type) {
- struct sh_arg *arg = pl->arg;
- char *ctl = arg->v[arg->c];
// Skip disabled block
if (blk && !blk->run) {
@@ -1571,7 +1600,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
// How many layers to peel off?
i = ss ? atol(ss) : 0;
if (i<1) i = 1;
- if (!blk || pl->arg->c>2 || ss[strspn(ss, "0123456789")]) {
+ if (!blk || arg->c>2 || ss[strspn(ss, "0123456789")]) {
syntax_err("bad %s", s);
break;
}
@@ -1605,6 +1634,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
// TODO: "echo | read i" is backgroundable with ctrl-Z despite read = builtin.
// probably have to inline run_command here to do that? Implicit ()
// also "X=42 | true; echo $X" doesn't get X.
+// I.E. run_subshell() here sometimes? (But when?)
dlist_add_nomalloc((void *)&pplist, (void *)run_command(arg));
}
@@ -1632,7 +1662,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
}
// If previous piped into this block, save context until block end
- if (pipe_segments(0, pipes, &urd)) break;
+ if (pipe_segments(end->arg->v[end->arg->c], pipes, &urd)) break;
// It's a new block we're running, save context and add it to the stack.
new = xzalloc(sizeof(*blk));
@@ -1649,7 +1679,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
*pipes = -1;
// Perform redirects listed at end of block
- pp = expand_redir(blk->end->arg, 0, blk->urd);
+ pp = expand_redir(end->arg, 1, blk->urd);
blk->urd = pp->urd;
if (pp->arg.c) perror_msg("unexpected %s", *pp->arg.v);
llist_traverse(pp->delete, free);
@@ -1659,9 +1689,7 @@ if (BUGBUG) dprintf(255, "type=%d %s %s\n", pl->type, pl->arg->v[0], pl->arg->v[
// What flow control statement is this?
-// TODO ( subshell
-
- // if/then/elif/else/fi, while until/do/done - no special handling needed
+ // {/} if/then/elif/else/fi, while until/do/done - no special handling
// for select/do/done
if (!strcmp(s, "for") || !strcmp(s, "select")) {
@@ -1677,10 +1705,25 @@ dprintf(2, "TODO skipped init for((;;)), need math parser\n");
expand_arg(&blk->farg, pl->next->arg->v[i], 0, &blk->fdelete);
} else expand_arg(&blk->farg, "\"$@\"", 0, &blk->fdelete);
}
- pl = pl->next;
- }
-// TODO case/esac {/} [[/]] (/) ((/)) function/}
+// TODO case/esac [[/]] (/) ((/)) function/}
+
+/*
+TODO: a | b | c needs subshell for builtins?
+ - anything that can produce output
+ - echo declare dirs
+ (a; b; c) like { } but subshell
+ when to auto-exec? ps vs sh -c 'ps' vs sh -c '(ps)'
+*/
+
+ // subshell
+ } else if (!strcmp(s, "(")) {
+ if (!CFG_TOYBOX_FORK) {
+ ss = pl2str(pl->next);
+ run_subshell(ss, strlen(ss));
+ }
+ pl = blk->end;
+ }
// gearshift from block start to block body (end of flow control test)
} else if (pl->type == 2) {
@@ -1705,6 +1748,9 @@ dprintf(2, "TODO skipped running for((;;)), need math parser\n");
// end of block, may have trailing redirections and/or pipe
} else if (pl->type == 3) {
+ // if we end a block we're not in, we started in a block.
+ if (!blk) break;
+
// repeating block?
if (blk->run && !strcmp(s, "done")) {
pl = blk->start;
@@ -1750,7 +1796,7 @@ static int sh_run(char *new)
// TODO switch the fmemopen for -c to use this? Error checking? $(blah)
memset(&scratch, 0, sizeof(struct sh_function));
- if (!parse_line(new, &scratch)) run_function(&scratch);
+ if (!parse_line(new, &scratch)) run_function(scratch.pipeline);
free_function(&scratch);
rc = toys.exitval;
toys.exitval = 0;
@@ -1972,7 +2018,7 @@ if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); }
if (BUGBUG) dump_state(&scratch);
if (prompt != 1) {
// TODO: ./blah.sh one two three: put one two three in scratch.arg
- if (!prompt) run_function(&scratch);
+ if (!prompt) run_function(scratch.pipeline);
free_function(&scratch);
prompt = 0;
}