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