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