diff options
| author | Rob Landley <rob@landley.net> | 2006-09-08 16:59:08 +0000 |
|---|---|---|
| committer | Rob Landley <rob@landley.net> | 2006-09-08 16:59:08 +0000 |
| commit | 3476ad651dc023a35abe87a49478d806dee22f97 (patch) | |
| tree | e138505163d99fc9a59176654290f11fabd96e1e /shell | |
| parent | c27f4f5445e046d2c719e8d96ad54cfd7133061e (diff) | |
| download | busybox-w32-3476ad651dc023a35abe87a49478d806dee22f97.tar.gz busybox-w32-3476ad651dc023a35abe87a49478d806dee22f97.tar.bz2 busybox-w32-3476ad651dc023a35abe87a49478d806dee22f97.zip | |
Second drop. More infrastructure in place, especially for parsing pipelines.
The minimal version got a couple hundred bytes bigger, partly because it's
broken into more functions but mostly because it now dynamically reallocates
and frees everything with no hard-coded limits. (I thought about making
that optional, but there's a limit to what I can debug and maintain. It
should still be reasonably NOMMU friendly, the allocations are small and
short lived, and shouldn't contribute noticeably to long-term memory
fragmentation.)
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/bbsh.c | 193 |
1 files changed, 172 insertions, 21 deletions
diff --git a/shell/bbsh.c b/shell/bbsh.c index f2d76cc01..57a103bf8 100644 --- a/shell/bbsh.c +++ b/shell/bbsh.c | |||
| @@ -7,49 +7,197 @@ | |||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
| 8 | */ | 8 | */ |
| 9 | 9 | ||
| 10 | // Handle embedded NUL bytes in the command line. | 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 | */ | ||
| 11 | 38 | ||
| 12 | #include <busybox.h> | 39 | #include <busybox.h> |
| 13 | 40 | ||
| 14 | static int handle(char *command) | 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; | ||
| 54 | int flags; // exit, suspend, && || | ||
| 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)) | ||
| 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. | ||
| 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; | ||
| 93 | while (*end && !isspace(*end)) 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 | |||
| 108 | static char *parse_pipeline(char *cmdline, struct pipeline *pipe) | ||
| 15 | { | 109 | { |
| 16 | int argc=0; | 110 | struct command **cmd = &(pipe->cmd); |
| 17 | char *argv[10], *start = command; | 111 | char *start = pipe->cmdline = cmdline; |
| 112 | |||
| 113 | if (!cmdline) return 0; | ||
| 114 | |||
| 115 | if (ENABLE_BBSH_JOBCTL) pipe->cmdline = cmdline; | ||
| 18 | 116 | ||
| 19 | // Parse command into argv[] | 117 | // Parse command into argv[] |
| 20 | for (;;) { | 118 | for (;;) { |
| 21 | char *end; | 119 | char *end; |
| 22 | 120 | ||
| 23 | // Skip leading whitespace and detect EOL. | 121 | // Skip leading whitespace and detect end of line. |
| 24 | while(isspace(*start)) start++; | 122 | while (isspace(*start)) start++; |
| 25 | if(!*start || *start=='#') break; | 123 | if (!*start || *start=='#') { |
| 124 | if (ENABLE_BBSH_JOBCTL) pipe->cmdlinelen = start-cmdline; | ||
| 125 | return 0; | ||
| 126 | } | ||
| 26 | 127 | ||
| 27 | // Grab next word. (Add dequote and envvar logic here) | 128 | // Allocate next command structure if necessary |
| 28 | end=start; | 129 | if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *)); |
| 29 | while(*end && !isspace(*end)) end++; | 130 | |
| 30 | argv[argc++]=xstrndup(start,end-start); | 131 | // Parse next argument and add the results to argv[] |
| 31 | start=end; | 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; | ||
| 32 | } | 146 | } |
| 33 | argv[argc]=0; | ||
| 34 | 147 | ||
| 35 | if (!argc) return 0; | 148 | if (ENABLE_BBSH_JOBCTL) pipe->cmdlinelen = start-cmdline; |
| 36 | if (argc==2 && !strcmp(argv[0],"cd")) chdir(argv[1]); | 149 | |
| 37 | else if(!strcmp(argv[0],"exit")) exit(argc>1 ? atoi(argv[1]) : 0); | 150 | return start; |
| 151 | } | ||
| 152 | |||
| 153 | // Execute the commands in a pipeline | ||
| 154 | static int run_pipeline(struct pipeline *pipe) | ||
| 155 | { | ||
| 156 | struct command *cmd = pipe->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); | ||
| 38 | else { | 164 | else { |
| 39 | int status; | 165 | int status; |
| 40 | pid_t pid=fork(); | 166 | pid_t pid=fork(); |
| 41 | if(!pid) { | 167 | if(!pid) { |
| 42 | run_applet_by_name(argv[0],argc,argv); | 168 | run_applet_by_name(cmd->argv[0],cmd->argc,cmd->argv); |
| 43 | execvp(argv[0],argv); | 169 | execvp(cmd->argv[0],cmd->argv); |
| 44 | printf("No %s",argv[0]); | 170 | printf("No %s",cmd->argv[0]); |
| 45 | exit(1); | 171 | exit(1); |
| 46 | } else waitpid(pid, &status, 0); | 172 | } else waitpid(pid, &status, 0); |
| 47 | } | 173 | } |
| 48 | while(argc) free(argv[--argc]); | ||
| 49 | 174 | ||
| 50 | return 0; | 175 | return 0; |
| 51 | } | 176 | } |
| 52 | 177 | ||
| 178 | static 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 | |||
| 186 | static void handle(char *command) | ||
| 187 | { | ||
| 188 | struct pipeline pipe; | ||
| 189 | char *start = command; | ||
| 190 | |||
| 191 | for (;;) { | ||
| 192 | memset(&pipe,0,sizeof(struct pipeline)); | ||
| 193 | start = parse_pipeline(start, &pipe); | ||
| 194 | if (!pipe.cmd) break; | ||
| 195 | |||
| 196 | run_pipeline(&pipe); | ||
| 197 | free_list(pipe.cmd, free_cmd); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 53 | int bbsh_main(int argc, char *argv[]) | 201 | int bbsh_main(int argc, char *argv[]) |
| 54 | { | 202 | { |
| 55 | char *command=NULL; | 203 | char *command=NULL; |
| @@ -62,8 +210,11 @@ int bbsh_main(int argc, char *argv[]) | |||
| 62 | else { | 210 | else { |
| 63 | unsigned cmdlen=0; | 211 | unsigned cmdlen=0; |
| 64 | for (;;) { | 212 | for (;;) { |
| 213 | struct pipeline pipe; | ||
| 214 | |||
| 65 | if(!f) putchar('$'); | 215 | if(!f) putchar('$'); |
| 66 | if(1 > getline(&command,&cmdlen,f ? : stdin)) break; | 216 | if(1 > getline(&command, &cmdlen,f ? : stdin)) break; |
| 217 | |||
| 67 | handle(command); | 218 | handle(command); |
| 68 | } | 219 | } |
| 69 | if (ENABLE_FEATURE_CLEAN_UP) free(command); | 220 | if (ENABLE_FEATURE_CLEAN_UP) free(command); |
