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