05dab925442a96660c6c60e24f3e155bfeb6f7be
[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
725 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
726 {
727         struct job *job;
728         int i;
729         int nextin, nextout;
730         int pipefds[2];                         /* pipefd[0] is for reading */
731         struct builtInCommand *x;
732 #ifdef BB_FEATURE_STANDALONE_SHELL
733         const struct BB_applet *a = applets;
734 #endif
735
736
737         nextin = 0, nextout = 1;
738         for (i = 0; i < newJob.numProgs; i++) {
739                 if ((i + 1) < newJob.numProgs) {
740                         pipe(pipefds);
741                         nextout = pipefds[1];
742                 } else {
743                         nextout = 1;
744                 }
745
746                 if (!(newJob.progs[i].pid = fork())) {
747                         signal(SIGTTOU, SIG_DFL);
748
749                         if (nextin != 0) {
750                                 dup2(nextin, 0);
751                                 close(nextin);
752                         }
753
754                         if (nextout != 1) {
755                                 dup2(nextout, 1);
756                                 close(nextout);
757                         }
758
759                         /* explicit redirections override pipes */
760                         setupRedirections(newJob.progs + i);
761
762                         /* Match any built-ins here */
763                         for (x = bltins; x->cmd; x++) {
764                                 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
765                                         exit (x->function(&newJob, jobList));
766                                 }
767                         }
768 #ifdef BB_FEATURE_STANDALONE_SHELL
769                         /* Handle busybox internals here */
770                         while (a->name != 0) {
771                                 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
772                                         int argc;
773                                         char** argv=newJob.progs[i].argv;
774                                         for(argc=0;*argv!=NULL, argv++, argc++);
775                                         exit((*(a->main)) (argc, newJob.progs[i].argv));
776                                 }
777                                 a++;
778                         }
779 #endif
780
781                         execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
782                         fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
783                                            strerror(errno));
784                 }
785
786                 /* put our child in the process group whose leader is the
787                    first process in this pipe */
788                 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
789
790                 if (nextin != 0)
791                         close(nextin);
792                 if (nextout != 1)
793                         close(nextout);
794
795                 /* If there isn't another process, nextin is garbage 
796                    but it doesn't matter */
797                 nextin = pipefds[0];
798         }
799
800         newJob.pgrp = newJob.progs[0].pid;
801
802         /* find the ID for the job to use */
803         newJob.jobId = 1;
804         for (job = jobList->head; job; job = job->next)
805                 if (job->jobId >= newJob.jobId)
806                         newJob.jobId = job->jobId + 1;
807
808         /* add the job to the list of running jobs */
809         if (!jobList->head) {
810                 job = jobList->head = malloc(sizeof(*job));
811         } else {
812                 for (job = jobList->head; job->next; job = job->next);
813                 job->next = malloc(sizeof(*job));
814                 job = job->next;
815         }
816
817         *job = newJob;
818         job->next = NULL;
819         job->runningProgs = job->numProgs;
820         job->stoppedProgs = 0;
821
822         if (inBg) {
823                 /* we don't wait for background jobs to return -- append it 
824                    to the list of backgrounded jobs and leave it alone */
825                 printf("[%d] %d\n", job->jobId,
826                            newJob.progs[newJob.numProgs - 1].pid);
827         } else {
828                 jobList->fg = job;
829
830                 /* move the new process group into the foreground */
831                 if (tcsetpgrp(0, newJob.pgrp))
832                         perror("tcsetpgrp");
833         }
834
835         return 0;
836 }
837
838 static int setupRedirections(struct childProgram *prog)
839 {
840         int i;
841         int openfd;
842         int mode = O_RDONLY;
843         struct redirectionSpecifier *redir = prog->redirections;
844
845         for (i = 0; i < prog->numRedirections; i++, redir++) {
846                 switch (redir->type) {
847                 case REDIRECT_INPUT:
848                         mode = O_RDONLY;
849                         break;
850                 case REDIRECT_OVERWRITE:
851                         mode = O_RDWR | O_CREAT | O_TRUNC;
852                         break;
853                 case REDIRECT_APPEND:
854                         mode = O_RDWR | O_CREAT | O_APPEND;
855                         break;
856                 }
857
858                 openfd = open(redir->filename, mode, 0666);
859                 if (openfd < 0) {
860                         /* this could get lost if stderr has been redirected, but
861                            bash and ash both lose it as well (though zsh doesn't!) */
862                         fprintf(stderr, "error opening %s: %s\n", redir->filename,
863                                         strerror(errno));
864                         return 1;
865                 }
866
867                 if (openfd != redir->fd) {
868                         dup2(openfd, redir->fd);
869                         close(openfd);
870                 }
871         }
872
873         return 0;
874 }
875
876
877 static int busy_loop(FILE * input)
878 {
879         char *command;
880         char *nextCommand = NULL;
881         struct jobSet jobList = { NULL, NULL };
882         struct job newJob;
883         int i;
884         int status;
885         int inBg;
886
887         command = (char *) calloc(BUFSIZ, sizeof(char));
888
889         /* don't pay any attention to this signal; it just confuses 
890            things and isn't really meant for shells anyway */
891         signal(SIGTTOU, SIG_IGN);
892
893         while (1) {
894                 if (!jobList.fg) {
895                         /* no job is in the foreground */
896
897                         /* see if any background processes have exited */
898                         checkJobs(&jobList);
899
900                         if (!nextCommand) {
901                                 if (getCommand(input, command))
902                                         break;
903                                 nextCommand = command;
904                         }
905
906                         if (!parseCommand(&nextCommand, &newJob, &inBg) &&
907                                 newJob.numProgs) {
908                                 runCommand(newJob, &jobList, inBg);
909                         }
910                 } else {
911                         /* a job is running in the foreground; wait for it */
912                         i = 0;
913                         while (!jobList.fg->progs[i].pid ||
914                                    jobList.fg->progs[i].isStopped) i++;
915
916                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
917
918                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
919                                 /* the child exited */
920                                 jobList.fg->runningProgs--;
921                                 jobList.fg->progs[i].pid = 0;
922
923                                 if (!jobList.fg->runningProgs) {
924                                         /* child exited */
925
926                                         removeJob(&jobList, jobList.fg);
927                                         jobList.fg = NULL;
928
929                                         /* move the shell to the foreground */
930                                         if (tcsetpgrp(0, getpid()))
931                                                 perror("tcsetpgrp");
932                                 }
933                         } else {
934                                 /* the child was stopped */
935                                 jobList.fg->stoppedProgs++;
936                                 jobList.fg->progs[i].isStopped = 1;
937
938                                 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
939                                         printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
940                                                    "Stopped", jobList.fg->text);
941                                         jobList.fg = NULL;
942                                 }
943                         }
944
945                         if (!jobList.fg) {
946                                 /* move the shell to the foreground */
947                                 if (tcsetpgrp(0, getpid()))
948                                         perror("tcsetpgrp");
949                         }
950                 }
951         }
952         free(command);
953
954         return 0;
955 }
956
957
958 int shell_main(int argc, char **argv)
959 {
960         FILE *input = stdin;
961
962         if (argc > 2) {
963                 usage(shell_usage);
964         }
965         /* initialize the cwd */
966         getcwd(cwd, sizeof(cwd));
967
968 #ifdef BB_FEATURE_SH_COMMAND_EDITING
969         cmdedit_init();
970         signal(SIGWINCH, win_changed);
971         win_changed(0);
972 #endif
973
974         //if (argv[0] && argv[0][0] == '-') {
975         //      shell_source("/etc/profile");
976         //}
977
978         if (argc < 2) {
979                 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
980                 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
981         } else {
982                 if (*argv[1]=='-') {
983                         usage("sh\n\nlash -- the BusyBox LAme SHell (command interpreter)\n");
984                 }
985                 input = fopen(argv[1], "r");
986                 if (!input) {
987                         fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
988                                            strerror(errno));
989                 }
990         }
991
992         return (busy_loop(input));
993 }