[3320] | 1 | 2008-07-14
|
---|
| 2 |
|
---|
| 3 | Command parsing
|
---|
| 4 |
|
---|
| 5 | Command parsing results in a list of "pipe" structures.
|
---|
| 6 | This list correspond not only to usual "pipe1 || pipe2 && pipe3"
|
---|
| 7 | lists, but it also controls execution of if, while, etc statements.
|
---|
| 8 | Every such statement is a list for hush. List consists of pipes.
|
---|
| 9 |
|
---|
| 10 | struct 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 |
|
---|
| 18 | Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
|
---|
| 19 | as one pipe struct with one progs[0] element which is a "group" -
|
---|
| 20 | struct child_prog can contain a list of pipes. Sometimes these
|
---|
| 21 | "groups" are created implicitly, e.g. every control
|
---|
| 22 | statement (if, while, etc) sits inside its own group.
|
---|
| 23 |
|
---|
| 24 | res_word controls statement execution. Examples:
|
---|
| 25 |
|
---|
| 26 | "echo Hello" -
|
---|
| 27 | pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
|
---|
| 28 | pipe 1 res_word=NONE followup=SEQ
|
---|
| 29 |
|
---|
| 30 | "echo foo || echo bar" -
|
---|
| 31 | pipe 0 res_word=NONE followup=OR prog[0] 'echo' 'foo'
|
---|
| 32 | pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
|
---|
| 33 | pipe 2 res_word=NONE followup=SEQ
|
---|
| 34 |
|
---|
| 35 | "if true; then echo Hello; true; fi" -
|
---|
| 36 | res_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)
|
---|
| 43 | pipe 1 res_word=NONE followup=SEQ
|
---|
| 44 |
|
---|
| 45 | Above you see that if is a list, and it sits in a {} group
|
---|
| 46 | implicitly created by hush. Also note two THEN res_word's -
|
---|
| 47 | it is explained below.
|
---|
| 48 |
|
---|
| 49 | "if true; then { echo Hello; true; }; fi" -
|
---|
| 50 | pipe 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)
|
---|
| 59 | pipe 1 res_word=NONE followup=SEQ
|
---|
| 60 |
|
---|
| 61 | "for v in a b; do echo $v; true; done" -
|
---|
| 62 | pipe 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)
|
---|
| 70 | pipe 1 res_word=NONE followup=SEQ
|
---|
| 71 |
|
---|
| 72 | Note how "THEN" and "DO" does not just mark the first pipe,
|
---|
| 73 | it "sticks" to all pipes in the body. This is used when
|
---|
| 74 | hush executes parsed pipes.
|
---|
| 75 |
|
---|
| 76 | Dummy trailing pipes with no commands are artifacts of imperfect
|
---|
| 77 | parsing algorithm - done_pipe() appends new pipe struct beforehand
|
---|
| 78 | and last one ends up empty and unused.
|
---|
| 79 |
|
---|
| 80 | "for" and "case" statements (ab)use progs[] to keep their data
|
---|
| 81 | instead of argv vector progs[] usually do. "for" keyword is forcing
|
---|
| 82 | pipe termination after first word, which makes hush see
|
---|
| 83 | "for v in..." as "for v; in...". "case" keyword does the same.
|
---|
| 84 | Other 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" -
|
---|
| 90 | pipe 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)
|
---|
| 100 | pipe 1 res_word=NONE followup=SEQ
|
---|
| 101 |
|
---|
| 102 |
|
---|
| 103 | 2008-01
|
---|
| 104 |
|
---|
| 105 | Command execution
|
---|
| 106 |
|
---|
| 107 | /* callsite: process_command_subs */
|
---|
| 108 | generate_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 */
|
---|
| 119 | run_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 */
|
---|
| 124 | run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops
|
---|
| 125 | run_pipe - for every pipe in list
|
---|
| 126 |
|
---|
| 127 | /* callsite: run_list */
|
---|
| 128 | run_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 */
|
---|
| 134 | pseudo_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 | */
|
---|