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