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