diff options
-rw-r--r-- | toys/pending/sh.c | 133 |
1 files changed, 103 insertions, 30 deletions
diff --git a/toys/pending/sh.c b/toys/pending/sh.c index b461a6a4..3b733748 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -51,6 +51,7 @@ USE_SH(NEWTOY(cd, ">1LP[-LP]", TOYFLAG_NOFORK)) USE_SH(NEWTOY(exit, 0, TOYFLAG_NOFORK)) USE_SH(NEWTOY(unset, "fvn", TOYFLAG_NOFORK)) USE_SH(NEWTOY(eval, 0, TOYFLAG_NOFORK)) +USE_SH(NEWTOY(exec, "cla:", TOYFLAG_NOFORK)) USE_SH(NEWTOY(sh, "(noediting)(noprofile)(norc)sc:i", TOYFLAG_BIN)) USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN)) @@ -114,6 +115,17 @@ config EVAL usage: eval COMMAND... Execute (combined) arguments as a shell command. + +config EXEC + bool + default n + depends on SH + help + usage: exec [-cl] [-a NAME] COMMAND... + + -a set argv[0] to NAME + -c clear environment + -l prepend - to argv[0] */ #define FOR_sh @@ -124,6 +136,9 @@ GLOBALS( struct { char *c; } sh; + struct { + char *a; + } exec; }; // keep lineno here, we use it to work around a compiler bug @@ -133,7 +148,7 @@ GLOBALS( unsigned options, jobcnt, loc_ro, loc_magic; int hfd; // next high filehandle (>= 10) - // Running jobs. + // Running jobs for job control. struct sh_job { struct sh_job *next, *prev; unsigned jobno; @@ -154,6 +169,7 @@ GLOBALS( } *procs, *proc; } *jobs, *job; + struct sh_process *pp; struct sh_arg *arg; ) @@ -535,6 +551,26 @@ static char *utf8spnc(char *str, char *chrs, int c) return str; } +// glue together argument list with separator, plus pre/post sections +static char *merge_args(char *pre, int argc, char *argv[], char *sep, + int *len, char *post) +{ + int prlen = strlen(pre), polen = strlen(post)+1, jj = 1, kk = 0; + char *s, *ss; + + while (jj<argc) kk += *len + strlen(argv[jj++]); + s = ss = xmalloc(prlen+kk+polen); + memcpy(s, pre, prlen); + s += prlen; + for (jj = 1; jj<argc; jj++) s += sprintf(s, "%s%s", argv[jj], sep); + if (jj != 1) s -= *len; + *len = s-ss; + memcpy(s, post, polen); + + return ss; +} + + #define NO_PATH (1<<0) // path expansion (wildcards) #define NO_SPLIT (1<<1) // word splitting #define NO_BRACE (1<<2) // {brace,expansion} @@ -630,14 +666,16 @@ if (BUGBUG) dprintf(255, "expand %s\n", str); else if (cc == '*' || cc == '@') { // If not doing word split, handle here if ((qq&1) && cc=='*') { -//TODO separator is first char of IFS, not space: ll = utf8towc(&wc1, wc, 99); + char buf[8]; + wchar_t wc; - for (jj = kk = 0; jj<TT.arg->c; jj++) kk += strlen(TT.arg->v[jj]); - s = xmalloc(oo+kk+TT.arg->c+strlen(str+ii)+1); - memcpy(s, new, oo); - for (jj = 1; jj<TT.arg->c; jj++) - oo += sprintf(s+oo, " %s"+!jj, TT.arg->v[jj]); - strcpy(s+oo, str+ii); + new[oo] = 0; + if (0>(oo = utf8towc(&wc, TT.ifs, 4))) oo = 0; + memcpy(buf, TT.ifs, oo); + buf[oo] = 0; + s = merge_args(new, TT.arg->c, TT.arg->v, buf, &oo, str+ii); + if (new != old) free(new); + new = s; // otherwise hand off to IFS logic at end of loop. } else at = 1; @@ -1173,12 +1211,15 @@ if (BUGBUG) { int i; dprintf(255, "envlen=%d arg->c=%d run=", envlen, arg->c); f // to offsetof() the first thing _after_ the union to get the size. memset(&TT, 0, offsetof(struct sh_data, lineno)); + TT.pp = pp; if (!sigsetjmp(rebound, 1)) { toys.rebound = &rebound; toy_singleinit(tl, pp->arg.v); // arg.v must be null terminated tl->toy_main(); xflush(0); } + TT.pp = 0; + toys.rebound = 0; pp->exit = toys.exitval; if (toys.optargs != toys.argv+1) free(toys.optargs); if (toys.old_umask) umask(toys.old_umask); @@ -2183,9 +2224,9 @@ static void subshell_setup(void) void sh_main(void) { FILE *f; - char *new; + char *new, *cc = TT.sh.c; struct sh_function scratch; - int prompt = 0; + int prompt = 0, ii = FLAG(i); struct sh_arg arg; TT.hfd = 10; @@ -2207,32 +2248,41 @@ void sh_main(void) if (BUGBUG) { int fd = open("/dev/tty", O_RDWR); dup2(fd, 255); close(fd); } // Is this an interactive shell? -// if (FLAG(i) || (!FLAG(c)&&(FLAG(S)||!toys.optc) && isatty(0) && isatty(1))) - - // Set up signal handlers and grab control of this tty. + if (ii || (!FLAG(c)&&(FLAG(s)||!toys.optc) && isatty(0))) { + ii = 1; + // TODO Set up signal handlers and grab control of this tty. + } - // Read environment for exports from parent shell + // Read environment for exports from parent shell. Note, calls run_sh() + // which blanks argument sections of TT and this, so parse everything + // we need from shell command line before that. subshell_setup(); - memset(&scratch, 0, sizeof(scratch)); // TODO unify fmemopen() here with sh_run - if (TT.sh.c) f = fmemopen(TT.sh.c, strlen(TT.sh.c), "r"); - else if (*toys.optargs) f = xfopen(*toys.optargs, "r"); - else { - f = stdin; - if (isatty(0)) toys.optflags |= FLAG_i; - } + if (cc) f = fmemopen(cc, strlen(cc), "r"); + else if (*toys.optargs) { + + if (!(f = fopen(*toys.optargs, "r"))) { + char *pp = getvar("PATH"); + struct string_list *sl = find_in_path(pp?pp:_PATH_DEFPATH, *toys.optargs); + + for (;sl; free(llist_pop(&sl))) if ((f = fopen(sl->str, "r"))) break; + llist_traverse(sl, free); + } + } else f = stdin; for (;;) { // Prompt and read line - if (f == stdin) { + TT.lineno++; + if (ii && f == stdin) { char *s = getenv(prompt ? "PS2" : "PS1"); if (!s) s = prompt ? "> " : (getpid() ? "\\$ " : "# "); do_prompt(s); - } else TT.lineno++; + } + // TODO line editing/history, should set $COLUMNS $LINES and sigwinch update if (!(new = xgetline(f ? f : stdin, 0))) break; // TODO if (!isspace(*new)) add_to_history(line); @@ -2361,12 +2411,35 @@ void unset_main(void) void eval_main(void) { - struct sh_arg *aa, arg; + int len = 1; + char *s = merge_args("", toys.optc+1, toys.argv, " ", &len, ""); - aa = TT.arg; - TT.arg = &arg; - arg.v = toys.argv; - arg.c = toys.optc+1; - sh_run("\"$@\""); - TT.arg = aa; + sh_run(s); + free(s); +} + +#define CLEANUP_cd +#define FOR_exec +#include "generated/flags.h" + +void exec_main(void) +{ + char *ee[1] = {0}, *cc, *pp = getvar("PATH"); + struct string_list *sl; + + // discard redirects and return if nothing to exec + free(TT.pp->urd); + TT.pp->urd = 0; + if (!toys.optc) return; + + // exec, handling -acl + cc = *toys.optargs; + if (TT.exec.a || FLAG(l)) + *toys.optargs = xmprintf("%s%s", FLAG(l)?"-":"", TT.exec.a?TT.exec.a:cc); + for (sl = find_in_path(pp?pp:_PATH_DEFPATH, cc); sl; free(llist_pop(&sl))) + execve(sl->str, toys.optargs, FLAG(c) ? ee : environ); + + // report error (usually ENOENT) and return + perror_msg("%s", cc); + toys.exitval = 127; } |