1/* vi: set ts=4 : 2 * 3 * bbsh - busybox shell 4 * 5 * Copyright 2006 Rob Landley <rob@landley.net> 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 */ 9 10// A section of code that gets repeatedly or conditionally executed is stored 11// as a string and parsed each time it's run. 12 13 14 15// Wheee, debugging. 16 17// Terminal control 18#define ENABLE_BBSH_TTY 0 19 20// &, fg, bg, jobs. (ctrl-z with tty.) 21#define ENABLE_BBSH_JOBCTL 0 22 23// Flow control (if, while, for, functions { }) 24#define ENABLE_BBSH_FLOWCTL 0 25 26#define ENABLE_BBSH_ENVVARS 0 // Environment variable support 27 28// Local and synthetic variables, fancy prompts, set, $?, etc. 29#define ENABLE_BBSH_LOCALVARS 0 30 31// Pipes and redirects: | > < >> << && || & () ; 32#define ENABLE_BBSH_PIPES 0 33 34/* Fun: 35 36 echo `echo hello#comment " woot` and more 37*/ 38 39#include "libbb.h" 40 41// A single executable, its arguments, and other information we know about it. 42#define BBSH_FLAG_EXIT 1 43#define BBSH_FLAG_SUSPEND 2 44#define BBSH_FLAG_PIPE 4 45#define BBSH_FLAG_AND 8 46#define BBSH_FLAG_OR 16 47#define BBSH_FLAG_AMP 32 48#define BBSH_FLAG_SEMI 64 49#define BBSH_FLAG_PAREN 128 50 51// What we know about a single process. 52struct command { 53 struct command *next; 54 int flags; // exit, suspend, && || 55 int pid; // pid (or exit code) 56 int argc; 57 char *argv[]; 58}; 59 60// A collection of processes piped into/waiting on each other. 61struct pipeline { 62 struct pipeline *next; 63 int job_id; 64 struct command *cmd; 65 char *cmdline; 66 int cmdlinelen; 67}; 68 69static void free_list(void *list, void (*freeit)(void *data)) 70{ 71 while (list) { 72 void **next = (void **)list; 73 void *list_next = *next; 74 freeit(list); 75 free(list); 76 list = list_next; 77 } 78} 79 80// Parse one word from the command line, appending one or more argv[] entries 81// to struct command. Handles environment variable substitution and 82// substrings. Returns pointer to next used byte, or NULL if it 83// hit an ending token. 84static char *parse_word(char *start, struct command **cmd) 85{ 86 char *end; 87 88 // Detect end of line (and truncate line at comment) 89 if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0; 90 91 // Grab next word. (Add dequote and envvar logic here) 92 end = start; 93 end = skip_non_whitespace(end); 94 (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start); 95 96 // Allocate more space if there's no room for NULL terminator. 97 98 if (!((*cmd)->argc & 7)) 99 *cmd = xrealloc(*cmd, 100 sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *)); 101 (*cmd)->argv[(*cmd)->argc] = 0; 102 return end; 103} 104 105// Parse a line of text into a pipeline. 106// Returns a pointer to the next line. 107 108static char *parse_pipeline(char *cmdline, struct pipeline *line) 109{ 110 struct command **cmd = &(line->cmd); 111 char *start = line->cmdline = cmdline; 112 113 if (!cmdline) return 0; 114 115 if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline; 116 117 // Parse command into argv[] 118 for (;;) { 119 char *end; 120 121 // Skip leading whitespace and detect end of line. 122 start = skip_whitespace(start); 123 if (!*start || *start=='#') { 124 if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline; 125 return 0; 126 } 127 128 // Allocate next command structure if necessary 129 if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *)); 130 131 // Parse next argument and add the results to argv[] 132 end = parse_word(start, cmd); 133 134 // If we hit the end of this command, how did it end? 135 if (!end) { 136 if (ENABLE_BBSH_PIPES && *start) { 137 if (*start==';') { 138 start++; 139 break; 140 } 141 // handle | & < > >> << || && 142 } 143 break; 144 } 145 start = end; 146 } 147 148 if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline; 149 150 return start; 151} 152 153// Execute the commands in a pipeline 154static int run_pipeline(struct pipeline *line) 155{ 156 struct command *cmd = line->cmd; 157 if (!cmd || !cmd->argc) return 0; 158 159 // Handle local commands. This is totally fake and plastic. 160 if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd")) 161 chdir(cmd->argv[1]); 162 else if (!strcmp(cmd->argv[0],"exit")) 163 exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0); 164 else { 165 int status; 166 pid_t pid=fork(); 167 if (!pid) { 168 run_applet_and_exit(cmd->argv[0],cmd->argc,cmd->argv); 169 execvp(cmd->argv[0],cmd->argv); 170 printf("No %s", cmd->argv[0]); 171 exit(EXIT_FAILURE); 172 } else waitpid(pid, &status, 0); 173 } 174 175 return 0; 176} 177 178static void free_cmd(void *data) 179{ 180 struct command *cmd=(struct command *)data; 181 182 while (cmd->argc) free(cmd->argv[--cmd->argc]); 183} 184 185 186static void handle(char *command) 187{ 188 struct pipeline line; 189 char *start = command; 190 191 for (;;) { 192 memset(&line,0,sizeof(struct pipeline)); 193 start = parse_pipeline(start, &line); 194 if (!line.cmd) break; 195 196 run_pipeline(&line); 197 free_list(line.cmd, free_cmd); 198 } 199} 200 201int bbsh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 202int bbsh_main(int argc UNUSED_PARAM, char **argv) 203{ 204 char *command=NULL; 205 FILE *f; 206 207 getopt32(argv, "c:", &command); 208 209 f = argv[optind] ? xfopen_for_read(argv[optind]) : NULL; 210 if (command) handle(command); 211 else { 212 unsigned cmdlen=0; 213 for (;;) { 214 if (!f) putchar('$'); 215 if (1 > getline(&command, &cmdlen, f ? f : stdin)) break; 216 217 handle(command); 218 } 219 if (ENABLE_FEATURE_CLEAN_UP) free(command); 220 } 221 222 return 1; 223} 224