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