aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/sh.c
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2019-12-31 16:43:54 -0600
committerRob Landley <rob@landley.net>2019-12-31 16:43:54 -0600
commitb4ea29ef4f33345968df89dc8cbba80a75ffda59 (patch)
tree3bb80928bf10cc6ea1285c3071537b6d788de3bc /toys/pending/sh.c
parent2e7593b478df64e06ca0f1d5694c541d9e98b162 (diff)
downloadtoybox-b4ea29ef4f33345968df89dc8cbba80a75ffda59.tar.gz
toysh: start of <(echo hello) support.
Diffstat (limited to 'toys/pending/sh.c')
-rw-r--r--toys/pending/sh.c201
1 files changed, 132 insertions, 69 deletions
diff --git a/toys/pending/sh.c b/toys/pending/sh.c
index 2408a771..d575791f 100644
--- a/toys/pending/sh.c
+++ b/toys/pending/sh.c
@@ -230,10 +230,32 @@ static void do_prompt(char *prompt)
static void expand_arg(struct sh_arg *arg, char *new, unsigned flags,
struct string_list **delete)
{
+// char *s = new, quote = 0;
+
if (!(arg->c&32)) arg->v = xrealloc(arg->v, sizeof(void *)*(arg->c+33));
+/*
// TODO ls -l /proc/$$/fd
+// ${ $(( $( $[ $' `
+
+ while (*s) {
+ if (!quote && !(flags&NO_BRACE) && *s == '{') {
+TODO this recurses
+ } else if (quote != '*s == '$') {
+ // *@#?-$!_0 "Special Paremeters" ($0 not affected by shift)
+ // 0-9 positional parameters
+ if (s[1] == '$'
+
+// EUID GROUPS HOSTNAME HOSTTYPE=$(uname -m) MACHTYPE=$HOSTTYPE-unknown-linux
+// OLDPWD OSTYPE=linux/android PIPESTATUS PPID PWD RANDOM REPLY SECONDS UID
+// COLUMNS LINES HOME SHELL
+// IFS PATH
+// PS0 PS1='$ ' PS2='> ' PS3
+ }
+ }
+*/
+ // literal passthrough
arg->v[arg->c++] = new;
arg->v[arg->c] = 0;
@@ -257,7 +279,7 @@ static void expand_arg(struct sh_arg *arg, char *new, unsigned flags,
// Expand exactly one arg, returning NULL if it split.
// If return != new you need to free it.
-static char *expand_one_arg(char *new, unsigned flags)
+static char *expand_one_arg(char *new, unsigned flags, struct string_list **del)
{
struct sh_arg arg;
char *s = 0;
@@ -268,6 +290,7 @@ static char *expand_one_arg(char *new, unsigned flags)
if (arg.c == 1) s = *arg.v;
else for (i = 0; i < arg.c; i++) free(arg.v[i]);
free(arg.v);
+ if (del && s != new) dlist_add((void *)del, s);
return s;
}
@@ -384,13 +407,14 @@ int save_redirect(int **rd, int from, int to)
// save displaced to, copying to high (>=10) file descriptor to undo later
// except if we're saving to environment variable instead (don't undo that)
- if ((hfd = next_hfd())==-1 || hfd != dup2(to, hfd)) return 1;
- fcntl(hfd, F_SETFD, FD_CLOEXEC);
+ if ((hfd = next_hfd())==-1) return 1;
+ if (hfd != dup2(to, hfd)) hfd = -1;
+ else fcntl(hfd, F_SETFD, FD_CLOEXEC);
if (BUGBUG) dprintf(255, "redir from=%d to=%d hfd=%d\n", from, to, hfd);
// dup "to"
if (from != -1 && to != dup2(from, to)) {
- close(hfd);
+ if (hfd != -1) close(hfd);
return 1;
}
@@ -404,6 +428,34 @@ if (BUGBUG) dprintf(255, "redir from=%d to=%d hfd=%d\n", from, to, hfd);
return 0;
}
+// Pipeline segments
+struct sh_pipeline {
+ struct sh_pipeline *next, *prev;
+ int count, here, type;
+ struct sh_arg arg[1];
+};
+
+// scratch space (state held between calls). Don't want to make it global yet
+// because this could be reentrant.
+struct sh_function {
+ char *name;
+ struct sh_pipeline *pipeline;
+ struct double_list *expect;
+// TODO: lifetime rules for arg? remember "shift" command.
+ struct sh_arg *arg; // arguments to function call
+ char *end;
+};
+
+// TODO: try to avoid prototype.
+static int parse_line(char *line, struct sh_function *sp);
+void free_function(struct sh_function *sp);
+void argxtend(struct sh_arg *arg);
+
+void run_subshell(struct sh_pipeline *sp)
+{
+ dprintf(2, "TODO: run_subshell\n");
+}
+
// 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)
@@ -421,11 +473,48 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
// Expand arguments and perform redirections
for (j = envlen; j<arg->c; j++) {
- int saveclose = 0;
+ int saveclose = 0, bad = 0;
+
+ s = arg->v[j];
+
+ // Handle <() >() redirectionss
+ if ((*s == '<' || *s == '>') && s[1] == '(') {
+ struct sh_function sf;
+ int pipes[2], *uu = 0, dd;
+
+ // Grab subshell data
+ memset(&sf, 0, sizeof(struct sh_function));
+ errno = 0;
+ if (parse_line(s+1, &sf) || pipe(pipes)) {
+ perror_msg_raw(s);
+ free_function(&sf);
+ pp->exit = 1;
+
+ return pp;
+ }
+// pipe[1] > pipe[0]->0
+// pipe[0] < pipe[1]->1
+
+ // Perform input or output redirect and launch process
+ dd = *s == '<';
+ save_redirect(&uu, pipes[dd], dd);
+ fcntl(pipes[!dd], F_SETFD, FD_CLOEXEC);
+ run_subshell(sf.pipeline); // ignore errors, don't track
+ unredirect(uu);
+ save_redirect(&urd, -1, pipes[!dd]);
+
+ // Argument is /dev/fd/%d with pipe filehandle
+ dlist_add((void *)&pp->delete, ss = xmprintf("/dev/fd/%d", pipes[dd]));
+ argxtend(&pp->arg);
+ pp->arg.v[pp->arg.c++] = ss;
+ pp->arg.v[pp->arg.c] = 0;
+
+ continue;
+ }
// Is this a redirect? s = prefix, ss = operator
- sss = ss = (s = arg->v[j]) + redir_prefix(arg->v[j]);
- sss += anystart(ss, (void *)redirectors);
+ ss = s + redir_prefix(arg->v[j]);
+ sss = ss + anystart(ss, (void *)redirectors);
if (ss == sss) {
// Nope: save/expand argument and loop
expand_arg(&pp->arg, s, 0, &pp->delete);
@@ -442,13 +531,11 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
if (isdigit(*s) && ss-s>5) break;
// expand arguments for everything but << and <<-
- if (strncmp(ss, "<<", 2) && ss[2] != '<') {
- sss = expand_one_arg(sss, NO_PATH);
- if (!sss) {
- s = sss;
- break; // arg splitting here is an error
- }
- if (sss != arg->v[j]) dlist_add((void *)&pp->delete, sss);
+ if (strncmp(ss, "<<", 2) && ss[2] != '<' &&
+ !(sss = expand_one_arg(sss, NO_PATH, &pp->delete)))
+ {
+ s = sss;
+ break; // arg splitting here is an error
}
// Parse the [fd] part of [fd]<name
@@ -474,7 +561,7 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
// HERE documents?
if (!strcmp(ss, "<<<") || !strcmp(ss, "<<-") || !strcmp(ss, "<<")) {
char *tmp = getvar("TMPDIR", 6);
- int i, len, bad = 0, zap = (ss[2] == '-'), x = !ss[strcspn(ss, "\"'")];
+ int i, len, zap = (ss[2] == '-'), x = !ss[strcspn(ss, "\"'")];
// store contents in open-but-deleted /tmp file.
tmp = xmprintf("%s/sh-XXXXXX", tmp ? tmp : "/tmp");
@@ -483,7 +570,7 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
// write contents to file (if <<< else <<) then lseek back to start
else if (ss[2] == '<') {
- if (x) sss = expand_one_arg(sss, NO_PATH|NO_SPLIT);
+ if (x) sss = expand_one_arg(sss, NO_PATH|NO_SPLIT, 0);
len = strlen(sss);
if (len != writeall(from, sss, len)) bad++;
if (x) free(sss);
@@ -495,7 +582,7 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
sss = 0;
// expand_parameter, commands, and arithmetic
if (x) ss = sss = expand_one_arg(ss,
- NO_PATH|NO_SPLIT|NO_BRACE|NO_TILDE|NO_QUOTE);
+ NO_PATH|NO_SPLIT|NO_BRACE|NO_TILDE|NO_QUOTE, 0);
while (zap && *ss == '\t') ss++;
x = writeall(from, ss, len = strlen(ss));
@@ -543,22 +630,25 @@ static struct sh_process *expand_redir(struct sh_arg *arg, int envlen, int *urd)
}
}
-// TODO: /dev/fd/# /dev/{stdin,stdout,stderr} /dev/{tcp,udp}/host/port
+ // we expect /dev/fd/# and /dev/{stdin,stdout,stderr} to be in /dev
+
+// TODO: /dev/{tcp,udp}/host/port
// Open the file
if (-1 == (from = xcreate(sss, from|WARN_ONLY, 0666))) break;
}
// perform redirect, saving displaced "to".
- save_redirect(&pp->urd, from, to);
+ if (save_redirect(&pp->urd, from, to)) bad++;
// Do we save displaced "to" in env variable instead of undo list?
if (cv) {
--*pp->urd;
setvar(cv, TAKE_MEM);
cv = 0;
}
- if (saveclose) save_redirect(&pp->urd, -1, from);
+ if (saveclose && save_redirect(&pp->urd, -1, from)) bad++;
close(from);
+ if (bad) break;
}
// didn't parse everything?
@@ -593,13 +683,10 @@ if (BUGBUG) { int i; dprintf(255, "envlen=%d arg->c=%d run=", envlen, arg->c); f
// perform assignments locally if there's no command
if (envlen == arg->c) {
for (j = 0; j<envlen; j++) {
- s = expand_one_arg(arg->v[j], NO_PATH|NO_SPLIT);
-if (BUGBUG) dprintf(255, "setvar %s\n", s);
+ s = expand_one_arg(arg->v[j], NO_PATH|NO_SPLIT, 0);
setvar(s, TAKE_MEM*(s!=arg->v[j]));
}
- unredirect(pp->urd);
- return pp;
// Do nothing if nothing to do
} else if (pp->exit || !pp->arg.v);
else if (!strcmp(*pp->arg.v, "((")) {
@@ -637,8 +724,7 @@ if (BUGBUG) dprintf(255, "setvar %s\n", s);
if (environ) while (environ[kk]) kk++;
if (kk) env = xmemdup(environ, sizeof(char *)*(kk+1));
for (j = 0; j<envlen; j++) {
- sss = expand_one_arg(arg->v[j], NO_PATH|NO_SPLIT);
- if (sss != arg->v[j]) dlist_add((void *)&pp->delete, sss);
+ sss = expand_one_arg(arg->v[j], NO_PATH|NO_SPLIT, &pp->delete);
for (ll = 0; ll<kk; ll++) {
for (s = sss, ss = env[ll]; *s == *ss && *s != '='; s++, ss++);
if (*s != '=') continue;
@@ -681,9 +767,21 @@ static void free_process(void *ppp)
// caller eats leading spaces
static char *parse_word(char *start)
{
- int i, j, quote = 0, q, qc = 0;
+ int i, quote = 0, q, qc = 0;
char *end = start, *s;
+ // Things we should only return at the _start_ of a word
+
+ if (strstart(&end, "<(") || strstart(&end, ">(")) toybuf[quote++]=')';
+
+ // Redirections. 123<<file- parses as 2 args: "123<<" "file-".
+ s = end + redir_prefix(end);
+ if ((i = anystart(s, (void *)redirectors))) s += i;
+ // Flow control characters that end pipeline segments
+ else s = end + anystart(end, (char *[]){";;&", ";;", ";&", ";", "||",
+ "|&", "|", "&&", "&", "(", ")", 0});
+ if (s != end) return (end == start) ? s : end;
+
// (( is a special quote at the start of a word
if (strstart(&end, "((")) toybuf[quote++] = 255;
@@ -697,9 +795,8 @@ static char *parse_word(char *start)
return (void *)1;
}
- q = quote ? toybuf[quote-1] : 0;
// Handle quote contexts
- if (q) {
+ if ((q = quote ? toybuf[quote-1] : 0)) {
// when waiting for parentheses, they nest
if ((q == ')' || q == '\xff') && (*end == '(' || *end == ')')) {
@@ -709,7 +806,7 @@ static char *parse_word(char *start)
// (( can end with )) or retroactively become two (( if we hit one )
if (strstart(&end, "))")) quote--;
else return start+1;
- }
+ } else if (*end == ')') quote--;
end++;
// end quote?
@@ -725,27 +822,13 @@ static char *parse_word(char *start)
// Things that only matter when unquoted
if (isspace(*end)) break;
-
- // Things we should only return at the _start_ of a word
-
- // Redirections. 123<<file- parses as 2 args: "123<<" "file-".
- s = end + redir_prefix(end);
- j = anystart(s, (void *)redirectors);
- if (j) s += j;
-
- // Flow control characters that end pipeline segments
- else s = end + anystart(end, (char *[]){";;&", ";;", ";&", ";", "||",
- "|&", "|", "&&", "&", "(", ")", 0});
- if (s != end) return (end == start) ? s : end;
- i++;
+ if (*end == ')') return end+(start==end);
}
// Things the same unquoted or in most non-single-quote contexts
// start new quote context?
if (strchr("\"'`", *end)) toybuf[quote++] = *end++;
- else if (q != '"' && (strstart(&end, "<(") || strstart(&end,">(")))
- toybuf[quote++]=')';
// backslash escapes
else if (*end == '\\') {
@@ -770,24 +853,6 @@ void argxtend(struct sh_arg *arg)
if (!(arg->c&31)) arg->v = xrealloc(arg->v, (33+arg->c)*sizeof(void *));
}
-// Pipeline segments
-struct sh_pipeline {
- struct sh_pipeline *next, *prev;
- int count, here, type;
- struct sh_arg arg[1];
-};
-
-// scratch space (state held between calls). Don't want to make it global yet
-// because this could be reentrant.
-struct sh_function {
- char *name;
- struct sh_pipeline *pipeline;
- struct double_list *expect;
-// TODO: lifetime rules for arg? remember "shift" command.
- struct sh_arg *arg; // arguments to function call
- char *end;
-};
-
// Free one pipeline segment.
void free_pipeline(void *pipeline)
{
@@ -913,11 +978,8 @@ static int parse_line(char *line, struct sh_function *sp)
s = 0;
// skip leading whitespace/comment here to know where next word starts
- for (;;) {
- if (isspace(*start)) ++start;
- else if (*start=='#') while (*start && *start != '\n') ++start;
- else break;
- }
+ while (isspace(*start)) ++start;
+ if (*start=='#') while (*start && *start != '\n') ++start;
// Parse next word and detect overflow (too many nested quotes).
if ((end = parse_word(start)) == (void *)1)
@@ -1168,7 +1230,8 @@ static void dump_state(struct sh_function *sp)
for (pl = sp->pipeline; pl ; pl = (pl->next == sp->pipeline) ? 0 : pl->next) {
for (i = 0; i<pl->arg->c; i++)
dprintf(255, "arg[%d][%ld]=%s\n", q, i, pl->arg->v[i]);
- dprintf(255, "type=%d term[%d]=%s\n", pl->type, q++, pl->arg->v[pl->arg->c]);
+ if (pl->arg->c<0) dprintf(255, "argc=%d\n", pl->arg->c);
+ else dprintf(255, "type=%d term[%d]=%s\n", pl->type, q++, pl->arg->v[pl->arg->c]);
}
}