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