Contents of /tags/mkinitrd-6_3_4/busybox/shell/bbsh.c
Parent Directory | Revision Log
Revision 1189 -
(show annotations)
(download)
Thu Dec 16 01:27:43 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 5150 byte(s)
Thu Dec 16 01:27:43 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 5150 byte(s)
tagged 'mkinitrd-6_3_4'
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. |
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[]; |
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 | 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 | |
108 | static 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 |
154 | static 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 | |
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 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 | |
201 | int bbsh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
202 | int 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 | } |