busybox/shell/hush_doc.txt
<<
>>
Prefs
   12008-07-14
   2
   3        Command parsing
   4
   5Command parsing results in a list of "pipe" structures.
   6This list correspond not only to usual "pipe1 || pipe2 && pipe3"
   7lists, but it also controls execution of if, while, etc statements.
   8Every such statement is a list for hush. List consists of pipes.
   9
  10struct pipe fields:
  11  smallint res_word - "none" for normal commands,
  12                      "if" for if condition etc
  13  struct child_prog progs[] - array of commands in pipe
  14  smallint followup - how this pipe is related to next: is it
  15                      "pipe; pipe", "pipe & pipe" "pipe && pipe",
  16                      "pipe || pipe"?
  17
  18Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
  19as one pipe struct with one progs[0] element which is a "group" -
  20struct child_prog can contain a list of pipes. Sometimes these
  21"groups" are created implicitly, e.g. every control
  22statement (if, while, etc) sits inside its own group.
  23
  24res_word controls statement execution. Examples:
  25
  26"echo Hello" -
  27pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
  28pipe 1 res_word=NONE followup=SEQ
  29
  30"echo foo || echo bar" -
  31pipe 0 res_word=NONE followup=OR  prog[0] 'echo' 'foo'
  32pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
  33pipe 2 res_word=NONE followup=SEQ
  34
  35"if true; then echo Hello; true; fi" -
  36res_word=NONE followup=SEQ
  37 prog 0 group {}:
  38  pipe 0 res_word=IF followup=SEQ prog[0] 'true'
  39  pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello'
  40  pipe 2 res_word=THEN followup=SEQ prog[0] 'true'
  41  pipe 3 res_word=FI followup=SEQ
  42  pipe 4 res_word=NONE followup=(null)
  43pipe 1 res_word=NONE followup=SEQ
  44
  45Above you see that if is a list, and it sits in a {} group
  46implicitly created by hush. Also note two THEN res_word's -
  47it is explained below.
  48
  49"if true; then { echo Hello; true; }; fi" -
  50pipe 0 res_word=NONE followup=SEQ
  51 prog 0 group {}:
  52  pipe 0 res_word=IF followup=SEQ prog[0] 'true'
  53  pipe 1 res_word=THEN followup=SEQ
  54   prog 0 group {}:
  55    pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
  56    pipe 1 res_word=NONE followup=SEQ prog[0] 'true'
  57    pipe 2 res_word=NONE followup=SEQ
  58  pipe 2 res_word=NONE followup=(null)
  59pipe 1 res_word=NONE followup=SEQ
  60
  61"for v in a b; do echo $v; true; done" -
  62pipe 0 res_word=NONE followup=SEQ
  63 prog 0 group {}:
  64  pipe 0 res_word=FOR followup=SEQ prog[0] 'v'
  65  pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b'
  66  pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v'
  67  pipe 3 res_word=DO followup=SEQ prog[0] 'true'
  68  pipe 4 res_word=DONE followup=SEQ
  69  pipe 5 res_word=NONE followup=(null)
  70pipe 1 res_word=NONE followup=SEQ
  71
  72Note how "THEN" and "DO" does not just mark the first pipe,
  73it "sticks" to all pipes in the body. This is used when
  74hush executes parsed pipes.
  75
  76Dummy trailing pipes with no commands are artifacts of imperfect
  77parsing algorithm - done_pipe() appends new pipe struct beforehand
  78and last one ends up empty and unused.
  79
  80"for" and "case" statements (ab)use progs[] to keep their data
  81instead of argv vector progs[] usually do. "for" keyword is forcing
  82pipe termination after first word, which makes hush see
  83"for v in..." as "for v; in...". "case" keyword does the same.
  84Other judiciuosly placed hacks make hush see
  85"case word in a) cmd1;; b) cmd2;; esac" as if it was
  86"case word; match a; cmd; match b; cmd2; esac"
  87("match" is a fictitious keyword here):
  88
  89"case word in a) cmd1;; b) cmd2; esac" -
  90pipe 0 res_word=NONE followup=1 SEQ
  91 prog 0 group {}:
  92  pipe 0 res_word=CASE followup=SEQ prog[0] 'word'
  93  pipe 1 res_word=MATCH followup=SEQ prog[0] 'a'
  94  pipe 2 res_word=CASEI followup=SEQ prog[0] 'cmd1'
  95  pipe 3 res_word=MATCH followup=SEQ prog[0] 'b'
  96  pipe 4 res_word=CASEI followup=SEQ prog[0] 'cmd2'
  97  pipe 5 res_word=CASEI followup=SEQ prog[0] 'cmd3'
  98  pipe 6 res_word=ESAC followup=SEQ
  99  pipe 7 res_word=NONE followup=(null)
 100pipe 1 res_word=NONE followup=SEQ
 101
 102
 1032008-01
 104
 105        Command execution
 106
 107/* callsite: process_command_subs */
 108generate_stream_from_list(struct pipe *head) - handles `cmds`
 109  create UNIX pipe
 110  [v]fork
 111  child:
 112  redirect pipe output to stdout
 113  _exit(run_list(head));   /* leaks memory */
 114  parent:
 115  return UNIX pipe's output fd
 116  /* head is freed by the caller */
 117
 118/* callsite: parse_and_run_stream */
 119run_and_free_list(struct pipe *)
 120  run_list(struct pipe *)
 121  free_pipe_list(struct pipe *)
 122
 123/* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */
 124run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops
 125  run_pipe - for every pipe in list
 126
 127/* callsite: run_list */
 128run_pipe - runs "cmd1 | cmd2 | cmd3 [&]"
 129  run_list - used if only one cmd and it is of the form "{cmds;}"
 130  forks for every cmd if more than one cmd or if & is there
 131  pseudo_exec - runs each "cmdN" (handles builtins etc)
 132
 133/* callsite: run_pipe */
 134pseudo_exec - runs "cmd" (handles builtins etc)
 135  exec - execs external programs
 136  run_list - used if cmdN is "(cmds)" or "{cmds;}"
 137  /* problem: putenv's malloced strings into environ -
 138  ** with vfork they will leak into parent process
 139  */
 140  /* problem with ENABLE_FEATURE_SH_STANDALONE:
 141  ** run_applet_no_and_exit(a, argv) uses exit - this can interfere
 142  ** with vfork - switch to _exit there?
 143  */
 144