Contents of /trunk/mkinitrd-magellan/busybox/shell/hush_doc.txt
Parent Directory | Revision Log
Revision 816 -
(show annotations)
(download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 5172 byte(s)
Fri Apr 24 18:33:46 2009 UTC (15 years, 5 months ago) by niro
File MIME type: text/plain
File size: 5172 byte(s)
-updated to busybox-1.13.4
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 | */ |