Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 1 | /* vi: set ts=4 : |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 2 | * |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 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 | |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 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 | */ |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 38 | |
Denis Vlasenko | b6adbf1 | 2007-05-26 19:00:18 +0000 | [diff] [blame] | 39 | #include "libbb.h" |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 40 | |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 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. |
| 52 | struct command { |
| 53 | struct command *next; |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 54 | int flags; // exit, suspend, && || |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 55 | int pid; // pid (or exit code) |
| 56 | int argc; |
| 57 | char *argv[0]; |
| 58 | }; |
| 59 | |
| 60 | // A collection of processes piped into/waiting on each other. |
| 61 | struct pipeline { |
| 62 | struct pipeline *next; |
| 63 | int job_id; |
| 64 | struct command *cmd; |
| 65 | char *cmdline; |
| 66 | int cmdlinelen; |
| 67 | }; |
| 68 | |
| 69 | static void free_list(void *list, void (*freeit)(void *data)) |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 70 | { |
Denis Vlasenko | 51742f4 | 2007-04-12 00:32:05 +0000 | [diff] [blame] | 71 | while (list) { |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 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. |
| 84 | static 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; |
Bernhard Reutner-Fischer | de17ece | 2007-04-10 09:38:35 +0000 | [diff] [blame] | 93 | end = skip_non_whitespace(end); |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 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 | |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 108 | static char *parse_pipeline(char *cmdline, struct pipeline *line) |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 109 | { |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 110 | struct command **cmd = &(line->cmd); |
| 111 | char *start = line->cmdline = cmdline; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 112 | |
| 113 | if (!cmdline) return 0; |
| 114 | |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 115 | if (ENABLE_BBSH_JOBCTL) line->cmdline = cmdline; |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 116 | |
| 117 | // Parse command into argv[] |
| 118 | for (;;) { |
| 119 | char *end; |
| 120 | |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 121 | // Skip leading whitespace and detect end of line. |
Denis Vlasenko | d18a3a2 | 2006-10-25 12:46:03 +0000 | [diff] [blame] | 122 | start = skip_whitespace(start); |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 123 | if (!*start || *start=='#') { |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 124 | if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 125 | return 0; |
| 126 | } |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 127 | |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 128 | // Allocate next command structure if necessary |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 129 | if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *)); |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 130 | |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 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 | } |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 141 | // handle | & < > >> << || && |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 142 | } |
| 143 | break; |
| 144 | } |
| 145 | start = end; |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 146 | } |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 147 | |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 148 | if (ENABLE_BBSH_JOBCTL) line->cmdlinelen = start-cmdline; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 149 | |
| 150 | return start; |
| 151 | } |
| 152 | |
| 153 | // Execute the commands in a pipeline |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 154 | static int run_pipeline(struct pipeline *line) |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 155 | { |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 156 | struct command *cmd = line->cmd; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 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]); |
Denis Vlasenko | 51742f4 | 2007-04-12 00:32:05 +0000 | [diff] [blame] | 162 | else if (!strcmp(cmd->argv[0],"exit")) |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 163 | exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0); |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 164 | else { |
| 165 | int status; |
| 166 | pid_t pid=fork(); |
Denis Vlasenko | 51742f4 | 2007-04-12 00:32:05 +0000 | [diff] [blame] | 167 | if (!pid) { |
Denis Vlasenko | e4f2d06 | 2007-04-11 17:03:19 +0000 | [diff] [blame] | 168 | run_applet_and_exit(cmd->argv[0],cmd->argc,cmd->argv); |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 169 | execvp(cmd->argv[0],cmd->argv); |
Denis Vlasenko | f5d8c90 | 2008-06-26 14:32:57 +0000 | [diff] [blame] | 170 | printf("No %s", cmd->argv[0]); |
Bernhard Reutner-Fischer | 636a1f8 | 2008-05-19 09:29:47 +0000 | [diff] [blame] | 171 | exit(EXIT_FAILURE); |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 172 | } else waitpid(pid, &status, 0); |
| 173 | } |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 174 | |
| 175 | return 0; |
| 176 | } |
| 177 | |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 178 | static void free_cmd(void *data) |
| 179 | { |
| 180 | struct command *cmd=(struct command *)data; |
| 181 | |
Denis Vlasenko | 51742f4 | 2007-04-12 00:32:05 +0000 | [diff] [blame] | 182 | while (cmd->argc) free(cmd->argv[--cmd->argc]); |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | |
| 186 | static void handle(char *command) |
| 187 | { |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 188 | struct pipeline line; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 189 | char *start = command; |
| 190 | |
| 191 | for (;;) { |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 192 | memset(&line,0,sizeof(struct pipeline)); |
| 193 | start = parse_pipeline(start, &line); |
| 194 | if (!line.cmd) break; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 195 | |
Rob Landley | ef08184 | 2006-09-08 17:21:19 +0000 | [diff] [blame] | 196 | run_pipeline(&line); |
| 197 | free_list(line.cmd, free_cmd); |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 198 | } |
| 199 | } |
| 200 | |
Denis Vlasenko | 9b49a5e | 2007-10-11 10:05:36 +0000 | [diff] [blame] | 201 | int bbsh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
Bernhard Reutner-Fischer | febe3c4 | 2007-04-04 20:52:03 +0000 | [diff] [blame] | 202 | int bbsh_main(int argc, char **argv) |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 203 | { |
| 204 | char *command=NULL; |
| 205 | FILE *f; |
| 206 | |
Denis Vlasenko | fe7cd64 | 2007-08-18 15:32:12 +0000 | [diff] [blame] | 207 | getopt32(argv, "c:", &command); |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 208 | |
| 209 | f = argv[optind] ? xfopen(argv[optind],"r") : NULL; |
| 210 | if (command) handle(command); |
| 211 | else { |
| 212 | unsigned cmdlen=0; |
| 213 | for (;;) { |
Denis Vlasenko | 51742f4 | 2007-04-12 00:32:05 +0000 | [diff] [blame] | 214 | if (!f) putchar('$'); |
| 215 | if (1 > getline(&command, &cmdlen,f ? : stdin)) break; |
Rob Landley | 3476ad6 | 2006-09-08 16:59:08 +0000 | [diff] [blame] | 216 | |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 217 | handle(command); |
| 218 | } |
| 219 | if (ENABLE_FEATURE_CLEAN_UP) free(command); |
| 220 | } |
Denis Vlasenko | f7996f3 | 2007-01-11 17:20:00 +0000 | [diff] [blame] | 221 | |
Rob Landley | 02add9e | 2006-09-05 03:22:19 +0000 | [diff] [blame] | 222 | return 1; |
| 223 | } |