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