Mercurial > hg > toybox
comparison toys/posix/sh.c @ 694:786841fdb1e0
Reindent to two spaces per level. Remove vi: directives that haven't worked right in years (ubuntu broke its' vim implementation). Remove trailing spaces. Add/remove blank lines. Re-wordwrap in places. Update documentation with new coding style.
The actual code should be the same afterward, this is just cosmetic refactoring.
author | Rob Landley <rob@landley.net> |
---|---|
date | Tue, 13 Nov 2012 17:14:08 -0600 |
parents | 7e846e281e38 |
children | 99ca30ad3d2b |
comparison
equal
deleted
inserted
replaced
693:4a5a250e0633 | 694:786841fdb1e0 |
---|---|
1 /* vi: set sw=4 ts=4: | 1 /* sh.c - toybox shell |
2 * | |
3 * sh.c - toybox shell | |
4 * | 2 * |
5 * Copyright 2006 Rob Landley <rob@landley.net> | 3 * Copyright 2006 Rob Landley <rob@landley.net> |
6 * | 4 * |
7 * The POSIX-2008/SUSv4 spec for this is at: | 5 * The POSIX-2008/SUSv4 spec for this is at: |
8 * http://opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html | 6 * http://opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html |
28 | 26 |
29 USE_SH(NEWTOY(sh, "c:i", TOYFLAG_BIN)) | 27 USE_SH(NEWTOY(sh, "c:i", TOYFLAG_BIN)) |
30 USE_SH(OLDTOY(toysh, sh, "c:i", TOYFLAG_BIN)) | 28 USE_SH(OLDTOY(toysh, sh, "c:i", TOYFLAG_BIN)) |
31 | 29 |
32 config SH | 30 config SH |
33 bool "sh (toysh)" | 31 bool "sh (toysh)" |
34 default n | 32 default n |
35 help | 33 help |
36 usage: sh [-c command] [script] | 34 usage: sh [-c command] [script] |
37 | 35 |
38 Command shell. Runs a shell script, or reads input interactively | 36 Command shell. Runs a shell script, or reads input interactively |
39 and responds to it. | 37 and responds to it. |
40 | 38 |
41 -c command line to execute | 39 -c command line to execute |
42 | 40 |
43 config SH_TTY | 41 config SH_TTY |
44 bool "Interactive shell (terminal control)" | 42 bool "Interactive shell (terminal control)" |
45 default n | 43 default n |
46 depends on SH | 44 depends on SH |
47 help | 45 help |
48 Add terminal control to toysh. This is necessary for interactive use, | 46 Add terminal control to toysh. This is necessary for interactive use, |
49 so the shell isn't killed by CTRL-C. | 47 so the shell isn't killed by CTRL-C. |
50 | 48 |
51 config SH_PROFILE | 49 config SH_PROFILE |
52 bool "Profile support" | 50 bool "Profile support" |
53 default n | 51 default n |
54 depends on SH_TTY | 52 depends on SH_TTY |
55 help | 53 help |
56 Read /etc/profile and ~/.profile when running interactively. | 54 Read /etc/profile and ~/.profile when running interactively. |
57 | 55 |
58 Also enables the built-in command "source". | 56 Also enables the built-in command "source". |
59 | 57 |
60 config SH_JOBCTL | 58 config SH_JOBCTL |
61 bool "Job Control (fg, bg, jobs)" | 59 bool "Job Control (fg, bg, jobs)" |
62 default n | 60 default n |
63 depends on SH_TTY | 61 depends on SH_TTY |
64 help | 62 help |
65 Add job control to toysh. This lets toysh handle CTRL-Z, and enables | 63 Add job control to toysh. This lets toysh handle CTRL-Z, and enables |
66 the built-in commands "fg", "bg", and "jobs". | 64 the built-in commands "fg", "bg", and "jobs". |
67 | 65 |
68 With pipe support, enable use of "&" to run background processes. | 66 With pipe support, enable use of "&" to run background processes. |
69 | 67 |
70 config SH_FLOWCTL | 68 config SH_FLOWCTL |
71 bool "Flow control (if, while, for, functions)" | 69 bool "Flow control (if, while, for, functions)" |
72 default n | 70 default n |
73 depends on SH | 71 depends on SH |
74 help | 72 help |
75 Add flow control to toysh. This enables the if/then/else/fi, | 73 Add flow control to toysh. This enables the if/then/else/fi, |
76 while/do/done, and for/do/done constructs. | 74 while/do/done, and for/do/done constructs. |
77 | 75 |
78 With pipe support, this enables the ability to define functions | 76 With pipe support, this enables the ability to define functions |
79 using the "function name" or "name()" syntax, plus curly brackets | 77 using the "function name" or "name()" syntax, plus curly brackets |
80 "{ }" to group commands. | 78 "{ }" to group commands. |
81 | 79 |
82 config SH_QUOTES | 80 config SH_QUOTES |
83 bool "Smarter argument parsing (quotes)" | 81 bool "Smarter argument parsing (quotes)" |
84 default n | 82 default n |
85 depends on SH | 83 depends on SH |
86 help | 84 help |
87 Add support for parsing "" and '' style quotes to the toysh command | 85 Add support for parsing "" and '' style quotes to the toysh command |
88 parser, with lets arguments have spaces in them. | 86 parser, with lets arguments have spaces in them. |
89 | 87 |
90 config SH_WILDCARDS | 88 config SH_WILDCARDS |
91 bool "Wildcards ( ?*{,} )" | 89 bool "Wildcards ( ?*{,} )" |
92 default n | 90 default n |
93 depends on SH_QUOTES | 91 depends on SH_QUOTES |
94 help | 92 help |
95 Expand wildcards in argument names, ala "ls -l *.t?z" and | 93 Expand wildcards in argument names, ala "ls -l *.t?z" and |
96 "rm subdir/{one,two,three}.txt". | 94 "rm subdir/{one,two,three}.txt". |
97 | 95 |
98 config SH_PROCARGS | 96 config SH_PROCARGS |
99 bool "Executable arguments ( `` and $() )" | 97 bool "Executable arguments ( `` and $() )" |
100 default n | 98 default n |
101 depends on SH_QUOTES | 99 depends on SH_QUOTES |
102 help | 100 help |
103 Add support for executing arguments contianing $() and ``, using | 101 Add support for executing arguments contianing $() and ``, using |
104 the output of the command as the new argument value(s). | 102 the output of the command as the new argument value(s). |
105 | 103 |
106 (Bash calls this "command substitution".) | 104 (Bash calls this "command substitution".) |
107 | 105 |
108 config SH_ENVVARS | 106 config SH_ENVVARS |
109 bool "Environment variable support" | 107 bool "Environment variable support" |
110 default n | 108 default n |
111 depends on SH_QUOTES | 109 depends on SH_QUOTES |
112 help | 110 help |
113 Substitute environment variable values for $VARNAME or ${VARNAME}, | 111 Substitute environment variable values for $VARNAME or ${VARNAME}, |
114 and enable the built-in command "export". | 112 and enable the built-in command "export". |
115 | 113 |
116 config SH_LOCALS | 114 config SH_LOCALS |
117 bool "Local variables" | 115 bool "Local variables" |
118 default n | 116 default n |
119 depends on SH_ENVVARS | 117 depends on SH_ENVVARS |
120 help | 118 help |
121 Support for local variables, fancy prompts ($PS1), the "set" command, | 119 Support for local variables, fancy prompts ($PS1), the "set" command, |
122 and $?. | 120 and $?. |
123 | 121 |
124 config SH_ARRAYS | 122 config SH_ARRAYS |
125 bool "Array variables" | 123 bool "Array variables" |
126 default n | 124 default n |
127 depends on SH_LOCALS | 125 depends on SH_LOCALS |
128 help | 126 help |
129 Support for ${blah[blah]} style array variables. | 127 Support for ${blah[blah]} style array variables. |
130 | 128 |
131 config SH_PIPES | 129 config SH_PIPES |
132 bool "Pipes and redirects ( | > >> < << & && | || () ; )" | 130 bool "Pipes and redirects ( | > >> < << & && | || () ; )" |
133 default n | 131 default n |
134 depends on SH | 132 depends on SH |
135 help | 133 help |
136 Support multiple commands on the same command line. This includes | 134 Support multiple commands on the same command line. This includes |
137 | pipes, > >> < redirects, << here documents, || && conditional | 135 | pipes, > >> < redirects, << here documents, || && conditional |
138 execution, () subshells, ; sequential execution, and (with job | 136 execution, () subshells, ; sequential execution, and (with job |
139 control) & background processes. | 137 control) & background processes. |
140 | 138 |
141 config SH_BUILTINS | 139 config SH_BUILTINS |
142 bool "Builtin commands" | 140 bool "Builtin commands" |
143 default n | 141 default n |
144 depends on SH | 142 depends on SH |
145 help | 143 help |
146 Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set, | 144 Adds the commands exec, fg, bg, help, jobs, pwd, export, source, set, |
147 unset, read, alias. | 145 unset, read, alias. |
148 | 146 |
149 config EXIT | 147 config EXIT |
150 bool | 148 bool |
151 default n | 149 default n |
152 depends on SH | 150 depends on SH |
153 help | 151 help |
154 usage: exit [status] | 152 usage: exit [status] |
155 | 153 |
156 Exit shell. If no return value supplied on command line, use value | 154 Exit shell. If no return value supplied on command line, use value |
157 of most recent command, or 0 if none. | 155 of most recent command, or 0 if none. |
158 | 156 |
159 config CD | 157 config CD |
160 bool | 158 bool |
161 default n | 159 default n |
162 depends on SH | 160 depends on SH |
163 help | 161 help |
164 usage: cd [path] | 162 usage: cd [path] |
165 | 163 |
166 Change current directory. With no arguments, go to $HOME. | 164 Change current directory. With no arguments, go to $HOME. |
167 | 165 |
168 config CD_P | 166 config CD_P |
169 bool # "-P support for cd" | 167 bool # "-P support for cd" |
170 default n | 168 default n |
171 depends on SH | 169 depends on SH |
172 help | 170 help |
173 usage: cd [-PL] | 171 usage: cd [-PL] |
174 | 172 |
175 -P Physical path: resolve symlinks in path. | 173 -P Physical path: resolve symlinks in path. |
176 -L Cancel previous -P and restore default behavior. | 174 -L Cancel previous -P and restore default behavior. |
177 */ | 175 */ |
178 | 176 |
179 #define FOR_sh | 177 #define FOR_sh |
180 #include "toys.h" | 178 #include "toys.h" |
181 | 179 |
182 GLOBALS( | 180 GLOBALS( |
183 char *command; | 181 char *command; |
184 ) | 182 ) |
185 | 183 |
186 // A single executable, its arguments, and other information we know about it. | 184 // A single executable, its arguments, and other information we know about it. |
187 #define SH_FLAG_EXIT 1 | 185 #define SH_FLAG_EXIT 1 |
188 #define SH_FLAG_SUSPEND 2 | 186 #define SH_FLAG_SUSPEND 2 |
193 #define SH_FLAG_SEMI 64 | 191 #define SH_FLAG_SEMI 64 |
194 #define SH_FLAG_PAREN 128 | 192 #define SH_FLAG_PAREN 128 |
195 | 193 |
196 // What we know about a single process. | 194 // What we know about a single process. |
197 struct command { | 195 struct command { |
198 struct command *next; | 196 struct command *next; |
199 int flags; // exit, suspend, && || | 197 int flags; // exit, suspend, && || |
200 int pid; // pid (or exit code) | 198 int pid; // pid (or exit code) |
201 int argc; | 199 int argc; |
202 char *argv[0]; | 200 char *argv[0]; |
203 }; | 201 }; |
204 | 202 |
205 // A collection of processes piped into/waiting on each other. | 203 // A collection of processes piped into/waiting on each other. |
206 struct pipeline { | 204 struct pipeline { |
207 struct pipeline *next; | 205 struct pipeline *next; |
208 int job_id; | 206 int job_id; |
209 struct command *cmd; | 207 struct command *cmd; |
210 char *cmdline; // Unparsed line for display purposes | 208 char *cmdline; // Unparsed line for display purposes |
211 int cmdlinelen; // How long is cmdline? | 209 int cmdlinelen; // How long is cmdline? |
212 }; | 210 }; |
213 | 211 |
214 // Parse one word from the command line, appending one or more argv[] entries | 212 // Parse one word from the command line, appending one or more argv[] entries |
215 // to struct command. Handles environment variable substitution and | 213 // to struct command. Handles environment variable substitution and |
216 // substrings. Returns pointer to next used byte, or NULL if it | 214 // substrings. Returns pointer to next used byte, or NULL if it |
217 // hit an ending token. | 215 // hit an ending token. |
218 static char *parse_word(char *start, struct command **cmd) | 216 static char *parse_word(char *start, struct command **cmd) |
219 { | 217 { |
220 char *end; | 218 char *end; |
221 | 219 |
222 // Detect end of line (and truncate line at comment) | 220 // Detect end of line (and truncate line at comment) |
223 if (CFG_SH_PIPES && strchr("><&|(;", *start)) return 0; | 221 if (CFG_SH_PIPES && strchr("><&|(;", *start)) return 0; |
224 | 222 |
225 // Grab next word. (Add dequote and envvar logic here) | 223 // Grab next word. (Add dequote and envvar logic here) |
226 end = start; | 224 end = start; |
227 while (*end && !isspace(*end)) end++; | 225 while (*end && !isspace(*end)) end++; |
228 (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start); | 226 (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start); |
229 | 227 |
230 // Allocate more space if there's no room for NULL terminator. | 228 // Allocate more space if there's no room for NULL terminator. |
231 | 229 |
232 if (!((*cmd)->argc & 7)) | 230 if (!((*cmd)->argc & 7)) |
233 *cmd=xrealloc(*cmd, | 231 *cmd=xrealloc(*cmd, |
234 sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *)); | 232 sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *)); |
235 (*cmd)->argv[(*cmd)->argc] = 0; | 233 (*cmd)->argv[(*cmd)->argc] = 0; |
236 return end; | 234 return end; |
237 } | 235 } |
238 | 236 |
239 // Parse a line of text into a pipeline. | 237 // Parse a line of text into a pipeline. |
240 // Returns a pointer to the next line. | 238 // Returns a pointer to the next line. |
241 | 239 |
242 static char *parse_pipeline(char *cmdline, struct pipeline *line) | 240 static char *parse_pipeline(char *cmdline, struct pipeline *line) |
243 { | 241 { |
244 struct command **cmd = &(line->cmd); | 242 struct command **cmd = &(line->cmd); |
245 char *start = line->cmdline = cmdline; | 243 char *start = line->cmdline = cmdline; |
246 | 244 |
247 if (!cmdline) return 0; | 245 if (!cmdline) return 0; |
248 | 246 |
249 if (CFG_SH_JOBCTL) line->cmdline = cmdline; | 247 if (CFG_SH_JOBCTL) line->cmdline = cmdline; |
250 | 248 |
251 // Parse command into argv[] | 249 // Parse command into argv[] |
252 for (;;) { | 250 for (;;) { |
253 char *end; | 251 char *end; |
254 | 252 |
255 // Skip leading whitespace and detect end of line. | 253 // Skip leading whitespace and detect end of line. |
256 while (isspace(*start)) start++; | 254 while (isspace(*start)) start++; |
257 if (!*start || *start=='#') { | 255 if (!*start || *start=='#') { |
258 if (CFG_SH_JOBCTL) line->cmdlinelen = start-cmdline; | 256 if (CFG_SH_JOBCTL) line->cmdlinelen = start-cmdline; |
259 return 0; | 257 return 0; |
260 } | 258 } |
261 | 259 |
262 // Allocate next command structure if necessary | 260 // Allocate next command structure if necessary |
263 if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *)); | 261 if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *)); |
264 | 262 |
265 // Parse next argument and add the results to argv[] | 263 // Parse next argument and add the results to argv[] |
266 end = parse_word(start, cmd); | 264 end = parse_word(start, cmd); |
267 | 265 |
268 // If we hit the end of this command, how did it end? | 266 // If we hit the end of this command, how did it end? |
269 if (!end) { | 267 if (!end) { |
270 if (CFG_SH_PIPES && *start) { | 268 if (CFG_SH_PIPES && *start) { |
271 if (*start==';') { | 269 if (*start==';') { |
272 start++; | 270 start++; |
273 break; | 271 break; |
274 } | 272 } |
275 // handle | & < > >> << || && | 273 // handle | & < > >> << || && |
276 } | 274 } |
277 break; | 275 break; |
278 } | 276 } |
279 start = end; | 277 start = end; |
280 } | 278 } |
281 | 279 |
282 if (CFG_SH_JOBCTL) line->cmdlinelen = start-cmdline; | 280 if (CFG_SH_JOBCTL) line->cmdlinelen = start-cmdline; |
283 | 281 |
284 return start; | 282 return start; |
285 } | 283 } |
286 | 284 |
287 // Execute the commands in a pipeline | 285 // Execute the commands in a pipeline |
288 static void run_pipeline(struct pipeline *line) | 286 static void run_pipeline(struct pipeline *line) |
289 { | 287 { |
290 struct toy_list *tl; | 288 struct toy_list *tl; |
291 struct command *cmd = line->cmd; | 289 struct command *cmd = line->cmd; |
292 if (!cmd || !cmd->argc) return; | 290 if (!cmd || !cmd->argc) return; |
293 | 291 |
294 tl = toy_find(cmd->argv[0]); | 292 tl = toy_find(cmd->argv[0]); |
295 // Is this command a builtin that should run in this process? | 293 // Is this command a builtin that should run in this process? |
296 if (tl && (tl->flags & TOYFLAG_NOFORK)) { | 294 if (tl && (tl->flags & TOYFLAG_NOFORK)) { |
297 struct toy_context temp; | 295 struct toy_context temp; |
298 | 296 |
299 // This fakes lots of what toybox_main() does. | 297 // This fakes lots of what toybox_main() does. |
300 memcpy(&temp, &toys, sizeof(struct toy_context)); | 298 memcpy(&temp, &toys, sizeof(struct toy_context)); |
301 memset(&toys, 0, sizeof(struct toy_context)); | 299 memset(&toys, 0, sizeof(struct toy_context)); |
302 toy_init(tl, cmd->argv); | 300 toy_init(tl, cmd->argv); |
303 tl->toy_main(); | 301 tl->toy_main(); |
304 cmd->pid = toys.exitval; | 302 cmd->pid = toys.exitval; |
305 free(toys.optargs); | 303 free(toys.optargs); |
306 if (toys.old_umask) umask(toys.old_umask); | 304 if (toys.old_umask) umask(toys.old_umask); |
307 memcpy(&toys, &temp, sizeof(struct toy_context)); | 305 memcpy(&toys, &temp, sizeof(struct toy_context)); |
308 } else { | 306 } else { |
309 int status; | 307 int status; |
310 | 308 |
311 cmd->pid = vfork(); | 309 cmd->pid = vfork(); |
312 if (!cmd->pid) xexec(cmd->argv); | 310 if (!cmd->pid) xexec(cmd->argv); |
313 else waitpid(cmd->pid, &status, 0); | 311 else waitpid(cmd->pid, &status, 0); |
314 | 312 |
315 if (CFG_SH_FLOWCTL || CFG_SH_PIPES) { | 313 if (CFG_SH_FLOWCTL || CFG_SH_PIPES) { |
316 if (WIFEXITED(status)) cmd->pid = WEXITSTATUS(status); | 314 if (WIFEXITED(status)) cmd->pid = WEXITSTATUS(status); |
317 if (WIFSIGNALED(status)) cmd->pid = WTERMSIG(status); | 315 if (WIFSIGNALED(status)) cmd->pid = WTERMSIG(status); |
318 } | 316 } |
319 } | 317 } |
320 | 318 |
321 return; | 319 return; |
322 } | 320 } |
323 | 321 |
324 // Free the contents of a command structure | 322 // Free the contents of a command structure |
325 static void free_cmd(void *data) | 323 static void free_cmd(void *data) |
326 { | 324 { |
327 struct command *cmd=(struct command *)data; | 325 struct command *cmd=(struct command *)data; |
328 | 326 |
329 while(cmd->argc) free(cmd->argv[--cmd->argc]); | 327 while(cmd->argc) free(cmd->argv[--cmd->argc]); |
330 } | 328 } |
331 | 329 |
332 | 330 |
333 // Parse a command line and do what it says to do. | 331 // Parse a command line and do what it says to do. |
334 static void handle(char *command) | 332 static void handle(char *command) |
335 { | 333 { |
336 struct pipeline line; | 334 struct pipeline line; |
337 char *start = command; | 335 char *start = command; |
338 | 336 |
339 // Loop through commands in this line | 337 // Loop through commands in this line |
340 | 338 |
341 for (;;) { | 339 for (;;) { |
342 | 340 |
343 // Parse a group of connected commands | 341 // Parse a group of connected commands |
344 | 342 |
345 memset(&line,0,sizeof(struct pipeline)); | 343 memset(&line,0,sizeof(struct pipeline)); |
346 start = parse_pipeline(start, &line); | 344 start = parse_pipeline(start, &line); |
347 if (!line.cmd) break; | 345 if (!line.cmd) break; |
348 | 346 |
349 // Run those commands | 347 // Run those commands |
350 | 348 |
351 run_pipeline(&line); | 349 run_pipeline(&line); |
352 llist_traverse(line.cmd, free_cmd); | 350 llist_traverse(line.cmd, free_cmd); |
353 } | 351 } |
354 } | 352 } |
355 | 353 |
356 void cd_main(void) | 354 void cd_main(void) |
357 { | 355 { |
358 char *dest = *toys.optargs ? *toys.optargs : getenv("HOME"); | 356 char *dest = *toys.optargs ? *toys.optargs : getenv("HOME"); |
359 xchdir(dest); | 357 xchdir(dest); |
360 } | 358 } |
361 | 359 |
362 void exit_main(void) | 360 void exit_main(void) |
363 { | 361 { |
364 exit(*toys.optargs ? atoi(*toys.optargs) : 0); | 362 exit(*toys.optargs ? atoi(*toys.optargs) : 0); |
365 } | 363 } |
366 | 364 |
367 void sh_main(void) | 365 void sh_main(void) |
368 { | 366 { |
369 FILE *f; | 367 FILE *f; |
370 | 368 |
371 // Set up signal handlers and grab control of this tty. | 369 // Set up signal handlers and grab control of this tty. |
372 if (CFG_SH_TTY) { | 370 if (CFG_SH_TTY) { |
373 if (isatty(0)) toys.optflags |= 1; | 371 if (isatty(0)) toys.optflags |= 1; |
374 } | 372 } |
375 f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL; | 373 f = *toys.optargs ? xfopen(*toys.optargs, "r") : NULL; |
376 if (TT.command) handle(TT.command); | 374 if (TT.command) handle(TT.command); |
377 else { | 375 else { |
378 size_t cmdlen = 0; | 376 size_t cmdlen = 0; |
379 for (;;) { | 377 for (;;) { |
380 char *command = 0; | 378 char *command = 0; |
381 if (!f) xputc('$'); | 379 if (!f) xputc('$'); |
382 if (1 > getline(&command, &cmdlen, f ? f : stdin)) break; | 380 if (1 > getline(&command, &cmdlen, f ? f : stdin)) break; |
383 handle(command); | 381 handle(command); |
384 free(command); | 382 free(command); |
385 } | 383 } |
386 } | 384 } |
387 | 385 |
388 toys.exitval = 1; | 386 toys.exitval = 1; |
389 } | 387 } |