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