• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* sh.c - toybox shell
2  *
3  * Copyright 2006 Rob Landley <rob@landley.net>
4  *
5  * The POSIX-2008/SUSv4 spec for this is at:
6  * http://opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
7  * and http://opengroup.org/onlinepubs/9699919799/utilities/sh.html
8  *
9  * The first link describes the following shell builtins:
10  *
11  *   break colon continue dot eval exec exit export readonly return set shift
12  *   times trap unset
13  *
14  * The second link (the utilities directory) also contains specs for the
15  * following shell builtins:
16  *
17  *   alias bg cd command fc fg getopts hash jobs kill read type ulimit
18  *   umask unalias wait
19  *
20  * Things like the bash man page are good to read too.
21  *
22  * TODO: "make sh" doesn't work (nofork builtins need to be included)
23  * TODO: test that $PS1 color changes work without stupid \[ \] hack
24  * TODO: make fake pty wrapper for test infrastructure
25  * TODO: // Handle embedded NUL bytes in the command line.
26  * TODO: var=val command
27  * existing but considered builtins: false kill pwd true time
28  * buitins: alias bg command fc fg getopts jobs newgrp read umask unalias wait
29  * "special" builtins: break continue : . eval exec export readonly return set
30  *   shift times trap unset
31  * | & ; < > ( ) $ ` \ " ' <space> <tab> <newline>
32  * * ? [ # ~ = %
33  * ! { } case do done elif else esac fi for if in then until while
34  * [[ ]] function select
35  * $@ $* $# $? $- $$ $! $0
36  * ENV HOME IFS LANG LC_ALL LINENO PATH PPID PS1 PS2 PS4 PWD
37  * label:
38  * TODO: test exit from "trap EXIT" doesn't recurse
39  * TODO: ! history expansion
40  *
41  * bash man page:
42  * control operators || & && ; ;; ;& ;;& ( ) | |& <newline>
43  * reserved words
44  *   ! case  coproc  do done elif else esac fi for  function  if  in  select
45  *   then until while { } time [[ ]]
46 
47 
48 
49 USE_SH(NEWTOY(cd, NULL, TOYFLAG_NOFORK))
50 USE_SH(NEWTOY(exit, NULL, TOYFLAG_NOFORK))
51 
52 USE_SH(NEWTOY(sh, "c:i", TOYFLAG_BIN))
53 USE_SH(OLDTOY(toysh, sh, TOYFLAG_BIN))
54 USE_SH(OLDTOY(bash, sh, TOYFLAG_BIN))
55 // Login lies in argv[0], so add some aliases to catch that
56 USE_SH(OLDTOY(-sh, sh, 0))
57 USE_SH(OLDTOY(-toysh, sh, 0))
58 USE_SH(OLDTOY(-bash, sh, 0))
59 
60 config SH
61   bool "sh (toysh)"
62   default n
63   help
64     usage: sh [-c command] [script]
65 
66     Command shell.  Runs a shell script, or reads input interactively
67     and responds to it.
68 
69     -c	command line to execute
70     -i	interactive mode (default when STDIN is a tty)
71 
72 # These are here for the help text, they're not selectable and control nothing
73 config CD
74   bool
75   default n
76   depends on SH
77   help
78     usage: cd [-PL] [path]
79 
80     Change current directory.  With no arguments, go $HOME.
81 
82     -P	Physical path: resolve symlinks in path
83     -L	Local path: .. trims directories off $PWD (default)
84 
85 config EXIT
86   bool
87   default n
88   depends on SH
89   help
90     usage: exit [status]
91 
92     Exit shell.  If no return value supplied on command line, use value
93     of most recent command, or 0 if none.
94 */
95 
96 #define FOR_sh
97 #include "toys.h"
98 
99 GLOBALS(
100   char *command;
101 
102   long lineno;
103 
104   struct double_list functions;
105   unsigned options;
106 
107   // Running jobs.
108   struct sh_job {
109     struct sh_job *next, *prev;
110     unsigned jobno;
111 
112     // Every pipeline has at least one set of arguments or it's Not A Thing
113     struct sh_arg {
114       char **v;
115       int c;
116     } pipeline;
117 
118     // null terminated array of running processes in pipeline
119     struct sh_process {
120       struct string_list *delete; // expanded strings
121       int pid, exit;   // status? Stopped? Exited?
122       struct sh_arg arg;
123     } *procs, *proc;
124   } *jobs, *job;
125   unsigned jobcnt;
126 )
127 
128 #define SH_NOCLOBBER 1   // set -C
129 
cd_main(void)130 void cd_main(void)
131 {
132   char *dest = *toys.optargs ? *toys.optargs : getenv("HOME");
133 
134 // TODO: -LPE@
135 // TODO: cd .. goes up $PWD path we used to get here, not ./..
136   xchdir(dest ? dest : "/");
137 }
138 
exit_main(void)139 void exit_main(void)
140 {
141   exit(*toys.optargs ? atoi(*toys.optargs) : 0);
142 }
143 
144 // like error_msg() but exit from shell scripts
syntax_err(char * msg,...)145 void syntax_err(char *msg, ...)
146 {
147   va_list va;
148 
149   va_start(va, msg);
150   verror_msg(msg, 0, va);
151   va_end(va);
152 
153   if (*toys.optargs) xexit();
154 }
155 
156 // Print prompt, parsing escapes
do_prompt(char * prompt)157 static void do_prompt(char *prompt)
158 {
159   char *s, c, cc;
160 
161   if (!prompt) prompt = "\\$ ";
162   while (*prompt) {
163     c = *(prompt++);
164 
165     if (c=='!') {
166       if (*prompt=='!') prompt++;
167       else {
168         printf("%ld", TT.lineno);
169         continue;
170       }
171     } else if (c=='\\') {
172       int i = 0;
173 
174       cc = *(prompt++);
175       if (!cc) goto down;
176 
177       // \nnn \dD{}hHjlstT@AuvVwW!#$
178       // Ignore bash's "nonprintable" hack; query our cursor position instead.
179       if (cc=='[' || cc==']') continue;
180       else if (cc=='$') putchar(getuid() ? '$' : '#');
181       else if (cc=='h' || cc=='H') {
182         *toybuf = 0;
183         gethostname(toybuf, sizeof(toybuf)-1);
184         if (cc=='h' && (s = strchr(toybuf, '.'))) *s = 0;
185         fputs(toybuf, stdout);
186       } else if (cc=='s') fputs(getbasename(*toys.argv), stdout);
187       else {
188         if (!(c = unescape(cc))) {
189           c = '\\';
190           prompt--;
191         }
192         i++;
193       }
194       if (!i) continue;
195     }
196 down:
197     putchar(c);
198   }
199   fflush(stdout);
200 }
201 
202 // quote removal, brace, tilde, parameter/variable, $(command),
203 // $((arithmetic)), split, path
204 #define NO_PATH  (1<<0)
205 #define NO_SPLIT (1<<1)
206 // TODO: ${name:?error} causes an error/abort here (syntax_err longjmp?)
207 // TODO: $1 $@ $* need args marshalled down here: function+structure?
208 // arg = append to this
209 // new = string to expand
210 // flags = type of expansions (not) to do
211 // delete = append new allocations to this so they can be freed later
212 // TODO: at_args: $1 $2 $3 $* $@
expand_arg(struct sh_arg * arg,char * new,unsigned flags,struct string_list ** delete)213 static void expand_arg(struct sh_arg *arg, char *new, unsigned flags,
214   struct string_list **delete)
215 {
216   if (!(arg->c&32)) arg->v = xrealloc(arg->v, sizeof(void *)*(arg->c+33));
217 
218   arg->v[arg->c++] = new;
219   arg->v[arg->c] = 0;
220 
221 /*
222   char *s = word, *new = 0;
223 
224   // replacement
225   while (*s) {
226     if (*s == '$') {
227       s++;
228     } else if (*strchr("*?[{", *s)) {
229       s++;
230     } else if (*s == '<' || *s == '>') {
231       s++;
232     } else s++;
233   }
234 
235   return new;
236 */
237 }
238 
239 // Assign one variable
240 // s: key=val
241 // type: 0 = whatever it was before, local otherwise
242 #define TAKE_MEM 0x80000000
243 // declare -aAilnrux
244 // ft
setvar(char * s,unsigned type)245 void setvar(char *s, unsigned type)
246 {
247   if (type&TAKE_MEM) type ^= TAKE_MEM;
248   else s = xstrdup(s);
249 
250   // local, export, readonly, integer...
251   xsetenv(s, 0);
252 }
253 
getvar(char * s)254 char *getvar(char *s)
255 {
256   return getenv(s);
257 }
258 
259 // return length of match found at this point
anystart(char * s,char ** try)260 static int anystart(char *s, char **try)
261 {
262   while (*try) {
263     if (strstart(&s, *try)) return strlen(*try);
264     try++;
265   }
266 
267   return 0;
268 }
269 
anystr(char * s,char ** try)270 static int anystr(char *s, char **try)
271 {
272   while (*try) if (!strcmp(s, *try++)) return 1;
273 
274   return 0;
275 }
276 
277 // return length of valid prefix that could go before redirect
redir_prefix(char * word)278 int redir_prefix(char *word)
279 {
280   char *s = word;
281 
282   if (*s == '{') {
283     for (s++; isalnum(*s) || *s=='_'; s++);
284     if (*s == '}' && s != word+1) s++;
285     else s = word;
286   } else while (isdigit(*s)) s++;
287 
288   return s-word;
289 }
290 
291 // TODO |&
292 
293 // rd[0] = next, 1 = prev, 2 = len, 3-x = to/from redirection pairs.
294 // Execute the commands in a pipeline segment
run_command(struct sh_arg * arg,int ** rdlist)295 struct sh_process *run_command(struct sh_arg *arg, int **rdlist)
296 {
297   struct sh_process *pp = xzalloc(sizeof(struct sh_process));
298   struct toy_list *tl;
299   char *s, *ss, *sss;
300   unsigned envlen, j;
301   int fd, here = 0, rdcount = 0, *rd = 0, *rr, hfd = 0;
302 
303   // Grab variable assignments
304   for (envlen = 0; envlen<arg->c; envlen++) {
305     s = arg->v[envlen];
306     for (j=0; s[j] && (s[j]=='_' || !ispunct(s[j])); j++);
307     if (!j || s[j] != '=') break;
308   }
309 
310   // perform assignments locally if there's no command
311   if (envlen == arg->c) {
312     for (j = 0; j<envlen; j++) {
313       struct sh_arg aa;
314 
315       aa.c = 0;
316       expand_arg(&aa, arg->v[j], NO_PATH|NO_SPLIT, 0);
317       setvar(*aa.v, TAKE_MEM);
318       free(aa.v);
319     }
320     free(pp);
321 
322     return 0;
323   }
324 
325   // We vfork() instead of fork to support nommu systems, and do
326   // redirection setup in the parent process. Open new filehandles
327   // and move them to temporary values >10. The rd[] array has pairs of
328   // filehandles: child replaces fd1 with fd2 via dup2() and close() after
329   // the vfork(). fd2 is <<1, if bottom bit set don't close it (dup instead).
330   // If fd2 < 0 it's a here document (parent process writes to a pipe later).
331 
332   // Expand arguments and perform redirections
333   for (j = envlen; j<arg->c; j++) {
334 
335     // Is this a redirect?
336     ss = (s = arg->v[j]) + redir_prefix(arg->v[j]);
337     if (!anystr(ss, (char *[]){"<<<", "<<-", "<<", "<&", "<>", "<", ">>", ">&",
338       ">|", ">", "&>>", "&>", 0}))
339     {
340       // Nope: save/expand argument and loop
341       expand_arg(&pp->arg, s, 0, 0);
342 
343       continue;
344     }
345 
346     // Yes. Expand rd[] and find first unused filehandle >10
347     if (!(rdcount&31)) {
348       if (rd) dlist_lpop((void *)rdlist);
349       rd = xrealloc(rd, (2*rdcount+3+2*32)*sizeof(int *));
350       dlist_add_nomalloc((void *)rdlist, (void *)rd);
351     }
352     rr = rd+3+rdcount;
353     if (!hfd)
354       for (hfd = 10; hfd<99999; hfd++) if (-1 == fcntl(hfd, F_GETFL)) break;
355 
356     // error check: premature EOF, target fd too high, or redirect file splits
357     if (++j == arg->c || (isdigit(*s) && ss-s>5)) goto flush;
358     fd = pp->arg.c;
359 
360     // expand arguments for everything but << and <<-
361     if (strncmp(ss, "<<", 2) || ss[2] == '<') {
362       expand_arg(&pp->arg, arg->v[j], NO_PATH|(NO_SPLIT*!strcmp(ss, "<<<")), 0);
363       if (fd+1 != pp->arg.c) goto flush;
364       sss = pp->arg.v[--pp->arg.c];
365     } else dlist_add((void *)&pp->delete, sss = xstrdup(arg->v[j]));
366 
367     // rd[] entries come in pairs: first is which fd gets redirected after
368     // vfork(), I.E. the [n] part of [n]<word
369 
370     if (isdigit(*ss)) fd = atoi(ss);
371     else if (*ss == '{') {
372       ss++;
373       // when we close a filehandle, we _read_ from {var}, not write to it
374       if ((!strcmp(s, "<&") || !strcmp(s, ">&")) && !strcmp(sss, "-")) {
375         ss = xstrndup(ss, (s-ss)-1);
376         sss = getvar(ss);
377         free(ss);
378         fd = -1;
379         if (sss) fd = atoi(sss);
380         if (fd<0) goto flush;
381         if (fd>2) {
382           rr[0] = fd;
383           rr[1] = fd<<1; // close it
384           rdcount++;
385         }
386         continue;
387       } else setvar(xmprintf("%.*s=%d", (int)(s-ss), ss, hfd),  TAKE_MEM);
388     } else fd = *ss != '<';
389     *rr = fd;
390 
391     // at this point for [n]<word s = start of [n], ss = start of <, sss = word
392 
393     // second entry in this rd[] pair is new fd to dup2() after vfork(),
394     // I.E. for [n]<word the fd if you open("word"). It's stored <<1 and the
395     // low bit set means don't close(rr[1]) after dup2(rr[1]>>1, rr[0]);
396 
397     // fd<0 means HERE document. Canned input stored earlier, becomes pipe later
398     if (!strcmp(s, "<<<") || !strcmp(s, "<<-") || !strcmp(s, "<<")) {
399       fd = --here<<2;
400       if (s[2] == '-') fd += 1;          // zap tabs
401       if (s[strcspn(s, "\"'")]) fd += 2; // it was quoted so no expansion
402       rr[1] = fd;
403       rdcount++;
404 
405       continue;
406     }
407 
408     // Handle file descriptor duplication/close (&> &>> <& >& with number or -)
409     if (strchr(ss, '&') && ss[2] != '>') {
410       char *dig = sss;
411 
412       // These redirect existing fd so nothing to open()
413       while (isdigit(dig)) dig++;
414       if (dig-sss>5) {
415         s = sss;
416         goto flush;
417       }
418 
419 // TODO can't check if fd is open here, must do it when actual redirects happen
420       if (!*dig || (*dig=='-' && !dig[1])) {
421         rr[1] = (((dig==sss) ? *rr : atoi(sss))<<1)+(*dig != '-');
422         rdcount++;
423 
424         continue;
425       }
426     }
427 
428     // Permissions to open external file with: < > >> <& >& <> >| &>> &>
429     if (!strcmp(ss, "<>")) fd = O_CREAT|O_RDWR;
430     else if (strstr(ss, ">>")) fd = O_CREAT|O_APPEND;
431     else {
432       fd = (*ss != '<') ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY;
433       if (!strcmp(ss, ">") && (TT.options&SH_NOCLOBBER)) {
434         struct stat st;
435 
436         // Not _just_ O_EXCL: > /dev/null allowed
437         if (stat(sss, &st) || !S_ISREG(st.st_mode)) fd |= O_EXCL;
438       }
439     }
440 
441     // Open the file
442 // TODO: /dev/fd/# /dev/{stdin,stdout,stderr} /dev/{tcp,udp}/host/port
443     if (-1 == (fd = xcreate(sss, fd|WARN_ONLY, 777)) || hfd != dup2(fd, hfd)) {
444       pp->exit = 1;
445       s = 0;
446 
447       goto flush;
448     }
449     if (fd != hfd) close(fd);
450     rr[1] = hfd<<1;
451     rdcount++;
452 
453     // queue up a 2>&1 ?
454     if (strchr(ss, '&')) {
455       if (!(31&++rdcount)) rd = xrealloc(rd, (2*rdcount+66)*sizeof(int *));
456       rr = rd+3+rdcount;
457       rr[0] = 2;
458       rr[1] = 1+(1<<1);
459       rdcount++;
460     }
461   }
462   if (rd) rd[2] = rdcount;
463 
464 // TODO: ok, now _use_ in_rd[in_rdcount] and rd[rdcount]. :)
465 
466 // TODO: handle ((math)) here
467 
468 // TODO use envlen
469 // TODO: check for functions
470 
471   // Is this command a builtin that should run in this process?
472   if ((tl = toy_find(*pp->arg.v))
473     && (tl->flags & (TOYFLAG_NOFORK|TOYFLAG_MAYFORK)))
474   {
475     struct toy_context temp;
476     sigjmp_buf rebound;
477 
478     // This fakes lots of what toybox_main() does.
479     memcpy(&temp, &toys, sizeof(struct toy_context));
480     memset(&toys, 0, sizeof(struct toy_context));
481 
482 // TODO: redirect stdin/out
483     if (!sigsetjmp(rebound, 1)) {
484       toys.rebound = &rebound;
485 // must be null terminated
486       toy_init(tl, pp->arg.v);
487       tl->toy_main();
488     }
489     pp->exit = toys.exitval;
490     if (toys.optargs != toys.argv+1) free(toys.optargs);
491     if (toys.old_umask) umask(toys.old_umask);
492     memcpy(&toys, &temp, sizeof(struct toy_context));
493   } else {
494     int pipe[2];
495 
496     pipe[0] = 0;
497     pipe[1] = 1;
498 // TODO: redirect and pipe
499 // TODO: redirecting stderr needs xpopen3() or rethink
500     if (-1 == (pp->pid = xpopen_both(pp->arg.v, pipe)))
501       perror_msg("%s: vfork", *pp->arg.v);
502 // TODO: don't close stdin/stdout!
503     else pp->exit = xpclose_both(pp->pid, 0);
504   }
505 
506   s = 0;
507 flush:
508   if (s) {
509     syntax_err("bad %s", s);
510     if (!pp->exit) pp->exit = 1;
511   }
512   for (j = 0; j<rdcount; j++) if (rd[4+2*j]>6) close(rd[4+2*j]>>1);
513   if (rdcount) free(dlist_lpop((void *)rdlist));
514 
515   return pp;
516 }
517 
518 // parse next word from command line. Returns end, or 0 if need continuation
519 // caller eats leading spaces
parse_word(char * start)520 static char *parse_word(char *start)
521 {
522   int i, j, quote = 0, q, qc = 0;
523   char *end = start, *s;
524 
525   // (( is a special quote at the start of a word
526   if (strstart(&end, "((")) toybuf[quote++] = 255;
527 
528   // find end of this word
529   while (*end) {
530     i = 0;
531 
532     // barf if we're near overloading quote stack (nesting ridiculously deep)
533     if (quote>4000) {
534       syntax_err("tilt");
535       return (void *)1;
536     }
537 
538     q = quote ? toybuf[quote-1] : 0;
539     // Handle quote contexts
540     if (q) {
541 
542       // when waiting for parentheses, they nest
543       if ((q == ')' || q == '\xff') && (*end == '(' || *end == ')')) {
544         if (*end == '(') qc++;
545         else if (qc) qc--;
546         else if (q == '\xff') {
547           // (( can end with )) or retroactively become two (( if we hit one )
548           if (strstart(&end, "))")) quote--;
549           else return start+1;
550         }
551         end++;
552 
553       // end quote?
554       } else if (*end == q) quote--, end++;
555 
556       // single quote claims everything
557       else if (q == '\'') end++;
558       else i++;
559 
560       // loop if we already handled a symbol
561       if (!i) continue;
562     } else {
563       // Things that only matter when unquoted
564 
565       if (isspace(*end)) break;
566 
567       // Things we should only return at the _start_ of a word
568 
569       // Redirections. 123<<file- parses as 2 args: "123<<" "file-".
570       // Greedy matching: >&; becomes >& ; not > &;
571       s = end + redir_prefix(end);
572       j = anystart(s, (char *[]){"<<<", "<<-", "<<", "<&", "<>", "<", ">>",
573         ">&", ">|", ">", 0});
574       if (j) s += j;
575 
576       // Control characters
577       else s = end + anystart(end, (char *[]){";;&", ";;", ";&", ";", "||",
578           "|&", "|", "&&", "&>>", "&>", "&", "(", ")", 0});
579       if (s != end) return (end == start) ? s : end;
580       i++;
581     }
582 
583     // Things the same unquoted or in most non-single-quote contexts
584 
585     // start new quote context?
586     if (strchr("\"'`", *end)) toybuf[quote++] = *end++;
587     else if (q != '"' && (strstart(&end, "<(") || strstart(&end,">(")))
588       toybuf[quote++]=')';
589 
590     // backslash escapes
591     else if (*end == '\\') {
592       if (!end[1] || (end[1]=='\n' && !end[2])) return 0;
593       end += 2;
594     } else if (*end++ == '$') {
595       if (-1 != (i = stridx("({[", *end))) {
596         toybuf[quote++] = ")}]"[i];
597         end++;
598       }
599     }
600   }
601 
602   return quote ? 0 : end;
603 }
604 
605 // if then fi for while until select done done case esac break continue return
606 
607 // Allocate more space for arg, and possibly terminator
argxtend(struct sh_arg * arg)608 void argxtend(struct sh_arg *arg)
609 {
610   if (!(arg->c&31)) arg->v = xrealloc(arg->v, (33+arg->c)*sizeof(void *));
611 }
612 
613 // Pipeline segments
614 struct sh_pipeline {
615   struct sh_pipeline *next, *prev;
616   int count, here, type;
617   struct sh_arg arg[1];
618 };
619 
620 // run a series of "command | command && command" with redirects.
run_pipeline(struct sh_pipeline ** pl,int * rd)621 int run_pipeline(struct sh_pipeline **pl, int *rd)
622 {
623   struct sh_process *pp;
624   int rc = 0;
625 
626   for (;;) {
627 // TODO job control
628     if (!(pp = run_command((*pl)->arg, &rd))) rc = 0;
629     else {
630 //wait4(pp);
631       llist_traverse(pp->delete, free);
632       rc = pp->exit;
633       free(pp);
634     }
635 
636     if ((*pl)->next && !(*pl)->next->type) *pl = (*pl)->next;
637     else return rc;
638   }
639 }
640 
641 
642 
643 // scratch space (state held between calls). Don't want to make it global yet
644 // because this could be reentrant.
645 struct sh_function {
646   char *name;
647   struct sh_pipeline *pipeline;
648   struct double_list *expect;
649 // TODO: lifetime rules for arg? remember "shift" command.
650   struct sh_arg *arg; // arguments to function call
651   char *end;
652 };
653 
654 // Free one pipeline segment.
free_pipeline(void * pipeline)655 void free_pipeline(void *pipeline)
656 {
657   struct sh_pipeline *pl = pipeline;
658   int i, j;
659 
660   if (pl) for (j=0; j<=pl->count; j++) {
661     for (i = 0; i<=pl->arg->c; i++)  free(pl->arg[j].v[i]);
662     free(pl->arg[j].v);
663   }
664   free(pl);
665 }
666 
667 // Return end of current block, or NULL if we weren't in block and fell off end.
block_end(struct sh_pipeline * pl)668 struct sh_pipeline *block_end(struct sh_pipeline *pl)
669 {
670   int i = 0;
671 
672   while (pl) {
673     if (pl->type == 1 || pl->type == 'f') i++;
674     else if (pl->type == 3) if (--i<1) break;
675     pl = pl->next;
676   }
677 
678   return 0;
679 }
680 
free_function(struct sh_function * sp)681 void free_function(struct sh_function *sp)
682 {
683   llist_traverse(sp->pipeline, free_pipeline);
684   llist_traverse(sp->expect, free);
685   memset(sp, 0, sizeof(struct sh_function));
686 }
687 
688 // TODO this has to add to a namespace context. Functions within functions...
add_function(char * name,struct sh_pipeline * pl)689 struct sh_pipeline *add_function(char *name, struct sh_pipeline *pl)
690 {
691 dprintf(2, "stub add_function");
692 
693   return block_end(pl->next);
694 }
695 
696 // Add a line of shell script to a shell function. Returns 0 if finished,
697 // 1 to request another line of input (> prompt), -1 for syntax err
parse_line(char * line,struct sh_function * sp)698 static int parse_line(char *line, struct sh_function *sp)
699 {
700   char *start = line, *delete = 0, *end, *last = 0, *s, *ex, done = 0;
701   struct sh_pipeline *pl = sp->pipeline ? sp->pipeline->prev : 0;
702   struct sh_arg *arg = 0;
703   long i;
704 
705   // Resume appending to last statement?
706   if (pl) {
707     arg = pl->arg;
708 
709     // Extend/resume quoted block
710     if (arg->c<0) {
711       delete = start = xmprintf("%s%s", arg->v[arg->c = (-arg->c)-1], start);
712       free(arg->v[arg->c]);
713       arg->v[arg->c] = 0;
714 
715     // is a HERE document in progress?
716     } else if (pl->count != pl->here) {
717       arg += 1+pl->here;
718 
719       argxtend(arg);
720       if (strcmp(line, arg->v[arg->c])) {
721         // Add this line
722         arg->v[arg->c+1] = arg->v[arg->c];
723         arg->v[arg->c++] = xstrdup(line);
724       // EOF hit, end HERE document
725       } else {
726         arg->v[arg->c] = 0;
727         pl->here++;
728       }
729       start = 0;
730 
731     // Nope, new segment
732     } else pl = 0;
733   }
734 
735   // Parse words, assemble argv[] pipelines, check flow control and HERE docs
736   if (start) for (;;) {
737     ex = sp->expect ? sp->expect->prev->data : 0;
738 
739     // Look for << HERE redirections in completed pipeline segment
740     if (pl && pl->count == -1) {
741       pl->count = 0;
742       arg = pl->arg;
743 
744       // find arguments of the form [{n}]<<[-] with another one after it
745       for (i = 0; i<arg->c; i++) {
746         s = arg->v[i] + redir_prefix(arg->v[i]);
747         if (strcmp(s, "<<") && strcmp(s, "<<-") && strcmp(s, "<<<")) continue;
748         if (i+1 == arg->c) goto flush;
749 
750         // Add another arg[] to the pipeline segment (removing/readding to list
751         // because realloc can move pointer)
752         dlist_lpop(&sp->pipeline);
753         pl = xrealloc(pl, sizeof(*pl) + ++pl->count*sizeof(struct sh_arg));
754         dlist_add_nomalloc((void *)&sp->pipeline, (void *)pl);
755 
756         // queue up HERE EOF so input loop asks for more lines.
757         arg[pl->count].v = xzalloc(2*sizeof(void *));
758         *arg[pl->count].v = arg->v[++i];
759         arg[pl->count].c = -(s[2] == '<'); // note <<< as c = -1
760       }
761       pl = 0;
762     }
763     if (done) break;
764     s = 0;
765 
766     // skip leading whitespace/comment here to know where next word starts
767     for (;;) {
768       if (isspace(*start)) ++start;
769       else if (*start=='#') while (*start && *start != '\n') ++start;
770       else break;
771     }
772 
773     // Parse next word and detect overflow (too many nested quotes).
774     if ((end = parse_word(start)) == (void *)1)
775       goto flush;
776 
777     // Is this a new pipeline segment?
778     if (!pl) {
779       pl = xzalloc(sizeof(struct sh_pipeline));
780       arg = pl->arg;
781       dlist_add_nomalloc((void *)&sp->pipeline, (void *)pl);
782     }
783     argxtend(arg);
784 
785     // Do we need to request another line to finish word (find ending quote)?
786     if (!end) {
787       // Save unparsed bit of this line, we'll need to re-parse it.
788       arg->v[arg->c] = xstrndup(start, strlen(start));
789       arg->c = -(arg->c+1);
790       free(delete);
791 
792       return 1;
793     }
794 
795     // Ok, we have a word. What does it _mean_?
796 
797     // Did we hit end of line or ) outside a function declaration?
798     // ) is only saved at start of a statement, ends current statement
799     if (end == start || (arg->c && *start == ')' && pl->type!='f')) {
800       arg->v[arg->c] = 0;
801 
802       if (pl->type == 'f' && arg->c<3) {
803         s = "function()";
804         goto flush;
805       }
806 
807       // "for" on its own line is an error.
808       if (arg->c == 1 && ex && !memcmp(ex, "do\0A", 4)) {
809         s = "newline";
810         goto flush;
811       }
812 
813       // don't save blank pipeline segments
814       if (!arg->c) free_pipeline(dlist_lpop(&sp->pipeline));
815 
816       // stop at EOL, else continue with new pipeline segment for )
817       if (end == start) done++;
818       pl->count = -1;
819       last = 0;
820 
821       continue;
822     }
823 
824     // Save argument (strdup) and check for flow control
825     s = arg->v[arg->c] = xstrndup(start, end-start);
826     start = end;
827     if (strchr(";|&", *s)) {
828 
829       // flow control without a statement is an error
830       if (!arg->c) goto flush;
831 
832       // treat ; as newline so we don't have to check both elsewhere.
833       if (!strcmp(s, ";")) {
834         arg->v[arg->c] = 0;
835         free(s);
836         s = 0;
837       }
838       last = s;
839       pl->count = -1;
840 
841       continue;
842     } else arg->v[++arg->c] = 0;
843 
844     // is a function() in progress?
845     if (arg->c>1 && !strcmp(s, "(")) pl->type = 'f';
846     if (pl->type=='f') {
847       if (arg->c == 2 && strcmp(s, "(")) goto flush;
848       if (arg->c == 3) {
849         if (strcmp(s, ")")) goto flush;
850 
851         // end function segment, expect function body
852         pl->count = -1;
853         last = 0;
854         dlist_add(&sp->expect, "}");
855         dlist_add(&sp->expect, 0);
856         dlist_add(&sp->expect, "{");
857 
858         continue;
859       }
860 
861     // a for/select must have at least one additional argument on same line
862     } else if (ex && !memcmp(ex, "do\0A", 4)) {
863 
864       // Sanity check and break the segment
865       if (strncmp(s, "((", 2) && strchr(s, '=')) goto flush;
866       pl->count = -1;
867       sp->expect->prev->data = "do\0C";
868 
869       continue;
870 
871     // flow control is the first word of a pipeline segment
872     } else if (arg->c>1) continue;
873 
874     // Do we expect something that _must_ come next? (no multiple statements)
875     if (ex) {
876       // When waiting for { it must be next symbol, but can be on a new line.
877       if (!strcmp(ex, "{")) {
878         if (strcmp(s, "{")) goto flush;
879         free(arg->v[--arg->c]);  // don't save the {, function starts the block
880         free(dlist_lpop(&sp->expect));
881 
882         continue;
883 
884       // The "test" part of for/select loops can have (at most) one "in" line,
885       // for {((;;))|name [in...]} do
886       } else if (!memcmp(ex, "do\0C", 4)) {
887         if (strcmp(s, "do")) {
888           // can only have one "in" line between for/do, but not with for(())
889           if (!pl->prev->type) goto flush;
890           if (!strncmp(pl->prev->arg->v[1], "((", 2)) goto flush;
891           else if (strcmp(s, "in")) goto flush;
892 
893           continue;
894         }
895       }
896     }
897 
898     // start of a new block?
899 
900     // for/select requires variable name on same line, can't break segment yet
901     if (!strcmp(s, "for") || !strcmp(s, "select")) {
902       if (!pl->type) pl->type = 1;
903       dlist_add(&sp->expect, "do\0A");
904 
905       continue;
906     }
907 
908     end = 0;
909     if (!strcmp(s, "if")) end = "then";
910     else if (!strcmp(s, "while") || !strcmp(s, "until")) end = "do\0B";
911     else if (!strcmp(s, "case")) end = "esac";
912     else if (!strcmp(s, "{")) end = "}";
913     else if (!strcmp(s, "[[")) end = "]]";
914     else if (!strcmp(s, "(")) end = ")";
915 
916     // Expecting NULL means a statement: I.E. any otherwise unrecognized word
917     else if (sp->expect && !ex) {
918       free(dlist_lpop(&sp->expect));
919       continue;
920     } else if (!ex) goto check;
921 
922     // Did we start a new statement?
923     if (end) {
924       pl->type = 1;
925 
926       // Only innermost statement needed in { { { echo ;} ;} ;} and such
927       if (sp->expect && !sp->expect->prev->data) free(dlist_lpop(&sp->expect));
928 
929     // If we got here we expect a specific word to end this block: is this it?
930     } else if (!strcmp(s, ex)) {
931       // can't "if | then" or "while && do", only ; & or newline works
932       if (last && (strcmp(ex, "then") || strcmp(last, "&"))) {
933         s = end;
934         goto flush;
935       }
936 
937       free(dlist_lpop(&sp->expect));
938       pl->type = anystr(s, (char *[]){"fi", "done", "esac", "}", "]]", ")", 0})
939         ? 3 : 2;
940 
941       // if it's a multipart block, what comes next?
942       if (!strcmp(s, "do")) end = "done";
943       else if (!strcmp(s, "then")) end = "fi\0A";
944 
945     // fi could have elif, which queues a then.
946     } else if (!strcmp(ex, "fi")) {
947       if (!strcmp(s, "elif")) {
948         free(dlist_lpop(&sp->expect));
949         end = "then";
950       // catch duplicate else while we're here
951       } else if (!strcmp(s, "else")) {
952         if (ex[3] != 'A') {
953           s = "2 else";
954           goto flush;
955         }
956         free(dlist_lpop(&sp->expect));
957         end = "fi\0B";
958       }
959     }
960 
961     // Do we need to queue up the next thing to expect?
962     if (end) {
963       if (!pl->type) pl->type = 2;
964       dlist_add(&sp->expect, end);
965       dlist_add(&sp->expect, 0);    // they're all preceded by a statement
966       pl->count = -1;
967     }
968 
969 check:
970     // syntax error check: these can't be the first word in an unexpected place
971     if (!pl->type && anystr(s, (char *[]){"then", "do", "esac", "}", "]]", ")",
972         "done", "fi", "elif", "else", 0})) goto flush;
973   }
974   free(delete);
975 
976   // advance past <<< arguments (stored as here documents, but no new input)
977   pl = sp->pipeline->prev;
978   while (pl->count<pl->here && pl->arg[pl->count].c<0)
979     pl->arg[pl->count++].c = 0;
980 
981   // return if HERE document pending or more flow control needed to complete
982   if (sp->expect) return 1;
983   if (sp->pipeline && pl->count != pl->here) return 1;
984   dlist_terminate(sp->pipeline);
985 
986   // Don't need more input, can start executing.
987 
988   return 0;
989 
990 flush:
991   if (s) syntax_err("bad %s", s);
992   free_function(sp);
993 
994   return 0-!!s;
995 }
996 
dump_state(struct sh_function * sp)997 static void dump_state(struct sh_function *sp)
998 {
999   struct sh_pipeline *pl;
1000   int q = 0;
1001   long i;
1002 
1003   if (sp->expect) {
1004     struct double_list *dl;
1005 
1006     for (dl = sp->expect; dl; dl = (dl->next == sp->expect) ? 0 : dl->next)
1007       dprintf(2, "expecting %s\n", dl->data);
1008     if (sp->pipeline)
1009       dprintf(2, "pipeline count=%d here=%d\n", sp->pipeline->prev->count,
1010         sp->pipeline->prev->here);
1011   }
1012 
1013   for (pl = sp->pipeline; pl ; pl = (pl->next == sp->pipeline) ? 0 : pl->next) {
1014     for (i = 0; i<pl->arg->c; i++)
1015       printf("arg[%d][%ld]=%s\n", q, i, pl->arg->v[i]);
1016     printf("type=%d term[%d]=%s\n", pl->type, q++, pl->arg->v[pl->arg->c]);
1017   }
1018 }
1019 
1020 /* Flow control statements:
1021 
1022   if/then/elif/else/fi, for select while until/do/done, case/esac,
1023   {/}, [[/]], (/), function assignment
1024 */
1025 
1026 
1027 
1028 // run a shell function, handling flow control statements
run_function(struct sh_function * sp)1029 static void run_function(struct sh_function *sp)
1030 {
1031   struct sh_pipeline *pl = sp->pipeline, *end;
1032   struct blockstack {
1033     struct blockstack *next;
1034     struct sh_pipeline *start, *end;
1035     int run, loop, *redir;
1036 
1037     struct sh_arg farg;          // for/select arg stack
1038     struct string_list *fdelete; // farg's cleanup list
1039     char *fvar;                  // for/select's iteration variable name
1040   } *blk = 0, *new;
1041   long i;
1042 
1043   // iterate through the commands
1044   while (pl) {
1045     char *s = *pl->arg->v, *ss = pl->arg->v[1];
1046 //dprintf(2, "s=%s %s %d %s %d\n", s, ss, pl->type, blk ? blk->start->arg->v[0] : "X", blk ? blk->run : 0);
1047     // Normal executable statement?
1048     if (!pl->type) {
1049 // TODO: break & is supported? Seriously? Also break > potato
1050 // TODO: break multiple aguments
1051       if (!strcmp(s, "break") || !strcmp(s, "continue")) {
1052 
1053         // How many layers to peel off?
1054         i = ss ? atol(ss) : 0;
1055         if (i<1) i = 1;
1056         if (!blk || pl->arg->c>2 || ss[strspn(ss, "0123456789")]) {
1057           syntax_err("bad %s", s);
1058           break;
1059         }
1060         i = atol(ss);
1061         if (!i) i++;
1062         while (i && blk) {
1063           if (--i && *s == 'c') {
1064             pl = blk->start;
1065             break;
1066           }
1067           pl = blk->end;
1068           llist_traverse(blk->fdelete, free);
1069           free(llist_pop(&blk));
1070         }
1071         pl = pl->next;
1072 
1073         continue;
1074       }
1075 
1076 // inherit redirects?
1077 // returns last statement of pipeline
1078       if (!blk) toys.exitval = run_pipeline(&pl, 0);
1079       else if (blk->run) toys.exitval = run_pipeline(&pl, blk->redir);
1080       else while (pl->next && !pl->next->type) pl = pl->next;
1081 
1082     // Starting a new block?
1083     } else if (pl->type == 1) {
1084 
1085       // are we entering this block (rather than looping back to it)?
1086       if (!blk || blk->start != pl) {
1087 
1088         // If it's a nested block we're not running, skip ahead.
1089         end = block_end(pl->next);
1090         if (blk && !blk->run) {
1091           pl = end;
1092           if (pl) pl = pl->next;
1093           continue;
1094         }
1095 
1096         // It's a new block we're running, save context and add it to the stack.
1097         new = xzalloc(sizeof(*blk));
1098         new->next = blk;
1099         blk = new;
1100         blk->start = pl;
1101         blk->end = end;
1102         blk->run = 1;
1103 // TODO perform block end redirects to blk->redir
1104       }
1105 
1106       // What flow control statement is this?
1107 
1108       // if/then/elif/else/fi, while until/do/done - no special handling needed
1109 
1110       // for select/do/done
1111       if (!strcmp(s, "for") || !strcmp(s, "select")) {
1112         if (blk->loop);
1113         else if (!strncmp(blk->fvar = ss, "((", 2)) {
1114           blk->loop = 1;
1115 dprintf(2, "TODO skipped init for((;;)), need math parser\n");
1116         } else {
1117 
1118           // populate blk->farg with expanded arguments
1119           if (!pl->next->type) {
1120             for (i = 1; i<pl->next->arg->c; i++)
1121               expand_arg(&blk->farg, pl->next->arg->v[i], 0, &blk->fdelete);
1122           } else expand_arg(&blk->farg, "\"$@\"", 0, &blk->fdelete);
1123         }
1124         pl = pl->next;
1125       }
1126 
1127 /* TODO
1128 case/esac
1129 {/}
1130 [[/]]
1131 (/)
1132 ((/))
1133 function/}
1134 */
1135 
1136     // gearshift from block start to block body
1137     } else if (pl->type == 2) {
1138 
1139       // Handle if statement
1140       if (!strcmp(s, "then")) blk->run = blk->run && !toys.exitval;
1141       else if (!strcmp(s, "else") || !strcmp(s, "elif")) blk->run = !blk->run;
1142       else if (!strcmp(s, "do")) {
1143         ss = *blk->start->arg->v;
1144         if (!strcmp(ss, "while")) blk->run = blk->run && !toys.exitval;
1145         else if (!strcmp(ss, "until")) blk->run = blk->run && toys.exitval;
1146         else if (blk->loop >= blk->farg.c) {
1147           blk->run = 0;
1148           pl = block_end(pl);
1149           continue;
1150         } else if (!strncmp(blk->fvar, "((", 2)) {
1151 dprintf(2, "TODO skipped running for((;;)), need math parser\n");
1152         } else setvar(xmprintf("%s=%s", blk->fvar, blk->farg.v[blk->loop++]),
1153           TAKE_MEM);
1154       }
1155 
1156     // end of block
1157     } else if (pl->type == 3) {
1158 
1159       // repeating block?
1160       if (blk->run && !strcmp(s, "done")) {
1161         pl = blk->start;
1162         continue;
1163       }
1164 
1165       // if ending a block, pop stack.
1166       llist_traverse(blk->fdelete, free);
1167       free(llist_pop(&blk));
1168 
1169 // TODO unwind redirects (cleanup blk->redir)
1170 
1171     } else if (pl->type == 'f') pl = add_function(s, pl);
1172 
1173     pl = pl->next;
1174   }
1175 
1176   // Cleanup from syntax_err();
1177   while (blk) {
1178     llist_traverse(blk->fdelete, free);
1179     free(llist_pop(&blk));
1180   }
1181 
1182   return;
1183 }
1184 
subshell_imports(void)1185 void subshell_imports(void)
1186 {
1187 /*
1188   // TODO cull local variables because 'env "()=42" env | grep 42' works.
1189 
1190   // vfork() means subshells have to export and then re-import locals/functions
1191   sprintf(toybuf, "(%d#%d)", getpid(), getppid());
1192   if ((s = getenv(toybuf))) {
1193     char *from, *to, *ss;
1194 
1195     unsetenv(toybuf);
1196     ss = s;
1197 
1198     // Loop through packing \\ until \0
1199     for (from = to = s; *from; from++, to++) {
1200       *to = *from;
1201       if (*from != '\\') continue;
1202       if (from[1] == '\\' || from[1] == '0') from++;
1203       if (from[1] != '0') continue;
1204       *to = 0;
1205 
1206       // save chunk
1207       for (ss = s; ss<to; ss++) {
1208         if (*ss == '=') {
1209           // first char of name is variable type ala declare
1210           if (s+1<ss && strchr("aAilnru", *s)) {
1211             setvar(ss, *s);
1212 
1213             break;
1214           }
1215         } else if (!strncmp(ss, "(){", 3)) {
1216           FILE *ff = fmemopen(s, to-s, "r");
1217 
1218           while ((new = xgetline(ff, 0))) {
1219             if ((prompt = parse_line(new, &scratch))<0) break;
1220             free(new);
1221           }
1222           if (!prompt) {
1223             add_function(s, scratch.pipeline);
1224             free_function(&scratch);
1225             break;
1226           }
1227           fclose(ff);
1228         } else if (!isspace(*s) && !ispunct(*s)) continue;
1229 
1230         error_exit("bad locals");
1231       }
1232       s = from+1;
1233     }
1234   }
1235 */
1236 }
1237 
sh_main(void)1238 void sh_main(void)
1239 {
1240   FILE *f;
1241   char *new;
1242   struct sh_function scratch;
1243   int prompt = 0;
1244 
1245   // Set up signal handlers and grab control of this tty.
1246 
1247   // Read environment for exports from parent shell
1248   subshell_imports();
1249 
1250   memset(&scratch, 0, sizeof(scratch));
1251   if (TT.command) f = fmemopen(TT.command, strlen(TT.command), "r");
1252   else if (*toys.optargs) f = xfopen(*toys.optargs, "r");
1253   else {
1254     f = stdin;
1255     if (isatty(0)) toys.optflags |= FLAG_i;
1256   }
1257 
1258   for (;;) {
1259 
1260     // Prompt and read line
1261     if (f == stdin) {
1262       char *s = getenv(prompt ? "PS2" : "PS1");
1263 
1264       if (!s) s = prompt ? "> " : (getpid() ? "\\$ " : "# ");
1265       do_prompt(s);
1266     } else TT.lineno++;
1267     if (!(new = xgetline(f ? f : stdin, 0))) break;
1268 
1269 // TODO if (!isspace(*new)) add_to_history(line);
1270 
1271     // returns 0 if line consumed, command if it needs more data
1272     prompt = parse_line(new, &scratch);
1273 if (0) dump_state(&scratch);
1274     if (prompt != 1) {
1275 // TODO: ./blah.sh one two three: put one two three in scratch.arg
1276       if (!prompt) run_function(&scratch);
1277       free_function(&scratch);
1278       prompt = 0;
1279     }
1280     free(new);
1281   }
1282 
1283   if (prompt) error_exit("%ld:unfinished line"+4*!TT.lineno, TT.lineno);
1284   toys.exitval = f && ferror(f);
1285 }
1286