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