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