Major build system updates...
[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 #ifdef BB_FEATURE_SH_COMMAND_EDITING
43 #include "cmdedit.h"
44 #endif
45
46 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
47
48
49 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
50         REDIRECT_APPEND };
51
52 struct jobSet {
53     struct job *head;           /* head of list of running jobs */
54     struct job *fg;             /* current foreground job */
55 };
56
57 struct redirectionSpecifier {
58     enum redirectionType type;  /* type of redirection */
59     int fd;                     /* file descriptor being redirected */
60     char *filename;             /* file to redirect fd to */
61 };
62
63 struct childProgram {
64     pid_t pid;                  /* 0 if exited */
65     char **argv;                /* program name and arguments */
66     int numRedirections;        /* elements in redirection array */
67     struct redirectionSpecifier *redirections;  /* I/O redirections */
68     glob_t globResult;          /* result of parameter globbing */
69     int freeGlob;               /* should we globfree(&globResult)? */
70     int isStopped;              /* is the program currently running? */
71 };
72
73 struct job {
74     int jobId;                  /* job number */
75     int numProgs;               /* total number of programs in job */
76     int runningProgs;           /* number of programs running */
77     char *text;                 /* name of job */
78     char *cmdBuf;               /* buffer various argv's point into */
79     pid_t pgrp;                 /* process group ID for the job */
80     struct childProgram *progs; /* array of programs in job */
81     struct job *next;           /* to track background commands */
82     int stoppedProgs;           /* number of programs alive, but stopped */
83 };
84
85 struct builtInCommand {
86     char *cmd;                  /* name */
87     char *descr;                /* description */
88     char *usage;                /* usage */
89     int (*function) (struct job *, struct jobSet * jobList);    /* function ptr */
90 };
91
92 /* Some function prototypes */
93 static int shell_cd(struct job *cmd, struct jobSet *junk);
94 static int shell_env(struct job *dummy, struct jobSet *junk);
95 static int shell_exit(struct job *cmd, struct jobSet *junk);
96 static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97 static int shell_help(struct job *cmd, struct jobSet *junk);
98 static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99 static int shell_pwd(struct job *dummy, struct jobSet *junk);
100 static int shell_set(struct job *cmd, struct jobSet *junk);
101 static int shell_source(struct job *cmd, struct jobSet *jobList);
102 static int shell_unset(struct job *cmd, struct jobSet *junk);
103
104 static void checkJobs(struct jobSet *jobList);
105 static int getCommand(FILE * source, char *command);
106 static int parseCommand(char **commandPtr, struct job *job, int *isBg);
107 static int setupRedirections(struct childProgram *prog);
108 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
109 static int busy_loop(FILE * input);
110
111
112 /* Table of built-in functions */
113 static struct builtInCommand bltins[] = {
114     {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
115     {"cd", "Change working directory", "cd [dir]", shell_cd},
116     //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
117     {"env", "Print all environment variables", "env", shell_env},
118     {"exit", "Exit from shell()", "exit", shell_exit},
119     {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
120     {"jobs", "Lists the active jobs", "jobs", shell_jobs},
121     {"pwd", "Print current directory", "pwd", shell_pwd},
122     {"set", "Set environment variable", "set [VAR=value]", shell_set},
123     {"unset", "Unset environment variable", "unset VAR", shell_unset},
124         {".", "Source-in and run commands in a file", ". filename",
125      shell_source},
126     {"help", "List shell built-in commands", "help", shell_help},
127     {NULL, NULL, NULL, NULL}
128 };
129
130 static const char shell_usage[] =
131     "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
132
133
134 static char cwd[1024];
135 static char *prompt = "# ";
136
137
138
139 /* built-in 'cd <path>' handler */
140 static int shell_cd(struct job *cmd, struct jobSet *junk)
141 {
142     char *newdir;
143     if (!cmd->progs[0].argv[1] == 1)
144         newdir = getenv("HOME");
145     else
146         newdir = cmd->progs[0].argv[1];
147     if (chdir(newdir)) {
148         printf("cd: %s: %s\n", newdir, strerror(errno));
149         return FALSE;
150     }
151     getcwd(cwd, sizeof(cwd));
152
153     return TRUE;
154 }
155
156 /* built-in 'env' handler */
157 static int shell_env(struct job *dummy, struct jobSet *junk)
158 {
159     char **e;
160
161     for (e = environ; *e; e++) {
162         fprintf(stdout, "%s\n", *e);
163     }
164     return (0);
165 }
166
167 /* built-in 'exit' handler */
168 static int shell_exit(struct job *cmd, struct jobSet *junk)
169 {
170     if (!cmd->progs[0].argv[1] == 1)
171         exit TRUE;
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;
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         }
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
207     if (*cmd->progs[0].argv[0] == 'f') {
208         /* Make this job the foreground job */
209
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     for (job = jobList->head; job; job = job->next) {
246         if (job->runningProgs == job->stoppedProgs)
247             statusString = "Stopped";
248         else
249             statusString = "Running";
250
251         printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
252     }
253     return TRUE;
254 }
255
256
257 /* built-in 'pwd' handler */
258 static int shell_pwd(struct job *dummy, struct jobSet *junk)
259 {
260     getcwd(cwd, sizeof(cwd));
261     fprintf(stdout, "%s\n", cwd);
262     return TRUE;
263 }
264
265 /* built-in 'set VAR=value' handler */
266 static int shell_set(struct job *cmd, struct jobSet *junk)
267 {
268     int res;
269
270     if (!cmd->progs[0].argv[1] == 1) {
271         return (shell_env(cmd, junk));
272     }
273     res = putenv(cmd->progs[0].argv[1]);
274     if (res)
275         fprintf(stdout, "set: %s\n", strerror(errno));
276     return (res);
277 }
278
279 /* Built-in '.' handler (read-in and execute commands from file) */
280 static int shell_source(struct job *cmd, struct jobSet *junk)
281 {
282     FILE *input;
283     int status;
284
285     if (!cmd->progs[0].argv[1] == 1)
286         return FALSE;
287
288     input = fopen(cmd->progs[0].argv[1], "r");
289     if (!input) {
290         fprintf(stdout, "Couldn't open file '%s'\n",
291                 cmd->progs[0].argv[1]);
292         return FALSE;
293     }
294
295     /* Now run the file */
296     status = busy_loop(input);
297     return (status);
298 }
299
300 /* built-in 'unset VAR' handler */
301 static int shell_unset(struct job *cmd, struct jobSet *junk)
302 {
303     if (!cmd->progs[0].argv[1] == 1) {
304         fprintf(stdout, "unset: parameter required.\n");
305         return FALSE;
306     }
307     unsetenv(cmd->progs[0].argv[1]);
308     return TRUE;
309 }
310
311 /* free up all memory from a job */
312 static void freeJob(struct job *cmd)
313 {
314     int i;
315
316     for (i = 0; i < cmd->numProgs; i++) {
317         free(cmd->progs[i].argv);
318         if (cmd->progs[i].redirections)
319             free(cmd->progs[i].redirections);
320         if (cmd->progs[i].freeGlob)
321             globfree(&cmd->progs[i].globResult);
322     }
323     free(cmd->progs);
324     if (cmd->text)
325         free(cmd->text);
326     free(cmd->cmdBuf);
327 }
328
329 /* remove a job from the jobList */
330 static void removeJob(struct jobSet *jobList, struct job *job)
331 {
332     struct job *prevJob;
333
334     freeJob(job);
335     if (job == jobList->head) {
336         jobList->head = job->next;
337     } else {
338         prevJob = jobList->head;
339         while (prevJob->next != job)
340             prevJob = prevJob->next;
341         prevJob->next = job->next;
342     }
343
344     free(job);
345 }
346
347 /* Checks to see if any background processes have exited -- if they 
348    have, figure out why and see if a job has completed */
349 static void checkJobs(struct jobSet *jobList)
350 {
351     struct job *job;
352     pid_t childpid;
353     int status;
354     int progNum = 0;
355
356     while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
357         for (job = jobList->head; job; job = job->next) {
358             progNum = 0;
359             while (progNum < job->numProgs &&
360                    job->progs[progNum].pid != childpid) progNum++;
361             if (progNum < job->numProgs)
362                 break;
363         }
364
365         if (WIFEXITED(status) || WIFSIGNALED(status)) {
366             /* child exited */
367             job->runningProgs--;
368             job->progs[progNum].pid = 0;
369
370             if (!job->runningProgs) {
371                 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
372                 removeJob(jobList, job);
373             }
374         } else {
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         }
384     }
385
386     if (childpid == -1 && errno != ECHILD)
387         perror("waitpid");
388 }
389
390 static int getCommand(FILE * source, char *command)
391 {
392     if (source == stdin) {
393         fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
394         fflush(stdout);
395 #ifdef BB_FEATURE_SH_COMMAND_EDITING
396         cmdedit_read_input(fileno(stdin), fileno(stdout), command);
397         return 0;
398 #endif
399     }
400
401     if (!fgets(command, BUFSIZ - 2, source)) {
402         if (source == stdin)
403             printf("\n");
404         return 1;
405     }
406
407     /* remove trailing newline */
408     command[strlen(command) - 1] = '\0';
409
410     return 0;
411 }
412
413 static void globLastArgument(struct childProgram *prog, int *argcPtr,
414                              int *argcAllocedPtr)
415 {
416     int argc = *argcPtr;
417     int argcAlloced = *argcAllocedPtr;
418     int rc;
419     int flags;
420     int i;
421     char *src, *dst;
422
423     if (argc > 1) {             /* cmd->globResult is already initialized */
424         flags = GLOB_APPEND;
425         i = prog->globResult.gl_pathc;
426     } else {
427         prog->freeGlob = 1;
428         flags = 0;
429         i = 0;
430     }
431
432     rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
433     if (rc == GLOB_NOSPACE) {
434         fprintf(stderr, "out of space during glob operation\n");
435         return;
436     } else if (rc == GLOB_NOMATCH ||
437                (!rc && (prog->globResult.gl_pathc - i) == 1 &&
438                 !strcmp(prog->argv[argc - 1],
439                         prog->globResult.gl_pathv[i]))) {
440         /* we need to remove whatever \ quoting is still present */
441         src = dst = prog->argv[argc - 1];
442         while (*src) {
443             if (*src != '\\')
444                 *dst++ = *src;
445             src++;
446         }
447         *dst = '\0';
448     } else if (!rc) {
449         argcAlloced += (prog->globResult.gl_pathc - i);
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 }
460
461 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
462    line). If a valid command is found, commandPtr is set to point to
463    the beginning of the next command (if the original command had more 
464    then one job associated with it) or NULL if no more commands are 
465    present. */
466 static int parseCommand(char **commandPtr, struct job *job, int *isBg)
467 {
468     char *command;
469     char *returnCommand = NULL;
470     char *src, *buf, *chptr;
471     int argc = 0;
472     int done = 0;
473     int argvAlloced;
474     int i;
475     char quote = '\0';
476     int count;
477     struct childProgram *prog;
478
479     /* skip leading white space */
480     while (**commandPtr && isspace(**commandPtr))
481         (*commandPtr)++;
482
483     /* this handles empty lines or leading '#' characters */
484     if (!**commandPtr || (**commandPtr == '#')) {
485         job->numProgs = 0;
486         *commandPtr = NULL;
487         return 0;
488     }
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
527                 /* in shell, "\'" should yield \' */
528                 if (*src != quote)
529                     *buf++ = '\\';
530             } else if (*src == '*' || *src == '?' || *src == '[' ||
531                        *src == ']') *buf++ = '\\';
532             *buf++ = *src;
533         } else if (isspace(*src)) {
534             if (*prog->argv[argc]) {
535                 buf++, argc++;
536                 /* +1 here leaves room for the NULL which ends argv */
537                 if ((argc + 1) == argvAlloced) {
538                     argvAlloced += 5;
539                     prog->argv = realloc(prog->argv,
540                                          sizeof(*prog->argv) *
541                                          argvAlloced);
542                 }
543                 prog->argv[argc] = buf;
544
545                 globLastArgument(prog, &argc, &argvAlloced);
546             }
547         } else
548             switch (*src) {
549             case '"':
550             case '\'':
551                 quote = *src;
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
578                 if (prog->redirections[i].fd == -1) {
579                     if (*src == '>')
580                         prog->redirections[i].fd = 1;
581                     else
582                         prog->redirections[i].fd = 0;
583                 }
584
585                 if (*src++ == '>') {
586                     if (*src == '>')
587                         prog->redirections[i].type =
588                             REDIRECT_APPEND, src++;
589                     else
590                         prog->redirections[i].type = REDIRECT_OVERWRITE;
591                 } else {
592                     prog->redirections[i].type = REDIRECT_INPUT;
593                 }
594
595                 /* This isn't POSIX sh compliant. Oh well. */
596                 chptr = src;
597                 while (isspace(*chptr))
598                     chptr++;
599
600                 if (!*chptr) {
601                     fprintf(stderr, "file name expected after %c\n", *src);
602                     freeJob(job);
603                     return 1;
604                 }
605
606                 prog->redirections[i].filename = buf;
607                 while (*chptr && !isspace(*chptr))
608                     *buf++ = *chptr++;
609
610                 src = chptr - 1;        /* we src++ later */
611                 prog->argv[argc] = ++buf;
612                 break;
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                 }
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
639                 src++;
640                 while (*src && isspace(*src))
641                     src++;
642
643                 if (!*src) {
644                     fprintf(stderr, "empty command in pipe\n");
645                     return 1;
646                 }
647                 src--;          /* we'll ++ it at the end of the loop */
648
649                 break;
650
651             case '&':           /* background */
652                 *isBg = 1;
653             case ';':           /* multiple commands */
654                 done = 1;
655                 returnCommand = *commandPtr + (src - *commandPtr) + 1;
656                 break;
657
658             case '\\':
659                 src++;
660                 if (!*src) {
661                     freeJob(job);
662                     fprintf(stderr, "character expected after \\\n");
663                     return 1;
664                 }
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
685     if (!returnCommand) {
686         job->text = malloc(strlen(*commandPtr) + 1);
687         strcpy(job->text, *commandPtr);
688     } else {
689         /* This leaves any trailing spaces, which is a bit sloppy */
690
691         count = returnCommand - *commandPtr;
692         job->text = malloc(count + 1);
693         strncpy(job->text, *commandPtr, count);
694         job->text[count] = '\0';
695     }
696
697     *commandPtr = returnCommand;
698
699     return 0;
700 }
701
702 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
703 {
704     struct job *job;
705     int i;
706     int nextin, nextout;
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
718     nextin = 0, nextout = 1;
719     for (i = 0; i < newJob.numProgs; i++) {
720         if ((i + 1) < newJob.numProgs) {
721             pipe(pipefds);
722             nextout = pipefds[1];
723         } else {
724             nextout = 1;
725         }
726
727         if (!(newJob.progs[i].pid = fork())) {
728             signal(SIGTTOU, SIG_DFL);
729
730             if (nextin != 0) {
731                 dup2(nextin, 0);
732                 close(nextin);
733             }
734
735             if (nextout != 1) {
736                 dup2(nextout, 1);
737                 close(nextout);
738             }
739
740             /* explicit redirections override pipes */
741             setupRedirections(newJob.progs + i);
742
743             execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
744             fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
745                        strerror(errno));
746         }
747
748         /* put our child in the process group whose leader is the
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 }
801
802 static int setupRedirections(struct childProgram *prog)
803 {
804     int i;
805     int openfd;
806     int mode = O_RDONLY;
807     struct redirectionSpecifier *redir = prog->redirections;
808
809     for (i = 0; i < prog->numRedirections; i++, redir++) {
810         switch (redir->type) {
811         case REDIRECT_INPUT:
812             mode = O_RDONLY;
813             break;
814         case REDIRECT_OVERWRITE:
815             mode = O_RDWR | O_CREAT | O_TRUNC;
816             break;
817         case REDIRECT_APPEND:
818             mode = O_RDWR | O_CREAT | O_APPEND;
819             break;
820         }
821
822         openfd = open(redir->filename, mode, 0666);
823         if (openfd < 0) {
824             /* this could get lost if stderr has been redirected, but
825                bash and ash both lose it as well (though zsh doesn't!) */
826             fprintf(stderr, "error opening %s: %s\n", redir->filename,
827                     strerror(errno));
828             return 1;
829         }
830
831         if (openfd != redir->fd) {
832             dup2(openfd, redir->fd);
833             close(openfd);
834         }
835     }
836
837     return 0;
838 }
839
840
841 static int busy_loop(FILE * input)
842 {
843     char *command;
844     char *nextCommand = NULL;
845     struct jobSet jobList = { NULL, NULL };
846     struct job newJob;
847     int i;
848     int status;
849     int inBg;
850
851     command = (char*) calloc(BUFSIZ, sizeof(char));
852
853     /* don't pay any attention to this signal; it just confuses 
854        things and isn't really meant for shells anyway */
855     signal(SIGTTOU, SIG_IGN);
856
857     while (1) {
858                 if (!jobList.fg) {
859                         /* no job is in the foreground */
860
861                         /* see if any background processes have exited */
862                         checkJobs(&jobList);
863
864                         if (!nextCommand) {
865                         if (getCommand(input, command))
866                                 break;
867                         nextCommand = command;
868                         }
869
870                         if (!parseCommand(&nextCommand, &newJob, &inBg) &&
871                         newJob.numProgs) {
872                         runCommand(newJob, &jobList, inBg);
873                         }
874                 } else {
875                         /* a job is running in the foreground; wait for it */
876                         i = 0;
877                         while (!jobList.fg->progs[i].pid ||
878                            jobList.fg->progs[i].isStopped) i++;
879
880                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
881
882                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
883                         /* the child exited */
884                         jobList.fg->runningProgs--;
885                         jobList.fg->progs[i].pid = 0;
886
887                         if (!jobList.fg->runningProgs) {
888                                 /* child exited */
889
890                                 removeJob(&jobList, jobList.fg);
891                                 jobList.fg = NULL;
892
893                                 /* move the shell to the foreground */
894                                 if (tcsetpgrp(0, getpid()))
895                                 perror("tcsetpgrp");
896                         }
897                         } else {
898                         /* the child was stopped */
899                         jobList.fg->stoppedProgs++;
900                         jobList.fg->progs[i].isStopped = 1;
901
902                         if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
903                                 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
904                                    "Stopped", jobList.fg->text);
905                                 jobList.fg = NULL;
906                         }
907                         }
908
909                         if (!jobList.fg) {
910                         /* move the shell to the foreground */
911                         if (tcsetpgrp(0, getpid()))
912                                 perror("tcsetpgrp");
913                         }
914                 }
915         }
916         free( command);
917
918     return 0;
919 }
920
921
922 int shell_main(int argc, char **argv)
923 {
924     FILE *input = stdin;
925
926     if (argc > 2) {
927         usage(shell_usage);
928     }
929     /* initialize the cwd */
930     getcwd(cwd, sizeof(cwd));
931
932
933     //if (argv[0] && argv[0][0] == '-') {
934     //      shell_source("/etc/profile");
935     //}
936
937     if (argc < 2) {
938         fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
939                 BB_BT);
940         fprintf(stdout,
941                 "Enter 'help' for a list of built-in commands.\n\n");
942     } else {
943         input = fopen(argv[1], "r");
944         if (!input)
945             fatalError("A: Couldn't open file '%s': %s\n", argv[1],
946                        strerror(errno));
947 //              else
948 //                      fatalError("Got it.\n");
949         //exit(shell_source(argv[1]));
950
951         /* Set terminal IO to canonical mode, and save old term settings. */
952 #ifdef BB_FEATURE_SH_COMMAND_EDITING
953         cmdedit_init();
954 #endif
955     }
956
957     return (busy_loop(input));
958 }