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