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