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 maki
This commit was genuinely broken at the time (not related to our build system): scripts/config/mkconfigs.c (2005-08-01 - 2006-10-05 arasi) uretilen bbconfigopts.h icine .config satirlarini kacissiz, ham newline'larla gömüyordu - gecerli C degildi, busybox'un kendi bb_mkdep araci bile parse edemiyordu (7d219aab7 ile duzeltildi, mkconfigs shell scripte cevrildi)
No binary (build failed)
—
(log yok)
| @@ -7,49 +7,197 @@ | |
| * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | |
| */ | |
| - | // Handle embedded NUL bytes in the command line. |
| + | // A section of code that gets repeatedly or conditionally executed is stored |
| + | // as a string and parsed each time it's run. |
| + | |
| + | |
| + | |
| + | // Wheee, debugging. |
| + | |
| + | // Terminal control |
| + | #define ENABLE_BBSH_TTY 0 |
| + | |
| + | // &, fg, bg, jobs. (ctrl-z with tty.) |
| + | #define ENABLE_BBSH_JOBCTL 0 |
| + | |
| + | // Flow control (if, while, for, functions { }) |
| + | #define ENABLE_BBSH_FLOWCTL 0 |
| + | |
| + | #define ENABLE_BBSH_ENVVARS 0 // Environment variable support |
| + | |
| + | // Local and synthetic variables, fancy prompts, set, $?, etc. |
| + | #define ENABLE_BBSH_LOCALVARS 0 |
| + | |
| + | // Pipes and redirects: | > < >> << && || & () ; |
| + | #define ENABLE_BBSH_PIPES 0 |
| + | |
| + | /* Fun: |
| + | |
| + | echo `echo hello#comment " woot` and more |
| + | */ |
| #include <busybox.h> | |
| - | static int handle(char *command) |
| + | // A single executable, its arguments, and other information we know about it. |
| + | #define BBSH_FLAG_EXIT 1 |
| + | #define BBSH_FLAG_SUSPEND 2 |
| + | #define BBSH_FLAG_PIPE 4 |
| + | #define BBSH_FLAG_AND 8 |
| + | #define BBSH_FLAG_OR 16 |
| + | #define BBSH_FLAG_AMP 32 |
| + | #define BBSH_FLAG_SEMI 64 |
| + | #define BBSH_FLAG_PAREN 128 |
| + | |
| + | // What we know about a single process. |
| + | struct command { |
| + | struct command *next; |
| + | int flags; // exit, suspend, && || |
| + | int pid; // pid (or exit code) |
| + | int argc; |
| + | char *argv[0]; |
| + | }; |
| + | |
| + | // A collection of processes piped into/waiting on each other. |
| + | struct pipeline { |
| + | struct pipeline *next; |
| + | int job_id; |
| + | struct command *cmd; |
| + | char *cmdline; |
| + | int cmdlinelen; |
| + | }; |
| + | |
| + | static void free_list(void *list, void (*freeit)(void *data)) |
| + | { |
| + | while(list) { |
| + | void **next = (void **)list; |
| + | void *list_next = *next; |
| + | freeit(list); |
| + | free(list); |
| + | list = list_next; |
| + | } |
| + | } |
| + | |
| + | // Parse one word from the command line, appending one or more argv[] entries |
| + | // to struct command. Handles environment variable substitution and |
| + | // substrings. Returns pointer to next used byte, or NULL if it |
| + | // hit an ending token. |
| + | static char *parse_word(char *start, struct command **cmd) |
| + | { |
| + | char *end; |
| + | |
| + | // Detect end of line (and truncate line at comment) |
| + | if (ENABLE_BBSH_PIPES && strchr("><&|(;", *start)) return 0; |
| + | |
| + | // Grab next word. (Add dequote and envvar logic here) |
| + | end = start; |
| + | while (*end && !isspace(*end)) end++; |
| + | (*cmd)->argv[(*cmd)->argc++] = xstrndup(start, end-start); |
| + | |
| + | // Allocate more space if there's no room for NULL terminator. |
| + | |
| + | if (!((*cmd)->argc & 7)) |
| + | *cmd = xrealloc(*cmd, |
| + | sizeof(struct command) + ((*cmd)->argc+8)*sizeof(char *)); |
| + | (*cmd)->argv[(*cmd)->argc] = 0; |
| + | return end; |
| + | } |
| + | |
| + | // Parse a line of text into a pipeline. |
| + | // Returns a pointer to the next line. |
| + | |
| + | static char *parse_pipeline(char *cmdline, struct pipeline *pipe) |
| { | |
| - | int argc=0; |
| - | char *argv[10], *start = command; |
| + | struct command **cmd = &(pipe->cmd); |
| + | char *start = pipe->cmdline = cmdline; |
| + | |
| + | if (!cmdline) return 0; |
| + | |
| + | if (ENABLE_BBSH_JOBCTL) pipe->cmdline = cmdline; |
| // Parse command into argv[] | |
| for (;;) { | |
| char *end; | |
| - | // Skip leading whitespace and detect EOL. |
| - | while(isspace(*start)) start++; |
| - | if(!*start || *start=='#') break; |
| + | // Skip leading whitespace and detect end of line. |
| + | while (isspace(*start)) start++; |
| + | if (!*start || *start=='#') { |
| + | if (ENABLE_BBSH_JOBCTL) pipe->cmdlinelen = start-cmdline; |
| + | return 0; |
| + | } |
| - | // Grab next word. (Add dequote and envvar logic here) |
| - | end=start; |
| - | while(*end && !isspace(*end)) end++; |
| - | argv[argc++]=xstrndup(start,end-start); |
| - | start=end; |
| + | // Allocate next command structure if necessary |
| + | if (!*cmd) *cmd = xzalloc(sizeof(struct command)+8*sizeof(char *)); |
| + | |
| + | // Parse next argument and add the results to argv[] |
| + | end = parse_word(start, cmd); |
| + | |
| + | // If we hit the end of this command, how did it end? |
| + | if (!end) { |
| + | if (ENABLE_BBSH_PIPES && *start) { |
| + | if (*start==';') { |
| + | start++; |
| + | break; |
| + | } |
| + | // handle | & < > >> << || && |
| + | } |
| + | break; |
| + | } |
| + | start = end; |
| } | |
| - | argv[argc]=0; |
| - | if (!argc) return 0; |
| - | if (argc==2 && !strcmp(argv[0],"cd")) chdir(argv[1]); |
| - | else if(!strcmp(argv[0],"exit")) exit(argc>1 ? atoi(argv[1]) : 0); |
| + | if (ENABLE_BBSH_JOBCTL) pipe->cmdlinelen = start-cmdline; |
| + | |
| + | return start; |
| + | } |
| + | |
| + | // Execute the commands in a pipeline |
| + | static int run_pipeline(struct pipeline *pipe) |
| + | { |
| + | struct command *cmd = pipe->cmd; |
| + | if (!cmd || !cmd->argc) return 0; |
| + | |
| + | // Handle local commands. This is totally fake and plastic. |
| + | if (cmd->argc==2 && !strcmp(cmd->argv[0],"cd")) |
| + | chdir(cmd->argv[1]); |
| + | else if(!strcmp(cmd->argv[0],"exit")) |
| + | exit(cmd->argc>1 ? atoi(cmd->argv[1]) : 0); |
| else { | |
| int status; | |
| pid_t pid=fork(); | |
| if(!pid) { | |
| - | run_applet_by_name(argv[0],argc,argv); |
| - | execvp(argv[0],argv); |
| - | printf("No %s",argv[0]); |
| + | run_applet_by_name(cmd->argv[0],cmd->argc,cmd->argv); |
| + | execvp(cmd->argv[0],cmd->argv); |
| + | printf("No %s",cmd->argv[0]); |
| exit(1); | |
| } else waitpid(pid, &status, 0); | |
| } | |
| - | while(argc) free(argv[--argc]); |
| return 0; | |
| } | |
| + | static void free_cmd(void *data) |
| + | { |
| + | struct command *cmd=(struct command *)data; |
| + | |
| + | while(cmd->argc) free(cmd->argv[--cmd->argc]); |
| + | } |
| + | |
| + | |
| + | static void handle(char *command) |
| + | { |
| + | struct pipeline pipe; |
| + | char *start = command; |
| + | |
| + | for (;;) { |
| + | memset(&pipe,0,sizeof(struct pipeline)); |
| + | start = parse_pipeline(start, &pipe); |
| + | if (!pipe.cmd) break; |
| + | |
| + | run_pipeline(&pipe); |
| + | free_list(pipe.cmd, free_cmd); |
| + | } |
| + | } |
| + | |
| int bbsh_main(int argc, char *argv[]) | |
| { | |
| char *command=NULL; | |
| @@ -62,8 +210,11 @@ int bbsh_main(int argc, char *argv[]) | |
| else { | |
| unsigned cmdlen=0; | |
| for (;;) { | |
| + | struct pipeline pipe; |
| + | |
| if(!f) putchar('$'); | |
| - | if(1 > getline(&command,&cmdlen,f ? : stdin)) break; |
| + | if(1 > getline(&command, &cmdlen,f ? : stdin)) break; |
| + | |
| handle(command); | |
| } | |
| if (ENABLE_FEATURE_CLEAN_UP) free(command); | |