diff options
author | Erik Andersen <andersen@codepoet.org> | 2000-03-16 08:12:48 +0000 |
---|---|---|
committer | Erik Andersen <andersen@codepoet.org> | 2000-03-16 08:12:48 +0000 |
commit | 161220c4985b8c05a57f09b2693a6cad74d2e81d (patch) | |
tree | 55395efd08b325dd3fe7a4e27d988967bffbbe70 /sh.c | |
parent | 13456d1fcd0122d8464c3c3e1c356d86a56e6c08 (diff) | |
download | busybox-w32-161220c4985b8c05a57f09b2693a6cad74d2e81d.tar.gz busybox-w32-161220c4985b8c05a57f09b2693a6cad74d2e81d.tar.bz2 busybox-w32-161220c4985b8c05a57f09b2693a6cad74d2e81d.zip |
Fix fg bug
-Erik
Diffstat (limited to 'sh.c')
-rw-r--r-- | sh.c | 1346 |
1 files changed, 676 insertions, 670 deletions
@@ -47,46 +47,47 @@ | |||
47 | 47 | ||
48 | 48 | ||
49 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, | 49 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, |
50 | REDIRECT_APPEND }; | 50 | REDIRECT_APPEND |
51 | }; | ||
51 | 52 | ||
52 | struct jobSet { | 53 | struct jobSet { |
53 | struct job *head; /* head of list of running jobs */ | 54 | struct job *head; /* head of list of running jobs */ |
54 | struct job *fg; /* current foreground job */ | 55 | struct job *fg; /* current foreground job */ |
55 | }; | 56 | }; |
56 | 57 | ||
57 | struct redirectionSpecifier { | 58 | struct redirectionSpecifier { |
58 | enum redirectionType type; /* type of redirection */ | 59 | enum redirectionType type; /* type of redirection */ |
59 | int fd; /* file descriptor being redirected */ | 60 | int fd; /* file descriptor being redirected */ |
60 | char *filename; /* file to redirect fd to */ | 61 | char *filename; /* file to redirect fd to */ |
61 | }; | 62 | }; |
62 | 63 | ||
63 | struct childProgram { | 64 | struct childProgram { |
64 | pid_t pid; /* 0 if exited */ | 65 | pid_t pid; /* 0 if exited */ |
65 | char **argv; /* program name and arguments */ | 66 | char **argv; /* program name and arguments */ |
66 | int numRedirections; /* elements in redirection array */ | 67 | int numRedirections; /* elements in redirection array */ |
67 | struct redirectionSpecifier *redirections; /* I/O redirections */ | 68 | struct redirectionSpecifier *redirections; /* I/O redirections */ |
68 | glob_t globResult; /* result of parameter globbing */ | 69 | glob_t globResult; /* result of parameter globbing */ |
69 | int freeGlob; /* should we globfree(&globResult)? */ | 70 | int freeGlob; /* should we globfree(&globResult)? */ |
70 | int isStopped; /* is the program currently running? */ | 71 | int isStopped; /* is the program currently running? */ |
71 | }; | 72 | }; |
72 | 73 | ||
73 | struct job { | 74 | struct job { |
74 | int jobId; /* job number */ | 75 | int jobId; /* job number */ |
75 | int numProgs; /* total number of programs in job */ | 76 | int numProgs; /* total number of programs in job */ |
76 | int runningProgs; /* number of programs running */ | 77 | int runningProgs; /* number of programs running */ |
77 | char *text; /* name of job */ | 78 | char *text; /* name of job */ |
78 | char *cmdBuf; /* buffer various argv's point into */ | 79 | char *cmdBuf; /* buffer various argv's point into */ |
79 | pid_t pgrp; /* process group ID for the job */ | 80 | pid_t pgrp; /* process group ID for the job */ |
80 | struct childProgram *progs; /* array of programs in job */ | 81 | struct childProgram *progs; /* array of programs in job */ |
81 | struct job *next; /* to track background commands */ | 82 | struct job *next; /* to track background commands */ |
82 | int stoppedProgs; /* number of programs alive, but stopped */ | 83 | int stoppedProgs; /* number of programs alive, but stopped */ |
83 | }; | 84 | }; |
84 | 85 | ||
85 | struct builtInCommand { | 86 | struct builtInCommand { |
86 | char *cmd; /* name */ | 87 | char *cmd; /* name */ |
87 | char *descr; /* description */ | 88 | char *descr; /* description */ |
88 | char *usage; /* usage */ | 89 | char *usage; /* usage */ |
89 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ | 90 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ |
90 | }; | 91 | }; |
91 | 92 | ||
92 | /* Some function prototypes */ | 93 | /* Some function prototypes */ |
@@ -111,24 +112,26 @@ static int busy_loop(FILE * input); | |||
111 | 112 | ||
112 | /* Table of built-in functions */ | 113 | /* Table of built-in functions */ |
113 | static struct builtInCommand bltins[] = { | 114 | static struct builtInCommand bltins[] = { |
114 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, | 115 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, |
115 | {"cd", "Change working directory", "cd [dir]", shell_cd}, | 116 | {"cd", "Change working directory", "cd [dir]", shell_cd}, |
116 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, | 117 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, |
117 | {"env", "Print all environment variables", "env", shell_env}, | 118 | {"env", "Print all environment variables", "env", shell_env}, |
118 | {"exit", "Exit from shell()", "exit", shell_exit}, | 119 | {"exit", "Exit from shell()", "exit", shell_exit}, |
119 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, | 120 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, |
120 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, | 121 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, |
121 | {"pwd", "Print current directory", "pwd", shell_pwd}, | 122 | {"pwd", "Print current directory", "pwd", shell_pwd}, |
122 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, | 123 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, |
123 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | 124 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, |
124 | {".", "Source-in and run commands in a file", ". filename", | 125 | |
125 | shell_source}, | 126 | {".", "Source-in and run commands in a file", ". filename", |
126 | {"help", "List shell built-in commands", "help", shell_help}, | 127 | shell_source}, |
127 | {NULL, NULL, NULL, NULL} | 128 | {"help", "List shell built-in commands", "help", shell_help}, |
129 | {NULL, NULL, NULL, NULL} | ||
128 | }; | 130 | }; |
129 | 131 | ||
130 | static const char shell_usage[] = | 132 | static const char shell_usage[] = |
131 | "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n"; | 133 | |
134 | "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n"; | ||
132 | 135 | ||
133 | 136 | ||
134 | static char cwd[1024]; | 137 | static char cwd[1024]; |
@@ -139,323 +142,326 @@ static char *prompt = "# "; | |||
139 | /* built-in 'cd <path>' handler */ | 142 | /* built-in 'cd <path>' handler */ |
140 | static int shell_cd(struct job *cmd, struct jobSet *junk) | 143 | static int shell_cd(struct job *cmd, struct jobSet *junk) |
141 | { | 144 | { |
142 | char *newdir; | 145 | char *newdir; |
143 | if (!cmd->progs[0].argv[1] == 1) | 146 | |
144 | newdir = getenv("HOME"); | 147 | if (!cmd->progs[0].argv[1] == 1) |
145 | else | 148 | newdir = getenv("HOME"); |
146 | newdir = cmd->progs[0].argv[1]; | 149 | else |
147 | if (chdir(newdir)) { | 150 | newdir = cmd->progs[0].argv[1]; |
148 | printf("cd: %s: %s\n", newdir, strerror(errno)); | 151 | if (chdir(newdir)) { |
149 | return FALSE; | 152 | printf("cd: %s: %s\n", newdir, strerror(errno)); |
150 | } | 153 | return FALSE; |
151 | getcwd(cwd, sizeof(cwd)); | 154 | } |
152 | 155 | getcwd(cwd, sizeof(cwd)); | |
153 | return TRUE; | 156 | |
157 | return TRUE; | ||
154 | } | 158 | } |
155 | 159 | ||
156 | /* built-in 'env' handler */ | 160 | /* built-in 'env' handler */ |
157 | static int shell_env(struct job *dummy, struct jobSet *junk) | 161 | static int shell_env(struct job *dummy, struct jobSet *junk) |
158 | { | 162 | { |
159 | char **e; | 163 | char **e; |
160 | 164 | ||
161 | for (e = environ; *e; e++) { | 165 | for (e = environ; *e; e++) { |
162 | fprintf(stdout, "%s\n", *e); | 166 | fprintf(stdout, "%s\n", *e); |
163 | } | 167 | } |
164 | return (0); | 168 | return (0); |
165 | } | 169 | } |
166 | 170 | ||
167 | /* built-in 'exit' handler */ | 171 | /* built-in 'exit' handler */ |
168 | static int shell_exit(struct job *cmd, struct jobSet *junk) | 172 | static int shell_exit(struct job *cmd, struct jobSet *junk) |
169 | { | 173 | { |
170 | if (!cmd->progs[0].argv[1] == 1) | 174 | if (!cmd->progs[0].argv[1] == 1) |
171 | exit TRUE; | 175 | exit TRUE; |
172 | else | 176 | |
173 | exit(atoi(cmd->progs[0].argv[1])); | 177 | else |
178 | exit(atoi(cmd->progs[0].argv[1])); | ||
174 | } | 179 | } |
175 | 180 | ||
176 | /* built-in 'fg' and 'bg' handler */ | 181 | /* built-in 'fg' and 'bg' handler */ |
177 | static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) | 182 | static int shell_fg_bg(struct job *cmd, struct jobSet *jobList) |
178 | { | 183 | { |
179 | int i, jobNum; | 184 | int i, jobNum; |
180 | struct job *job; | 185 | struct job *job; |
181 | 186 | ||
182 | if (!jobList->head) { | 187 | if (!jobList->head) { |
183 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { | 188 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { |
184 | fprintf(stderr, "%s: exactly one argument is expected\n", | 189 | fprintf(stderr, "%s: exactly one argument is expected\n", |
185 | cmd->progs[0].argv[0]); | 190 | cmd->progs[0].argv[0]); |
186 | return FALSE; | 191 | return FALSE; |
187 | } | 192 | } |
188 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { | 193 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { |
189 | fprintf(stderr, "%s: bad argument '%s'\n", | 194 | fprintf(stderr, "%s: bad argument '%s'\n", |
190 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); | 195 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); |
191 | return FALSE; | 196 | return FALSE; |
197 | for (job = jobList->head; job; job = job->next) { | ||
198 | if (job->jobId == jobNum) { | ||
199 | break; | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | } else { | ||
204 | job = jobList->head; | ||
192 | } | 205 | } |
193 | } else { | ||
194 | job = jobList->head; | ||
195 | } | ||
196 | |||
197 | for (job = jobList->head; job; job = job->next) | ||
198 | if (job->jobId == jobNum) | ||
199 | break; | ||
200 | |||
201 | if (!job) { | ||
202 | fprintf(stderr, "%s: unknown job %d\n", | ||
203 | cmd->progs[0].argv[0], jobNum); | ||
204 | return FALSE; | ||
205 | } | ||
206 | 206 | ||
207 | if (*cmd->progs[0].argv[0] == 'f') { | 207 | if (!job) { |
208 | /* Make this job the foreground job */ | 208 | fprintf(stderr, "%s: unknown job %d\n", |
209 | cmd->progs[0].argv[0], jobNum); | ||
210 | return FALSE; | ||
211 | } | ||
209 | 212 | ||
210 | if (tcsetpgrp(0, job->pgrp)) | 213 | if (*cmd->progs[0].argv[0] == 'f') { |
211 | perror("tcsetpgrp"); | 214 | /* Make this job the foreground job */ |
212 | jobList->fg = job; | 215 | if (tcsetpgrp(0, job->pgrp)) |
213 | } | 216 | perror("tcsetpgrp"); |
217 | jobList->fg = job; | ||
218 | } | ||
214 | 219 | ||
215 | /* Restart the processes in the job */ | 220 | /* Restart the processes in the job */ |
216 | for (i = 0; i < job->numProgs; i++) | 221 | for (i = 0; i < job->numProgs; i++) |
217 | job->progs[i].isStopped = 0; | 222 | job->progs[i].isStopped = 0; |
218 | 223 | ||
219 | kill(-job->pgrp, SIGCONT); | 224 | kill(-job->pgrp, SIGCONT); |
220 | 225 | ||
221 | job->stoppedProgs = 0; | 226 | job->stoppedProgs = 0; |
222 | 227 | ||
223 | return TRUE; | 228 | return TRUE; |
224 | } | 229 | } |
225 | 230 | ||
226 | /* built-in 'help' handler */ | 231 | /* built-in 'help' handler */ |
227 | static int shell_help(struct job *cmd, struct jobSet *junk) | 232 | static int shell_help(struct job *cmd, struct jobSet *junk) |
228 | { | 233 | { |
229 | struct builtInCommand *x; | 234 | struct builtInCommand *x; |
230 | 235 | ||
231 | fprintf(stdout, "\nBuilt-in commands:\n"); | 236 | fprintf(stdout, "\nBuilt-in commands:\n"); |
232 | fprintf(stdout, "-------------------\n"); | 237 | fprintf(stdout, "-------------------\n"); |
233 | for (x = bltins; x->cmd; x++) { | 238 | for (x = bltins; x->cmd; x++) { |
234 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); | 239 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); |
235 | } | 240 | } |
236 | fprintf(stdout, "\n\n"); | 241 | fprintf(stdout, "\n\n"); |
237 | return TRUE; | 242 | return TRUE; |
238 | } | 243 | } |
239 | 244 | ||
240 | /* built-in 'jobs' handler */ | 245 | /* built-in 'jobs' handler */ |
241 | static int shell_jobs(struct job *dummy, struct jobSet *jobList) | 246 | static int shell_jobs(struct job *dummy, struct jobSet *jobList) |
242 | { | 247 | { |
243 | struct job *job; | 248 | struct job *job; |
244 | char *statusString; | 249 | char *statusString; |
245 | for (job = jobList->head; job; job = job->next) { | 250 | |
246 | if (job->runningProgs == job->stoppedProgs) | 251 | for (job = jobList->head; job; job = job->next) { |
247 | statusString = "Stopped"; | 252 | if (job->runningProgs == job->stoppedProgs) |
248 | else | 253 | statusString = "Stopped"; |
249 | statusString = "Running"; | 254 | else |
255 | statusString = "Running"; | ||
250 | 256 | ||
251 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text); | 257 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text); |
252 | } | 258 | } |
253 | return TRUE; | 259 | return TRUE; |
254 | } | 260 | } |
255 | 261 | ||
256 | 262 | ||
257 | /* built-in 'pwd' handler */ | 263 | /* built-in 'pwd' handler */ |
258 | static int shell_pwd(struct job *dummy, struct jobSet *junk) | 264 | static int shell_pwd(struct job *dummy, struct jobSet *junk) |
259 | { | 265 | { |
260 | getcwd(cwd, sizeof(cwd)); | 266 | getcwd(cwd, sizeof(cwd)); |
261 | fprintf(stdout, "%s\n", cwd); | 267 | fprintf(stdout, "%s\n", cwd); |
262 | return TRUE; | 268 | return TRUE; |
263 | } | 269 | } |
264 | 270 | ||
265 | /* built-in 'set VAR=value' handler */ | 271 | /* built-in 'set VAR=value' handler */ |
266 | static int shell_set(struct job *cmd, struct jobSet *junk) | 272 | static int shell_set(struct job *cmd, struct jobSet *junk) |
267 | { | 273 | { |
268 | int res; | 274 | int res; |
269 | 275 | ||
270 | if (!cmd->progs[0].argv[1] == 1) { | 276 | if (!cmd->progs[0].argv[1] == 1) { |
271 | return (shell_env(cmd, junk)); | 277 | return (shell_env(cmd, junk)); |
272 | } | 278 | } |
273 | res = putenv(cmd->progs[0].argv[1]); | 279 | res = putenv(cmd->progs[0].argv[1]); |
274 | if (res) | 280 | if (res) |
275 | fprintf(stdout, "set: %s\n", strerror(errno)); | 281 | fprintf(stdout, "set: %s\n", strerror(errno)); |
276 | return (res); | 282 | return (res); |
277 | } | 283 | } |
278 | 284 | ||
279 | /* Built-in '.' handler (read-in and execute commands from file) */ | 285 | /* Built-in '.' handler (read-in and execute commands from file) */ |
280 | static int shell_source(struct job *cmd, struct jobSet *junk) | 286 | static int shell_source(struct job *cmd, struct jobSet *junk) |
281 | { | 287 | { |
282 | FILE *input; | 288 | FILE *input; |
283 | int status; | 289 | int status; |
284 | 290 | ||
285 | if (!cmd->progs[0].argv[1] == 1) | 291 | if (!cmd->progs[0].argv[1] == 1) |
286 | return FALSE; | 292 | return FALSE; |
287 | 293 | ||
288 | input = fopen(cmd->progs[0].argv[1], "r"); | 294 | input = fopen(cmd->progs[0].argv[1], "r"); |
289 | if (!input) { | 295 | if (!input) { |
290 | fprintf(stdout, "Couldn't open file '%s'\n", | 296 | fprintf(stdout, "Couldn't open file '%s'\n", |
291 | cmd->progs[0].argv[1]); | 297 | cmd->progs[0].argv[1]); |
292 | return FALSE; | 298 | return FALSE; |
293 | } | 299 | } |
294 | 300 | ||
295 | /* Now run the file */ | 301 | /* Now run the file */ |
296 | status = busy_loop(input); | 302 | status = busy_loop(input); |
297 | return (status); | 303 | return (status); |
298 | } | 304 | } |
299 | 305 | ||
300 | /* built-in 'unset VAR' handler */ | 306 | /* built-in 'unset VAR' handler */ |
301 | static int shell_unset(struct job *cmd, struct jobSet *junk) | 307 | static int shell_unset(struct job *cmd, struct jobSet *junk) |
302 | { | 308 | { |
303 | if (!cmd->progs[0].argv[1] == 1) { | 309 | if (!cmd->progs[0].argv[1] == 1) { |
304 | fprintf(stdout, "unset: parameter required.\n"); | 310 | fprintf(stdout, "unset: parameter required.\n"); |
305 | return FALSE; | 311 | return FALSE; |
306 | } | 312 | } |
307 | unsetenv(cmd->progs[0].argv[1]); | 313 | unsetenv(cmd->progs[0].argv[1]); |
308 | return TRUE; | 314 | return TRUE; |
309 | } | 315 | } |
310 | 316 | ||
311 | /* free up all memory from a job */ | 317 | /* free up all memory from a job */ |
312 | static void freeJob(struct job *cmd) | 318 | static void freeJob(struct job *cmd) |
313 | { | 319 | { |
314 | int i; | 320 | int i; |
315 | 321 | ||
316 | for (i = 0; i < cmd->numProgs; i++) { | 322 | for (i = 0; i < cmd->numProgs; i++) { |
317 | free(cmd->progs[i].argv); | 323 | free(cmd->progs[i].argv); |
318 | if (cmd->progs[i].redirections) | 324 | if (cmd->progs[i].redirections) |
319 | free(cmd->progs[i].redirections); | 325 | free(cmd->progs[i].redirections); |
320 | if (cmd->progs[i].freeGlob) | 326 | if (cmd->progs[i].freeGlob) |
321 | globfree(&cmd->progs[i].globResult); | 327 | globfree(&cmd->progs[i].globResult); |
322 | } | 328 | } |
323 | free(cmd->progs); | 329 | free(cmd->progs); |
324 | if (cmd->text) | 330 | if (cmd->text) |
325 | free(cmd->text); | 331 | free(cmd->text); |
326 | free(cmd->cmdBuf); | 332 | free(cmd->cmdBuf); |
327 | } | 333 | } |
328 | 334 | ||
329 | /* remove a job from the jobList */ | 335 | /* remove a job from the jobList */ |
330 | static void removeJob(struct jobSet *jobList, struct job *job) | 336 | static void removeJob(struct jobSet *jobList, struct job *job) |
331 | { | 337 | { |
332 | struct job *prevJob; | 338 | struct job *prevJob; |
333 | 339 | ||
334 | freeJob(job); | 340 | freeJob(job); |
335 | if (job == jobList->head) { | 341 | if (job == jobList->head) { |
336 | jobList->head = job->next; | 342 | jobList->head = job->next; |
337 | } else { | 343 | } else { |
338 | prevJob = jobList->head; | 344 | prevJob = jobList->head; |
339 | while (prevJob->next != job) | 345 | while (prevJob->next != job) |
340 | prevJob = prevJob->next; | 346 | prevJob = prevJob->next; |
341 | prevJob->next = job->next; | 347 | prevJob->next = job->next; |
342 | } | 348 | } |
343 | 349 | ||
344 | free(job); | 350 | free(job); |
345 | } | 351 | } |
346 | 352 | ||
347 | /* Checks to see if any background processes have exited -- if they | 353 | /* Checks to see if any background processes have exited -- if they |
348 | have, figure out why and see if a job has completed */ | 354 | have, figure out why and see if a job has completed */ |
349 | static void checkJobs(struct jobSet *jobList) | 355 | static void checkJobs(struct jobSet *jobList) |
350 | { | 356 | { |
351 | struct job *job; | 357 | struct job *job; |
352 | pid_t childpid; | 358 | pid_t childpid; |
353 | int status; | 359 | int status; |
354 | int progNum = 0; | 360 | int progNum = 0; |
361 | |||
362 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | ||
363 | for (job = jobList->head; job; job = job->next) { | ||
364 | progNum = 0; | ||
365 | while (progNum < job->numProgs && | ||
366 | job->progs[progNum].pid != childpid) progNum++; | ||
367 | if (progNum < job->numProgs) | ||
368 | break; | ||
369 | } | ||
355 | 370 | ||
356 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | 371 | if (WIFEXITED(status) || WIFSIGNALED(status)) { |
357 | for (job = jobList->head; job; job = job->next) { | 372 | /* child exited */ |
358 | progNum = 0; | 373 | job->runningProgs--; |
359 | while (progNum < job->numProgs && | 374 | job->progs[progNum].pid = 0; |
360 | job->progs[progNum].pid != childpid) progNum++; | ||
361 | if (progNum < job->numProgs) | ||
362 | break; | ||
363 | } | ||
364 | 375 | ||
365 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | 376 | if (!job->runningProgs) { |
366 | /* child exited */ | 377 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); |
367 | job->runningProgs--; | 378 | removeJob(jobList, job); |
368 | job->progs[progNum].pid = 0; | 379 | } |
380 | } else { | ||
381 | /* child stopped */ | ||
382 | job->stoppedProgs++; | ||
383 | job->progs[progNum].isStopped = 1; | ||
369 | 384 | ||
370 | if (!job->runningProgs) { | 385 | if (job->stoppedProgs == job->numProgs) { |
371 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); | 386 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", |
372 | removeJob(jobList, job); | 387 | job->text); |
373 | } | 388 | } |
374 | } else { | 389 | } |
375 | /* child stopped */ | ||
376 | job->stoppedProgs++; | ||
377 | job->progs[progNum].isStopped = 1; | ||
378 | |||
379 | if (job->stoppedProgs == job->numProgs) { | ||
380 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", | ||
381 | job->text); | ||
382 | } | ||
383 | } | 390 | } |
384 | } | ||
385 | 391 | ||
386 | if (childpid == -1 && errno != ECHILD) | 392 | if (childpid == -1 && errno != ECHILD) |
387 | perror("waitpid"); | 393 | perror("waitpid"); |
388 | } | 394 | } |
389 | 395 | ||
390 | static int getCommand(FILE * source, char *command) | 396 | static int getCommand(FILE * source, char *command) |
391 | { | 397 | { |
392 | if (source == stdin) { | 398 | if (source == stdin) { |
393 | fprintf(stdout, "BBSHELL %s %s", cwd, prompt); | 399 | fprintf(stdout, "BBSHELL %s %s", cwd, prompt); |
394 | fflush(stdout); | 400 | fflush(stdout); |
395 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 401 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
396 | cmdedit_read_input(fileno(stdin), fileno(stdout), command); | 402 | cmdedit_read_input(fileno(stdin), fileno(stdout), command); |
397 | return 0; | 403 | return 0; |
398 | #endif | 404 | #endif |
399 | } | 405 | } |
400 | 406 | ||
401 | if (!fgets(command, BUFSIZ - 2, source)) { | 407 | if (!fgets(command, BUFSIZ - 2, source)) { |
402 | if (source == stdin) | 408 | if (source == stdin) |
403 | printf("\n"); | 409 | printf("\n"); |
404 | return 1; | 410 | return 1; |
405 | } | 411 | } |
406 | 412 | ||
407 | /* remove trailing newline */ | 413 | /* remove trailing newline */ |
408 | command[strlen(command) - 1] = '\0'; | 414 | command[strlen(command) - 1] = '\0'; |
409 | 415 | ||
410 | return 0; | 416 | return 0; |
411 | } | 417 | } |
412 | 418 | ||
413 | static void globLastArgument(struct childProgram *prog, int *argcPtr, | 419 | static void globLastArgument(struct childProgram *prog, int *argcPtr, |
414 | int *argcAllocedPtr) | 420 | int *argcAllocedPtr) |
415 | { | 421 | { |
416 | int argc = *argcPtr; | 422 | int argc = *argcPtr; |
417 | int argcAlloced = *argcAllocedPtr; | 423 | int argcAlloced = *argcAllocedPtr; |
418 | int rc; | 424 | int rc; |
419 | int flags; | 425 | int flags; |
420 | int i; | 426 | int i; |
421 | char *src, *dst; | 427 | char *src, *dst; |
422 | 428 | ||
423 | if (argc > 1) { /* cmd->globResult is already initialized */ | 429 | if (argc > 1) { /* cmd->globResult is already initialized */ |
424 | flags = GLOB_APPEND; | 430 | flags = GLOB_APPEND; |
425 | i = prog->globResult.gl_pathc; | 431 | i = prog->globResult.gl_pathc; |
426 | } else { | 432 | } else { |
427 | prog->freeGlob = 1; | 433 | prog->freeGlob = 1; |
428 | flags = 0; | 434 | flags = 0; |
429 | i = 0; | 435 | i = 0; |
430 | } | 436 | } |
431 | 437 | ||
432 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); | 438 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); |
433 | if (rc == GLOB_NOSPACE) { | 439 | if (rc == GLOB_NOSPACE) { |
434 | fprintf(stderr, "out of space during glob operation\n"); | 440 | fprintf(stderr, "out of space during glob operation\n"); |
435 | return; | 441 | return; |
436 | } else if (rc == GLOB_NOMATCH || | 442 | } else if (rc == GLOB_NOMATCH || |
437 | (!rc && (prog->globResult.gl_pathc - i) == 1 && | 443 | (!rc && (prog->globResult.gl_pathc - i) == 1 && |
438 | !strcmp(prog->argv[argc - 1], | 444 | !strcmp(prog->argv[argc - 1], |
439 | prog->globResult.gl_pathv[i]))) { | 445 | prog->globResult.gl_pathv[i]))) { |
440 | /* we need to remove whatever \ quoting is still present */ | 446 | /* we need to remove whatever \ quoting is still present */ |
441 | src = dst = prog->argv[argc - 1]; | 447 | src = dst = prog->argv[argc - 1]; |
442 | while (*src) { | 448 | while (*src) { |
443 | if (*src != '\\') | 449 | if (*src != '\\') |
444 | *dst++ = *src; | 450 | *dst++ = *src; |
445 | src++; | 451 | src++; |
452 | } | ||
453 | *dst = '\0'; | ||
454 | } else if (!rc) { | ||
455 | argcAlloced += (prog->globResult.gl_pathc - i); | ||
456 | prog->argv = | ||
457 | realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); | ||
458 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, | ||
459 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); | ||
460 | argc += (prog->globResult.gl_pathc - i - 1); | ||
446 | } | 461 | } |
447 | *dst = '\0'; | 462 | |
448 | } else if (!rc) { | 463 | *argcAllocedPtr = argcAlloced; |
449 | argcAlloced += (prog->globResult.gl_pathc - i); | 464 | *argcPtr = argc; |
450 | prog->argv = | ||
451 | realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); | ||
452 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, | ||
453 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); | ||
454 | argc += (prog->globResult.gl_pathc - i - 1); | ||
455 | } | ||
456 | |||
457 | *argcAllocedPtr = argcAlloced; | ||
458 | *argcPtr = argc; | ||
459 | } | 465 | } |
460 | 466 | ||
461 | /* Return cmd->numProgs as 0 if no command is present (e.g. an empty | 467 | /* Return cmd->numProgs as 0 if no command is present (e.g. an empty |
@@ -465,396 +471,396 @@ static void globLastArgument(struct childProgram *prog, int *argcPtr, | |||
465 | present. */ | 471 | present. */ |
466 | static int parseCommand(char **commandPtr, struct job *job, int *isBg) | 472 | static int parseCommand(char **commandPtr, struct job *job, int *isBg) |
467 | { | 473 | { |
468 | char *command; | 474 | char *command; |
469 | char *returnCommand = NULL; | 475 | char *returnCommand = NULL; |
470 | char *src, *buf, *chptr; | 476 | char *src, *buf, *chptr; |
471 | int argc = 0; | 477 | int argc = 0; |
472 | int done = 0; | 478 | int done = 0; |
473 | int argvAlloced; | 479 | int argvAlloced; |
474 | int i; | 480 | int i; |
475 | char quote = '\0'; | 481 | char quote = '\0'; |
476 | int count; | 482 | int count; |
477 | struct childProgram *prog; | 483 | struct childProgram *prog; |
478 | 484 | ||
479 | /* skip leading white space */ | 485 | /* skip leading white space */ |
480 | while (**commandPtr && isspace(**commandPtr)) | 486 | while (**commandPtr && isspace(**commandPtr)) |
481 | (*commandPtr)++; | 487 | (*commandPtr)++; |
482 | 488 | ||
483 | /* this handles empty lines or leading '#' characters */ | 489 | /* this handles empty lines or leading '#' characters */ |
484 | if (!**commandPtr || (**commandPtr == '#')) { | 490 | if (!**commandPtr || (**commandPtr == '#')) { |
485 | job->numProgs = 0; | 491 | job->numProgs = 0; |
486 | *commandPtr = NULL; | 492 | *commandPtr = NULL; |
487 | return 0; | 493 | return 0; |
488 | } | 494 | } |
489 | |||
490 | *isBg = 0; | ||
491 | job->numProgs = 1; | ||
492 | job->progs = malloc(sizeof(*job->progs)); | ||
493 | |||
494 | /* We set the argv elements to point inside of this string. The | ||
495 | memory is freed by freeJob(). | ||
496 | |||
497 | Getting clean memory relieves us of the task of NULL | ||
498 | terminating things and makes the rest of this look a bit | ||
499 | cleaner (though it is, admittedly, a tad less efficient) */ | ||
500 | job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); | ||
501 | job->text = NULL; | ||
502 | |||
503 | prog = job->progs; | ||
504 | prog->numRedirections = 0; | ||
505 | prog->redirections = NULL; | ||
506 | prog->freeGlob = 0; | ||
507 | prog->isStopped = 0; | ||
508 | |||
509 | argvAlloced = 5; | ||
510 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
511 | prog->argv[0] = job->cmdBuf; | ||
512 | |||
513 | buf = command; | ||
514 | src = *commandPtr; | ||
515 | while (*src && !done) { | ||
516 | if (quote == *src) { | ||
517 | quote = '\0'; | ||
518 | } else if (quote) { | ||
519 | if (*src == '\\') { | ||
520 | src++; | ||
521 | if (!*src) { | ||
522 | fprintf(stderr, "character expected after \\\n"); | ||
523 | freeJob(job); | ||
524 | return 1; | ||
525 | } | ||
526 | 495 | ||
527 | /* in shell, "\'" should yield \' */ | 496 | *isBg = 0; |
528 | if (*src != quote) | 497 | job->numProgs = 1; |
529 | *buf++ = '\\'; | 498 | job->progs = malloc(sizeof(*job->progs)); |
530 | } else if (*src == '*' || *src == '?' || *src == '[' || | 499 | |
531 | *src == ']') *buf++ = '\\'; | 500 | /* We set the argv elements to point inside of this string. The |
532 | *buf++ = *src; | 501 | memory is freed by freeJob(). |
533 | } else if (isspace(*src)) { | 502 | |
534 | if (*prog->argv[argc]) { | 503 | Getting clean memory relieves us of the task of NULL |
535 | buf++, argc++; | 504 | terminating things and makes the rest of this look a bit |
536 | /* +1 here leaves room for the NULL which ends argv */ | 505 | cleaner (though it is, admittedly, a tad less efficient) */ |
537 | if ((argc + 1) == argvAlloced) { | 506 | job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); |
538 | argvAlloced += 5; | 507 | job->text = NULL; |
539 | prog->argv = realloc(prog->argv, | 508 | |
540 | sizeof(*prog->argv) * | 509 | prog = job->progs; |
541 | argvAlloced); | 510 | prog->numRedirections = 0; |
542 | } | 511 | prog->redirections = NULL; |
543 | prog->argv[argc] = buf; | 512 | prog->freeGlob = 0; |
513 | prog->isStopped = 0; | ||
514 | |||
515 | argvAlloced = 5; | ||
516 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
517 | prog->argv[0] = job->cmdBuf; | ||
518 | |||
519 | buf = command; | ||
520 | src = *commandPtr; | ||
521 | while (*src && !done) { | ||
522 | if (quote == *src) { | ||
523 | quote = '\0'; | ||
524 | } else if (quote) { | ||
525 | if (*src == '\\') { | ||
526 | src++; | ||
527 | if (!*src) { | ||
528 | fprintf(stderr, "character expected after \\\n"); | ||
529 | freeJob(job); | ||
530 | return 1; | ||
531 | } | ||
532 | |||
533 | /* in shell, "\'" should yield \' */ | ||
534 | if (*src != quote) | ||
535 | *buf++ = '\\'; | ||
536 | } else if (*src == '*' || *src == '?' || *src == '[' || | ||
537 | *src == ']') *buf++ = '\\'; | ||
538 | *buf++ = *src; | ||
539 | } else if (isspace(*src)) { | ||
540 | if (*prog->argv[argc]) { | ||
541 | buf++, argc++; | ||
542 | /* +1 here leaves room for the NULL which ends argv */ | ||
543 | if ((argc + 1) == argvAlloced) { | ||
544 | argvAlloced += 5; | ||
545 | prog->argv = realloc(prog->argv, | ||
546 | sizeof(*prog->argv) * | ||
547 | argvAlloced); | ||
548 | } | ||
549 | prog->argv[argc] = buf; | ||
550 | |||
551 | globLastArgument(prog, &argc, &argvAlloced); | ||
552 | } | ||
553 | } else | ||
554 | switch (*src) { | ||
555 | case '"': | ||
556 | case '\'': | ||
557 | quote = *src; | ||
558 | break; | ||
559 | |||
560 | case '#': /* comment */ | ||
561 | done = 1; | ||
562 | break; | ||
563 | |||
564 | case '>': /* redirections */ | ||
565 | case '<': | ||
566 | i = prog->numRedirections++; | ||
567 | prog->redirections = realloc(prog->redirections, | ||
568 | sizeof(*prog->redirections) * | ||
569 | (i + 1)); | ||
570 | |||
571 | prog->redirections[i].fd = -1; | ||
572 | if (buf != prog->argv[argc]) { | ||
573 | /* the stuff before this character may be the file number | ||
574 | being redirected */ | ||
575 | prog->redirections[i].fd = | ||
576 | strtol(prog->argv[argc], &chptr, 10); | ||
577 | |||
578 | if (*chptr && *prog->argv[argc]) { | ||
579 | buf++, argc++; | ||
580 | globLastArgument(prog, &argc, &argvAlloced); | ||
581 | } | ||
582 | } | ||
583 | |||
584 | if (prog->redirections[i].fd == -1) { | ||
585 | if (*src == '>') | ||
586 | prog->redirections[i].fd = 1; | ||
587 | else | ||
588 | prog->redirections[i].fd = 0; | ||
589 | } | ||
590 | |||
591 | if (*src++ == '>') { | ||
592 | if (*src == '>') | ||
593 | prog->redirections[i].type = | ||
594 | REDIRECT_APPEND, src++; | ||
595 | else | ||
596 | prog->redirections[i].type = REDIRECT_OVERWRITE; | ||
597 | } else { | ||
598 | prog->redirections[i].type = REDIRECT_INPUT; | ||
599 | } | ||
600 | |||
601 | /* This isn't POSIX sh compliant. Oh well. */ | ||
602 | chptr = src; | ||
603 | while (isspace(*chptr)) | ||
604 | chptr++; | ||
605 | |||
606 | if (!*chptr) { | ||
607 | fprintf(stderr, "file name expected after %c\n", *src); | ||
608 | freeJob(job); | ||
609 | return 1; | ||
610 | } | ||
611 | |||
612 | prog->redirections[i].filename = buf; | ||
613 | while (*chptr && !isspace(*chptr)) | ||
614 | *buf++ = *chptr++; | ||
615 | |||
616 | src = chptr - 1; /* we src++ later */ | ||
617 | prog->argv[argc] = ++buf; | ||
618 | break; | ||
619 | |||
620 | case '|': /* pipe */ | ||
621 | /* finish this command */ | ||
622 | if (*prog->argv[argc]) | ||
623 | argc++; | ||
624 | if (!argc) { | ||
625 | fprintf(stderr, "empty command in pipe\n"); | ||
626 | freeJob(job); | ||
627 | return 1; | ||
628 | } | ||
629 | prog->argv[argc] = NULL; | ||
630 | |||
631 | /* and start the next */ | ||
632 | job->numProgs++; | ||
633 | job->progs = realloc(job->progs, | ||
634 | sizeof(*job->progs) * job->numProgs); | ||
635 | prog = job->progs + (job->numProgs - 1); | ||
636 | prog->numRedirections = 0; | ||
637 | prog->redirections = NULL; | ||
638 | prog->freeGlob = 0; | ||
639 | argc = 0; | ||
640 | |||
641 | argvAlloced = 5; | ||
642 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
643 | prog->argv[0] = ++buf; | ||
644 | |||
645 | src++; | ||
646 | while (*src && isspace(*src)) | ||
647 | src++; | ||
648 | |||
649 | if (!*src) { | ||
650 | fprintf(stderr, "empty command in pipe\n"); | ||
651 | return 1; | ||
652 | } | ||
653 | src--; /* we'll ++ it at the end of the loop */ | ||
654 | |||
655 | break; | ||
656 | |||
657 | case '&': /* background */ | ||
658 | *isBg = 1; | ||
659 | case ';': /* multiple commands */ | ||
660 | done = 1; | ||
661 | returnCommand = *commandPtr + (src - *commandPtr) + 1; | ||
662 | break; | ||
544 | 663 | ||
664 | case '\\': | ||
665 | src++; | ||
666 | if (!*src) { | ||
667 | freeJob(job); | ||
668 | fprintf(stderr, "character expected after \\\n"); | ||
669 | return 1; | ||
670 | } | ||
671 | if (*src == '*' || *src == '[' || *src == ']' | ||
672 | || *src == '?') *buf++ = '\\'; | ||
673 | /* fallthrough */ | ||
674 | default: | ||
675 | *buf++ = *src; | ||
676 | } | ||
677 | |||
678 | src++; | ||
679 | } | ||
680 | |||
681 | if (*prog->argv[argc]) { | ||
682 | argc++; | ||
545 | globLastArgument(prog, &argc, &argvAlloced); | 683 | globLastArgument(prog, &argc, &argvAlloced); |
546 | } | 684 | } |
547 | } else | 685 | if (!argc) { |
548 | switch (*src) { | 686 | freeJob(job); |
549 | case '"': | 687 | return 0; |
550 | case '\'': | 688 | } |
551 | quote = *src; | 689 | prog->argv[argc] = NULL; |
552 | break; | ||
553 | |||
554 | case '#': /* comment */ | ||
555 | done = 1; | ||
556 | break; | ||
557 | |||
558 | case '>': /* redirections */ | ||
559 | case '<': | ||
560 | i = prog->numRedirections++; | ||
561 | prog->redirections = realloc(prog->redirections, | ||
562 | sizeof(*prog->redirections) * | ||
563 | (i + 1)); | ||
564 | |||
565 | prog->redirections[i].fd = -1; | ||
566 | if (buf != prog->argv[argc]) { | ||
567 | /* the stuff before this character may be the file number | ||
568 | being redirected */ | ||
569 | prog->redirections[i].fd = | ||
570 | strtol(prog->argv[argc], &chptr, 10); | ||
571 | |||
572 | if (*chptr && *prog->argv[argc]) { | ||
573 | buf++, argc++; | ||
574 | globLastArgument(prog, &argc, &argvAlloced); | ||
575 | } | ||
576 | } | ||
577 | 690 | ||
578 | if (prog->redirections[i].fd == -1) { | 691 | if (!returnCommand) { |
579 | if (*src == '>') | 692 | job->text = malloc(strlen(*commandPtr) + 1); |
580 | prog->redirections[i].fd = 1; | 693 | strcpy(job->text, *commandPtr); |
581 | else | 694 | } else { |
582 | prog->redirections[i].fd = 0; | 695 | /* This leaves any trailing spaces, which is a bit sloppy */ |
583 | } | ||
584 | 696 | ||
585 | if (*src++ == '>') { | 697 | count = returnCommand - *commandPtr; |
586 | if (*src == '>') | 698 | job->text = malloc(count + 1); |
587 | prog->redirections[i].type = | 699 | strncpy(job->text, *commandPtr, count); |
588 | REDIRECT_APPEND, src++; | 700 | job->text[count] = '\0'; |
589 | else | 701 | } |
590 | prog->redirections[i].type = REDIRECT_OVERWRITE; | 702 | |
591 | } else { | 703 | *commandPtr = returnCommand; |
592 | prog->redirections[i].type = REDIRECT_INPUT; | ||
593 | } | ||
594 | 704 | ||
595 | /* This isn't POSIX sh compliant. Oh well. */ | 705 | return 0; |
596 | chptr = src; | 706 | } |
597 | while (isspace(*chptr)) | ||
598 | chptr++; | ||
599 | 707 | ||
600 | if (!*chptr) { | 708 | static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) |
601 | fprintf(stderr, "file name expected after %c\n", *src); | 709 | { |
602 | freeJob(job); | 710 | struct job *job; |
603 | return 1; | 711 | int i; |
712 | int nextin, nextout; | ||
713 | int pipefds[2]; /* pipefd[0] is for reading */ | ||
714 | struct builtInCommand *x; | ||
715 | |||
716 | /* handle built-ins here -- we don't fork() so we can't background | ||
717 | these very easily */ | ||
718 | for (x = bltins; x->cmd; x++) { | ||
719 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { | ||
720 | return (x->function(&newJob, jobList)); | ||
604 | } | 721 | } |
722 | } | ||
605 | 723 | ||
606 | prog->redirections[i].filename = buf; | 724 | nextin = 0, nextout = 1; |
607 | while (*chptr && !isspace(*chptr)) | 725 | for (i = 0; i < newJob.numProgs; i++) { |
608 | *buf++ = *chptr++; | 726 | if ((i + 1) < newJob.numProgs) { |
609 | 727 | pipe(pipefds); | |
610 | src = chptr - 1; /* we src++ later */ | 728 | nextout = pipefds[1]; |
611 | prog->argv[argc] = ++buf; | 729 | } else { |
612 | break; | 730 | nextout = 1; |
613 | |||
614 | case '|': /* pipe */ | ||
615 | /* finish this command */ | ||
616 | if (*prog->argv[argc]) | ||
617 | argc++; | ||
618 | if (!argc) { | ||
619 | fprintf(stderr, "empty command in pipe\n"); | ||
620 | freeJob(job); | ||
621 | return 1; | ||
622 | } | 731 | } |
623 | prog->argv[argc] = NULL; | ||
624 | |||
625 | /* and start the next */ | ||
626 | job->numProgs++; | ||
627 | job->progs = realloc(job->progs, | ||
628 | sizeof(*job->progs) * job->numProgs); | ||
629 | prog = job->progs + (job->numProgs - 1); | ||
630 | prog->numRedirections = 0; | ||
631 | prog->redirections = NULL; | ||
632 | prog->freeGlob = 0; | ||
633 | argc = 0; | ||
634 | |||
635 | argvAlloced = 5; | ||
636 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
637 | prog->argv[0] = ++buf; | ||
638 | 732 | ||
639 | src++; | 733 | if (!(newJob.progs[i].pid = fork())) { |
640 | while (*src && isspace(*src)) | 734 | signal(SIGTTOU, SIG_DFL); |
641 | src++; | ||
642 | 735 | ||
643 | if (!*src) { | 736 | if (nextin != 0) { |
644 | fprintf(stderr, "empty command in pipe\n"); | 737 | dup2(nextin, 0); |
645 | return 1; | 738 | close(nextin); |
646 | } | 739 | } |
647 | src--; /* we'll ++ it at the end of the loop */ | ||
648 | 740 | ||
649 | break; | 741 | if (nextout != 1) { |
742 | dup2(nextout, 1); | ||
743 | close(nextout); | ||
744 | } | ||
650 | 745 | ||
651 | case '&': /* background */ | 746 | /* explicit redirections override pipes */ |
652 | *isBg = 1; | 747 | setupRedirections(newJob.progs + i); |
653 | case ';': /* multiple commands */ | ||
654 | done = 1; | ||
655 | returnCommand = *commandPtr + (src - *commandPtr) + 1; | ||
656 | break; | ||
657 | 748 | ||
658 | case '\\': | 749 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); |
659 | src++; | 750 | fatalError("sh: %s: %s\n", newJob.progs[i].argv[0], |
660 | if (!*src) { | 751 | strerror(errno)); |
661 | freeJob(job); | ||
662 | fprintf(stderr, "character expected after \\\n"); | ||
663 | return 1; | ||
664 | } | 752 | } |
665 | if (*src == '*' || *src == '[' || *src == ']' | ||
666 | || *src == '?') *buf++ = '\\'; | ||
667 | /* fallthrough */ | ||
668 | default: | ||
669 | *buf++ = *src; | ||
670 | } | ||
671 | |||
672 | src++; | ||
673 | } | ||
674 | |||
675 | if (*prog->argv[argc]) { | ||
676 | argc++; | ||
677 | globLastArgument(prog, &argc, &argvAlloced); | ||
678 | } | ||
679 | if (!argc) { | ||
680 | freeJob(job); | ||
681 | return 0; | ||
682 | } | ||
683 | prog->argv[argc] = NULL; | ||
684 | 753 | ||
685 | if (!returnCommand) { | 754 | /* put our child in the process group whose leader is the |
686 | job->text = malloc(strlen(*commandPtr) + 1); | 755 | first process in this pipe */ |
687 | strcpy(job->text, *commandPtr); | 756 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); |
688 | } else { | ||
689 | /* This leaves any trailing spaces, which is a bit sloppy */ | ||
690 | 757 | ||
691 | count = returnCommand - *commandPtr; | 758 | if (nextin != 0) |
692 | job->text = malloc(count + 1); | 759 | close(nextin); |
693 | strncpy(job->text, *commandPtr, count); | 760 | if (nextout != 1) |
694 | job->text[count] = '\0'; | 761 | close(nextout); |
695 | } | ||
696 | 762 | ||
697 | *commandPtr = returnCommand; | 763 | /* If there isn't another process, nextin is garbage |
764 | but it doesn't matter */ | ||
765 | nextin = pipefds[0]; | ||
766 | } | ||
698 | 767 | ||
699 | return 0; | 768 | newJob.pgrp = newJob.progs[0].pid; |
700 | } | ||
701 | 769 | ||
702 | static int runCommand(struct job newJob, struct jobSet *jobList, int inBg) | 770 | /* find the ID for the job to use */ |
703 | { | 771 | newJob.jobId = 1; |
704 | struct job *job; | 772 | for (job = jobList->head; job; job = job->next) |
705 | int i; | 773 | if (job->jobId >= newJob.jobId) |
706 | int nextin, nextout; | 774 | newJob.jobId = job->jobId + 1; |
707 | int pipefds[2]; /* pipefd[0] is for reading */ | ||
708 | struct builtInCommand *x; | ||
709 | |||
710 | /* handle built-ins here -- we don't fork() so we can't background | ||
711 | these very easily */ | ||
712 | for (x = bltins; x->cmd; x++) { | ||
713 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { | ||
714 | return (x->function(&newJob, jobList)); | ||
715 | } | ||
716 | } | ||
717 | 775 | ||
718 | nextin = 0, nextout = 1; | 776 | /* add the job to the list of running jobs */ |
719 | for (i = 0; i < newJob.numProgs; i++) { | 777 | if (!jobList->head) { |
720 | if ((i + 1) < newJob.numProgs) { | 778 | job = jobList->head = malloc(sizeof(*job)); |
721 | pipe(pipefds); | ||
722 | nextout = pipefds[1]; | ||
723 | } else { | 779 | } else { |
724 | nextout = 1; | 780 | for (job = jobList->head; job->next; job = job->next); |
781 | job->next = malloc(sizeof(*job)); | ||
782 | job = job->next; | ||
725 | } | 783 | } |
726 | 784 | ||
727 | if (!(newJob.progs[i].pid = fork())) { | 785 | *job = newJob; |
728 | signal(SIGTTOU, SIG_DFL); | 786 | job->next = NULL; |
787 | job->runningProgs = job->numProgs; | ||
788 | job->stoppedProgs = 0; | ||
729 | 789 | ||
730 | if (nextin != 0) { | 790 | if (inBg) { |
731 | dup2(nextin, 0); | 791 | /* we don't wait for background jobs to return -- append it |
732 | close(nextin); | 792 | to the list of backgrounded jobs and leave it alone */ |
733 | } | ||
734 | 793 | ||
735 | if (nextout != 1) { | 794 | printf("[%d] %d\n", job->jobId, |
736 | dup2(nextout, 1); | 795 | newJob.progs[newJob.numProgs - 1].pid); |
737 | close(nextout); | 796 | } else { |
738 | } | 797 | jobList->fg = job; |
739 | 798 | ||
740 | /* explicit redirections override pipes */ | 799 | /* move the new process group into the foreground */ |
741 | setupRedirections(newJob.progs + i); | ||
742 | 800 | ||
743 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); | 801 | if (tcsetpgrp(0, newJob.pgrp)) |
744 | fatalError("sh: %s: %s\n", newJob.progs[i].argv[0], | 802 | perror("tcsetpgrp"); |
745 | strerror(errno)); | ||
746 | } | 803 | } |
747 | 804 | ||
748 | /* put our child in the process group whose leader is the | 805 | return 0; |
749 | first process in this pipe */ | ||
750 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); | ||
751 | |||
752 | if (nextin != 0) | ||
753 | close(nextin); | ||
754 | if (nextout != 1) | ||
755 | close(nextout); | ||
756 | |||
757 | /* If there isn't another process, nextin is garbage | ||
758 | but it doesn't matter */ | ||
759 | nextin = pipefds[0]; | ||
760 | } | ||
761 | |||
762 | newJob.pgrp = newJob.progs[0].pid; | ||
763 | |||
764 | /* find the ID for the job to use */ | ||
765 | newJob.jobId = 1; | ||
766 | for (job = jobList->head; job; job = job->next) | ||
767 | if (job->jobId >= newJob.jobId) | ||
768 | newJob.jobId = job->jobId + 1; | ||
769 | |||
770 | /* add the job to the list of running jobs */ | ||
771 | if (!jobList->head) { | ||
772 | job = jobList->head = malloc(sizeof(*job)); | ||
773 | } else { | ||
774 | for (job = jobList->head; job->next; job = job->next); | ||
775 | job->next = malloc(sizeof(*job)); | ||
776 | job = job->next; | ||
777 | } | ||
778 | |||
779 | *job = newJob; | ||
780 | job->next = NULL; | ||
781 | job->runningProgs = job->numProgs; | ||
782 | job->stoppedProgs = 0; | ||
783 | |||
784 | if (inBg) { | ||
785 | /* we don't wait for background jobs to return -- append it | ||
786 | to the list of backgrounded jobs and leave it alone */ | ||
787 | |||
788 | printf("[%d] %d\n", job->jobId, | ||
789 | newJob.progs[newJob.numProgs - 1].pid); | ||
790 | } else { | ||
791 | jobList->fg = job; | ||
792 | |||
793 | /* move the new process group into the foreground */ | ||
794 | |||
795 | if (tcsetpgrp(0, newJob.pgrp)) | ||
796 | perror("tcsetpgrp"); | ||
797 | } | ||
798 | |||
799 | return 0; | ||
800 | } | 806 | } |
801 | 807 | ||
802 | static int setupRedirections(struct childProgram *prog) | 808 | static int setupRedirections(struct childProgram *prog) |
803 | { | 809 | { |
804 | int i; | 810 | int i; |
805 | int openfd; | 811 | int openfd; |
806 | int mode = O_RDONLY; | 812 | int mode = O_RDONLY; |
807 | struct redirectionSpecifier *redir = prog->redirections; | 813 | struct redirectionSpecifier *redir = prog->redirections; |
808 | 814 | ||
809 | for (i = 0; i < prog->numRedirections; i++, redir++) { | 815 | for (i = 0; i < prog->numRedirections; i++, redir++) { |
810 | switch (redir->type) { | 816 | switch (redir->type) { |
811 | case REDIRECT_INPUT: | 817 | case REDIRECT_INPUT: |
812 | mode = O_RDONLY; | 818 | mode = O_RDONLY; |
813 | break; | 819 | break; |
814 | case REDIRECT_OVERWRITE: | 820 | case REDIRECT_OVERWRITE: |
815 | mode = O_RDWR | O_CREAT | O_TRUNC; | 821 | mode = O_RDWR | O_CREAT | O_TRUNC; |
816 | break; | 822 | break; |
817 | case REDIRECT_APPEND: | 823 | case REDIRECT_APPEND: |
818 | mode = O_RDWR | O_CREAT | O_APPEND; | 824 | mode = O_RDWR | O_CREAT | O_APPEND; |
819 | break; | 825 | break; |
820 | } | 826 | } |
821 | 827 | ||
822 | openfd = open(redir->filename, mode, 0666); | 828 | openfd = open(redir->filename, mode, 0666); |
823 | if (openfd < 0) { | 829 | if (openfd < 0) { |
824 | /* this could get lost if stderr has been redirected, but | 830 | /* this could get lost if stderr has been redirected, but |
825 | bash and ash both lose it as well (though zsh doesn't!) */ | 831 | bash and ash both lose it as well (though zsh doesn't!) */ |
826 | fprintf(stderr, "error opening %s: %s\n", redir->filename, | 832 | fprintf(stderr, "error opening %s: %s\n", redir->filename, |
827 | strerror(errno)); | 833 | strerror(errno)); |
828 | return 1; | 834 | return 1; |
829 | } | 835 | } |
830 | 836 | ||
831 | if (openfd != redir->fd) { | 837 | if (openfd != redir->fd) { |
832 | dup2(openfd, redir->fd); | 838 | dup2(openfd, redir->fd); |
833 | close(openfd); | 839 | close(openfd); |
840 | } | ||
834 | } | 841 | } |
835 | } | ||
836 | 842 | ||
837 | return 0; | 843 | return 0; |
838 | } | 844 | } |
839 | 845 | ||
840 | 846 | ||
841 | static int busy_loop(FILE * input) | 847 | static int busy_loop(FILE * input) |
842 | { | 848 | { |
843 | char *command; | 849 | char *command; |
844 | char *nextCommand = NULL; | 850 | char *nextCommand = NULL; |
845 | struct jobSet jobList = { NULL, NULL }; | 851 | struct jobSet jobList = { NULL, NULL }; |
846 | struct job newJob; | 852 | struct job newJob; |
847 | int i; | 853 | int i; |
848 | int status; | 854 | int status; |
849 | int inBg; | 855 | int inBg; |
850 | 856 | ||
851 | command = (char*) calloc(BUFSIZ, sizeof(char)); | 857 | command = (char *) calloc(BUFSIZ, sizeof(char)); |
852 | 858 | ||
853 | /* don't pay any attention to this signal; it just confuses | 859 | /* don't pay any attention to this signal; it just confuses |
854 | things and isn't really meant for shells anyway */ | 860 | things and isn't really meant for shells anyway */ |
855 | signal(SIGTTOU, SIG_IGN); | 861 | signal(SIGTTOU, SIG_IGN); |
856 | 862 | ||
857 | while (1) { | 863 | while (1) { |
858 | if (!jobList.fg) { | 864 | if (!jobList.fg) { |
859 | /* no job is in the foreground */ | 865 | /* no job is in the foreground */ |
860 | 866 | ||
@@ -862,97 +868,97 @@ static int busy_loop(FILE * input) | |||
862 | checkJobs(&jobList); | 868 | checkJobs(&jobList); |
863 | 869 | ||
864 | if (!nextCommand) { | 870 | if (!nextCommand) { |
865 | if (getCommand(input, command)) | 871 | if (getCommand(input, command)) |
866 | break; | 872 | break; |
867 | nextCommand = command; | 873 | nextCommand = command; |
868 | } | 874 | } |
869 | 875 | ||
870 | if (!parseCommand(&nextCommand, &newJob, &inBg) && | 876 | if (!parseCommand(&nextCommand, &newJob, &inBg) && |
871 | newJob.numProgs) { | 877 | newJob.numProgs) { |
872 | runCommand(newJob, &jobList, inBg); | 878 | runCommand(newJob, &jobList, inBg); |
873 | } | 879 | } |
874 | } else { | 880 | } else { |
875 | /* a job is running in the foreground; wait for it */ | 881 | /* a job is running in the foreground; wait for it */ |
876 | i = 0; | 882 | i = 0; |
877 | while (!jobList.fg->progs[i].pid || | 883 | while (!jobList.fg->progs[i].pid || |
878 | jobList.fg->progs[i].isStopped) i++; | 884 | jobList.fg->progs[i].isStopped) i++; |
879 | 885 | ||
880 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); | 886 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); |
881 | 887 | ||
882 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | 888 | if (WIFEXITED(status) || WIFSIGNALED(status)) { |
883 | /* the child exited */ | 889 | /* the child exited */ |
884 | jobList.fg->runningProgs--; | 890 | jobList.fg->runningProgs--; |
885 | jobList.fg->progs[i].pid = 0; | 891 | jobList.fg->progs[i].pid = 0; |
886 | 892 | ||
887 | if (!jobList.fg->runningProgs) { | 893 | if (!jobList.fg->runningProgs) { |
888 | /* child exited */ | 894 | /* child exited */ |
889 | 895 | ||
890 | removeJob(&jobList, jobList.fg); | 896 | removeJob(&jobList, jobList.fg); |
891 | jobList.fg = NULL; | 897 | jobList.fg = NULL; |
892 | 898 | ||
893 | /* move the shell to the foreground */ | 899 | /* move the shell to the foreground */ |
894 | if (tcsetpgrp(0, getpid())) | 900 | if (tcsetpgrp(0, getpid())) |
895 | perror("tcsetpgrp"); | 901 | perror("tcsetpgrp"); |
896 | } | 902 | } |
897 | } else { | 903 | } else { |
898 | /* the child was stopped */ | 904 | /* the child was stopped */ |
899 | jobList.fg->stoppedProgs++; | 905 | jobList.fg->stoppedProgs++; |
900 | jobList.fg->progs[i].isStopped = 1; | 906 | jobList.fg->progs[i].isStopped = 1; |
901 | 907 | ||
902 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { | 908 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { |
903 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, | 909 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, |
904 | "Stopped", jobList.fg->text); | 910 | "Stopped", jobList.fg->text); |
905 | jobList.fg = NULL; | 911 | jobList.fg = NULL; |
906 | } | 912 | } |
907 | } | 913 | } |
908 | 914 | ||
909 | if (!jobList.fg) { | 915 | if (!jobList.fg) { |
910 | /* move the shell to the foreground */ | 916 | /* move the shell to the foreground */ |
911 | if (tcsetpgrp(0, getpid())) | 917 | if (tcsetpgrp(0, getpid())) |
912 | perror("tcsetpgrp"); | 918 | perror("tcsetpgrp"); |
913 | } | 919 | } |
914 | } | 920 | } |
915 | } | 921 | } |
916 | free( command); | 922 | free(command); |
917 | 923 | ||
918 | return 0; | 924 | return 0; |
919 | } | 925 | } |
920 | 926 | ||
921 | 927 | ||
922 | int shell_main(int argc, char **argv) | 928 | int shell_main(int argc, char **argv) |
923 | { | 929 | { |
924 | FILE *input = stdin; | 930 | FILE *input = stdin; |
925 | 931 | ||
926 | if (argc > 2) { | 932 | if (argc > 2) { |
927 | usage(shell_usage); | 933 | usage(shell_usage); |
928 | } | 934 | } |
929 | /* initialize the cwd */ | 935 | /* initialize the cwd */ |
930 | getcwd(cwd, sizeof(cwd)); | 936 | getcwd(cwd, sizeof(cwd)); |
931 | 937 | ||
932 | 938 | ||
933 | //if (argv[0] && argv[0][0] == '-') { | 939 | //if (argv[0] && argv[0][0] == '-') { |
934 | // shell_source("/etc/profile"); | 940 | // shell_source("/etc/profile"); |
935 | //} | 941 | //} |
936 | 942 | ||
937 | if (argc < 2) { | 943 | if (argc < 2) { |
938 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, | 944 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, |
939 | BB_BT); | 945 | BB_BT); |
940 | fprintf(stdout, | 946 | fprintf(stdout, |
941 | "Enter 'help' for a list of built-in commands.\n\n"); | 947 | "Enter 'help' for a list of built-in commands.\n\n"); |
942 | } else { | 948 | } else { |
943 | input = fopen(argv[1], "r"); | 949 | input = fopen(argv[1], "r"); |
944 | if (!input) | 950 | if (!input) |
945 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], | 951 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], |
946 | strerror(errno)); | 952 | strerror(errno)); |
947 | // else | 953 | // else |
948 | // fatalError("Got it.\n"); | 954 | // fatalError("Got it.\n"); |
949 | //exit(shell_source(argv[1])); | 955 | //exit(shell_source(argv[1])); |
950 | 956 | ||
951 | /* Set terminal IO to canonical mode, and save old term settings. */ | 957 | /* Set terminal IO to canonical mode, and save old term settings. */ |
952 | #ifdef BB_FEATURE_SH_COMMAND_EDITING | 958 | #ifdef BB_FEATURE_SH_COMMAND_EDITING |
953 | cmdedit_init(); | 959 | cmdedit_init(); |
954 | #endif | 960 | #endif |
955 | } | 961 | } |
956 | 962 | ||
957 | return (busy_loop(input)); | 963 | return (busy_loop(input)); |
958 | } | 964 | } |