123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- 2008-07-14
- Command parsing
- Command parsing results in a list of "pipe" structures.
- This list correspond not only to usual "pipe1 || pipe2 && pipe3"
- lists, but it also controls execution of if, while, etc statements.
- Every such statement is a list for hush. List consists of pipes.
- struct pipe fields:
- smallint res_word - "none" for normal commands,
- "if" for if condition etc
- struct child_prog progs[] - array of commands in pipe
- smallint followup - how this pipe is related to next: is it
- "pipe; pipe", "pipe & pipe" "pipe && pipe",
- "pipe || pipe"?
- Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
- as one pipe struct with one progs[0] element which is a "group" -
- struct child_prog can contain a list of pipes. Sometimes these
- "groups" are created implicitly, e.g. every control
- statement (if, while, etc) sits inside its own group.
- res_word controls statement execution. Examples:
- "echo Hello" -
- pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
- pipe 1 res_word=NONE followup=SEQ
- "echo foo || echo bar" -
- pipe 0 res_word=NONE followup=OR prog[0] 'echo' 'foo'
- pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
- pipe 2 res_word=NONE followup=SEQ
- "if true; then echo Hello; true; fi" -
- res_word=NONE followup=SEQ
- prog 0 group {}:
- pipe 0 res_word=IF followup=SEQ prog[0] 'true'
- pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello'
- pipe 2 res_word=THEN followup=SEQ prog[0] 'true'
- pipe 3 res_word=FI followup=SEQ
- pipe 4 res_word=NONE followup=(null)
- pipe 1 res_word=NONE followup=SEQ
- Above you see that if is a list, and it sits in a {} group
- implicitly created by hush. Also note two THEN res_word's -
- it is explained below.
- "if true; then { echo Hello; true; }; fi" -
- pipe 0 res_word=NONE followup=SEQ
- prog 0 group {}:
- pipe 0 res_word=IF followup=SEQ prog[0] 'true'
- pipe 1 res_word=THEN followup=SEQ
- prog 0 group {}:
- pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
- pipe 1 res_word=NONE followup=SEQ prog[0] 'true'
- pipe 2 res_word=NONE followup=SEQ
- pipe 2 res_word=NONE followup=(null)
- pipe 1 res_word=NONE followup=SEQ
- "for v in a b; do echo $v; true; done" -
- pipe 0 res_word=NONE followup=SEQ
- prog 0 group {}:
- pipe 0 res_word=FOR followup=SEQ prog[0] 'v'
- pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b'
- pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v'
- pipe 3 res_word=DO followup=SEQ prog[0] 'true'
- pipe 4 res_word=DONE followup=SEQ
- pipe 5 res_word=NONE followup=(null)
- pipe 1 res_word=NONE followup=SEQ
- Note how "THEN" and "DO" does not just mark the first pipe,
- it "sticks" to all pipes in the body. This is used when
- hush executes parsed pipes.
- Dummy trailing pipes with no commands are artifacts of imperfect
- parsing algorithm - done_pipe() appends new pipe struct beforehand
- and last one ends up empty and unused.
- "for" and "case" statements (ab)use progs[] to keep their data
- instead of argv vector progs[] usually do. "for" keyword is forcing
- pipe termination after first word, which makes hush see
- "for v in..." as "for v; in...". "case" keyword does the same.
- Other judiciuosly placed hacks make hush see
- "case word in a) cmd1;; b) cmd2;; esac" as if it was
- "case word; match a; cmd; match b; cmd2; esac"
- ("match" is a fictitious keyword here):
- "case word in a) cmd1;; b) cmd2; esac" -
- pipe 0 res_word=NONE followup=1 SEQ
- prog 0 group {}:
- pipe 0 res_word=CASE followup=SEQ prog[0] 'word'
- pipe 1 res_word=MATCH followup=SEQ prog[0] 'a'
- pipe 2 res_word=CASEI followup=SEQ prog[0] 'cmd1'
- pipe 3 res_word=MATCH followup=SEQ prog[0] 'b'
- pipe 4 res_word=CASEI followup=SEQ prog[0] 'cmd2'
- pipe 5 res_word=CASEI followup=SEQ prog[0] 'cmd3'
- pipe 6 res_word=ESAC followup=SEQ
- pipe 7 res_word=NONE followup=(null)
- pipe 1 res_word=NONE followup=SEQ
- 2008-01
- Command execution
- /* callsite: process_command_subs */
- generate_stream_from_list(struct pipe *head) - handles `cmds`
- create UNIX pipe
- [v]fork
- child:
- redirect pipe output to stdout
- _exit(run_list(head)); /* leaks memory */
- parent:
- return UNIX pipe's output fd
- /* head is freed by the caller */
- /* callsite: parse_and_run_stream */
- run_and_free_list(struct pipe *)
- run_list(struct pipe *)
- free_pipe_list(struct pipe *)
- /* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */
- run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops
- run_pipe - for every pipe in list
- /* callsite: run_list */
- run_pipe - runs "cmd1 | cmd2 | cmd3 [&]"
- run_list - used if only one cmd and it is of the form "{cmds;}"
- forks for every cmd if more than one cmd or if & is there
- pseudo_exec - runs each "cmdN" (handles builtins etc)
- /* callsite: run_pipe */
- pseudo_exec - runs "cmd" (handles builtins etc)
- exec - execs external programs
- run_list - used if cmdN is "(cmds)" or "{cmds;}"
- /* problem: putenv's malloced strings into environ -
- ** with vfork they will leak into parent process
- */
- /* problem with ENABLE_FEATURE_SH_STANDALONE:
- ** run_applet_no_and_exit(a, argv) uses exit - this can interfere
- ** with vfork - switch to _exit there?
- */
|