d829b6b29e932eb5e8d319dd7dbcf794338aa393
[oweals/busybox.git] / lash.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * lash -- the BusyBox Lame-Ass SHell
4  *
5  * Copyright (C) 2000 by Lineo, inc.
6  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7  *
8  * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9  * under the following liberal license: "We have placed this source code in the
10  * public domain. Use it in any project, free or commercial."
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  *
26  */
27
28 #include "internal.h"
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <glob.h>
35 #include <signal.h>
36 #include <string.h>
37 #include <sys/ioctl.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40 #ifdef BB_FEATURE_SH_COMMAND_EDITING
41 #include "cmdedit.h"
42 #endif
43
44 #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, struct jobSet *jobList, 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 junk)
154 {
155         struct winsize win = { 0, 0, 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                         errorMsg("%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                         errorMsg("%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                 errorMsg("%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 *dummy, 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         memset(cmd, 0, sizeof(struct job));
393 }
394
395 /* remove a job from the jobList */
396 static void removeJob(struct jobSet *jobList, struct job *job)
397 {
398         struct job *prevJob;
399
400         freeJob(job);
401         if (job == jobList->head) {
402                 jobList->head = job->next;
403         } else {
404                 prevJob = jobList->head;
405                 while (prevJob->next != job)
406                         prevJob = prevJob->next;
407                 prevJob->next = job->next;
408         }
409
410         free(job);
411 }
412
413 /* Checks to see if any background processes have exited -- if they 
414    have, figure out why and see if a job has completed */
415 static void checkJobs(struct jobSet *jobList)
416 {
417         struct job *job;
418         pid_t childpid;
419         int status;
420         int progNum = 0;
421
422         while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
423                 for (job = jobList->head; job; job = job->next) {
424                         progNum = 0;
425                         while (progNum < job->numProgs &&
426                                    job->progs[progNum].pid != childpid) progNum++;
427                         if (progNum < job->numProgs)
428                                 break;
429                 }
430
431                 if (WIFEXITED(status) || WIFSIGNALED(status)) {
432                         /* child exited */
433                         job->runningProgs--;
434                         job->progs[progNum].pid = 0;
435
436                         if (!job->runningProgs) {
437                                 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
438                                 removeJob(jobList, job);
439                         }
440                 } else {
441                         /* child stopped */
442                         job->stoppedProgs++;
443                         job->progs[progNum].isStopped = 1;
444
445                         if (job->stoppedProgs == job->numProgs) {
446                                 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
447                                            job->text);
448                         }
449                 }
450         }
451
452         if (childpid == -1 && errno != ECHILD)
453                 perror("waitpid");
454 }
455
456 static int getCommand(FILE * source, char *command)
457 {
458         if (source == NULL) {
459                 if (local_pending_command) {
460                         /* a command specified (-c option): return it & mark it done */
461                         strcpy(command, local_pending_command);
462                         free(local_pending_command);
463                         local_pending_command = NULL;
464                         return 0;
465                 }
466                 return 1;
467         }
468
469         if (source == stdin) {
470 #ifdef BB_FEATURE_SH_COMMAND_EDITING
471                 int len;
472                 char *promptStr;
473                 len=fprintf(stdout, "%s %s", cwd, prompt);
474                 fflush(stdout);
475                 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
476                 sprintf(promptStr, "%s %s", cwd, prompt);
477                 cmdedit_read_input(promptStr, command);
478                 free( promptStr);
479                 return 0;
480 #else
481                 fprintf(stdout, "%s %s", cwd, prompt);
482                 fflush(stdout);
483 #endif
484         }
485
486         if (!fgets(command, BUFSIZ - 2, source)) {
487                 if (source == stdin)
488                         printf("\n");
489                 return 1;
490         }
491
492         /* remove trailing newline */
493         command[strlen(command) - 1] = '\0';
494
495         return 0;
496 }
497
498 static void globLastArgument(struct childProgram *prog, int *argcPtr,
499                                                          int *argcAllocedPtr)
500 {
501         int argc = *argcPtr;
502         int argcAlloced = *argcAllocedPtr;
503         int rc;
504         int flags;
505         int i;
506         char *src, *dst, *var;
507
508         if (argc > 1) {                         /* cmd->globResult is already initialized */
509                 flags = GLOB_APPEND;
510                 i = prog->globResult.gl_pathc;
511         } else {
512                 prog->freeGlob = 1;
513                 flags = 0;
514                 i = 0;
515         }
516         /* do shell variable substitution */
517         if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
518                 prog->argv[argc - 1] = var;
519
520         rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
521         if (rc == GLOB_NOSPACE) {
522                 errorMsg("out of space during glob operation\n");
523                 return;
524         } else if (rc == GLOB_NOMATCH ||
525                            (!rc && (prog->globResult.gl_pathc - i) == 1 &&
526                                 !strcmp(prog->argv[argc - 1],
527                                                 prog->globResult.gl_pathv[i]))) {
528                 /* we need to remove whatever \ quoting is still present */
529                 src = dst = prog->argv[argc - 1];
530                 while (*src) {
531                         if (*src != '\\')
532                                 *dst++ = *src;
533                         src++;
534                 }
535                 *dst = '\0';
536         } else if (!rc) {
537                 argcAlloced += (prog->globResult.gl_pathc - i);
538                 prog->argv =
539                         realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
540                 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
541                            sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
542                 argc += (prog->globResult.gl_pathc - i - 1);
543         }
544
545         *argcAllocedPtr = argcAlloced;
546         *argcPtr = argc;
547 }
548
549 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
550    line). If a valid command is found, commandPtr is set to point to
551    the beginning of the next command (if the original command had more 
552    then one job associated with it) or NULL if no more commands are 
553    present. */
554 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
555 {
556         char *command;
557         char *returnCommand = NULL;
558         char *src, *buf, *chptr;
559         int argc = 0;
560         int done = 0;
561         int argvAlloced;
562         int i;
563         char quote = '\0';
564         int count;
565         struct childProgram *prog;
566
567         /* skip leading white space */
568         while (**commandPtr && isspace(**commandPtr))
569                 (*commandPtr)++;
570
571         /* this handles empty lines or leading '#' characters */
572         if (!**commandPtr || (**commandPtr == '#')) {
573                 job->numProgs=0;
574                 return 0;
575         }
576
577         *isBg = 0;
578         job->numProgs = 1;
579         job->progs = xmalloc(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 = xmalloc(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                                         errorMsg("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                                         errorMsg("file name expected after %c\n", *src);
690                                         freeJob(job);
691                                         job->numProgs=0;
692                                         return 1;
693                                 }
694
695                                 prog->redirections[i].filename = buf;
696                                 while (*chptr && !isspace(*chptr))
697                                         *buf++ = *chptr++;
698
699                                 src = chptr - 1;        /* we src++ later */
700                                 prog->argv[argc] = ++buf;
701                                 break;
702
703                         case '|':                       /* pipe */
704                                 /* finish this command */
705                                 if (*prog->argv[argc])
706                                         argc++;
707                                 if (!argc) {
708                                         errorMsg("empty command in pipe1\n");
709                                         freeJob(job);
710                                         job->numProgs=0;
711                                         return 1;
712                                 }
713                                 prog->argv[argc] = NULL;
714
715                                 /* and start the next */
716                                 job->numProgs++;
717                                 job->progs = realloc(job->progs,
718                                                                          sizeof(*job->progs) * job->numProgs);
719                                 prog = job->progs + (job->numProgs - 1);
720                                 prog->numRedirections = 0;
721                                 prog->redirections = NULL;
722                                 prog->freeGlob = 0;
723                                 argc = 0;
724
725                                 argvAlloced = 5;
726                                 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
727                                 prog->argv[0] = ++buf;
728
729                                 src++;
730                                 while (*src && isspace(*src))
731                                         src++;
732
733                                 if (!*src) {
734                                         errorMsg("empty command in pipe2\n");
735                                         freeJob(job);
736                                         job->numProgs=0;
737                                         return 1;
738                                 }
739                                 src--;                  /* we'll ++ it at the end of the loop */
740
741                                 break;
742
743                         case '&':                       /* background */
744                                 *isBg = 1;
745                         case ';':                       /* multiple commands */
746                                 done = 1;
747                                 returnCommand = *commandPtr + (src - *commandPtr) + 1;
748                                 break;
749
750                         case '\\':
751                                 src++;
752                                 if (!*src) {
753                                         errorMsg("character expected after \\\n");
754                                         freeJob(job);
755                                         return 1;
756                                 }
757                                 if (*src == '*' || *src == '[' || *src == ']'
758                                         || *src == '?') *buf++ = '\\';
759                                 /* fallthrough */
760                         case '`':
761                                 /* Exec a backtick-ed command */
762                                 {
763                                         char* newcmd=NULL;
764                                         char* ptr=NULL;
765                                         struct job newJob;
766
767                                         ptr=strchr(++src, '`');
768                                         if (ptr==NULL) {
769                                                 fprintf(stderr, "Unmatched '`' in command\n");
770                                                 freeJob(job);
771                                                 return 1;
772                                         }
773
774                                         newcmd = xmalloc(1+ptr-src);
775                                         snprintf(newcmd, 1+ptr-src, src);
776
777                                         if (!parseCommand(&newcmd, &newJob, jobList, isBg) &&
778                                                         newJob.numProgs) {
779                                                 runCommand(&newJob, jobList, *isBg);
780                                         }
781
782                                         /* Clip out the the backticked command from the string */
783                                         memmove(--src, ptr, strlen(ptr)+1);
784                                         free(newcmd);
785                                 }
786                                 break;
787                         default:
788                                 *buf++ = *src;
789                         }
790
791                 src++;
792         }
793
794         if (*prog->argv[argc]) {
795                 argc++;
796                 globLastArgument(prog, &argc, &argvAlloced);
797         }
798         if (!argc) {
799                 freeJob(job);
800                 return 0;
801         }
802         prog->argv[argc] = NULL;
803
804         if (!returnCommand) {
805                 job->text = xmalloc(strlen(*commandPtr) + 1);
806                 strcpy(job->text, *commandPtr);
807         } else {
808                 /* This leaves any trailing spaces, which is a bit sloppy */
809                 count = returnCommand - *commandPtr;
810                 job->text = xmalloc(count + 1);
811                 strncpy(job->text, *commandPtr, count);
812                 job->text[count] = '\0';
813         }
814
815         *commandPtr = returnCommand;
816
817         return 0;
818 }
819
820
821 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg)
822 {
823         struct job *job;
824         int i;
825         int nextin, nextout;
826         int pipefds[2];                         /* pipefd[0] is for reading */
827         struct builtInCommand *x;
828 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
829         const struct BB_applet *a = applets;
830 #endif
831
832
833         nextin = 0, nextout = 1;
834         for (i = 0; i < newJob->numProgs; i++) {
835                 if ((i + 1) < newJob->numProgs) {
836                         pipe(pipefds);
837                         nextout = pipefds[1];
838                 } else {
839                         nextout = 1;
840                 }
841
842                 /* Check if the command matches any non-forking builtins */
843                 for (x = bltins; x->cmd; x++) {
844                         if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
845                                 return (x->function(newJob, jobList));
846                         }
847                 }
848
849                 if (!(newJob->progs[i].pid = fork())) {
850                         signal(SIGTTOU, SIG_DFL);
851
852                         if (nextin != 0) {
853                                 dup2(nextin, 0);
854                                 close(nextin);
855                         }
856
857                         if (nextout != 1) {
858                                 dup2(nextout, 1);
859                                 close(nextout);
860                         }
861
862                         /* explicit redirections override pipes */
863                         setupRedirections(newJob->progs + i);
864
865                         /* Check if the command matches any of the other builtins */
866                         for (x = bltins_forking; x->cmd; x++) {
867                                 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
868                                         exit (x->function(newJob, jobList));
869                                 }
870                         }
871 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
872                         /* Check if the command matches any busybox internal commands here */
873                         /* TODO: Add matching when paths are appended (i.e. 'cat' currently
874                          * works, but '/bin/cat' doesn't ) */
875                         while (a->name != 0) {
876                                 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
877                                         int argc;
878                                         char** argv=newJob->progs[i].argv;
879                                         for(argc=0;*argv!=NULL; argv++, argc++);
880                                         exit((*(a->main)) (argc, newJob->progs[i].argv));
881                                 }
882                                 a++;
883                         }
884 #endif
885
886                         execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
887                         fatalError("%s: %s\n", newJob->progs[i].argv[0],
888                                            strerror(errno));
889                 }
890
891                 /* put our child in the process group whose leader is the
892                    first process in this pipe */
893                 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
894
895                 if (nextin != 0)
896                         close(nextin);
897                 if (nextout != 1)
898                         close(nextout);
899
900                 /* If there isn't another process, nextin is garbage 
901                    but it doesn't matter */
902                 nextin = pipefds[0];
903         }
904
905         newJob->pgrp = newJob->progs[0].pid;
906
907         /* find the ID for the job to use */
908         newJob->jobId = 1;
909         for (job = jobList->head; job; job = job->next)
910                 if (job->jobId >= newJob->jobId)
911                         newJob->jobId = job->jobId + 1;
912
913         /* add the job to the list of running jobs */
914         if (!jobList->head) {
915                 job = jobList->head = xmalloc(sizeof(*job));
916         } else {
917                 for (job = jobList->head; job->next; job = job->next);
918                 job->next = xmalloc(sizeof(*job));
919                 job = job->next;
920         }
921
922         *job = *newJob;
923         job->next = NULL;
924         job->runningProgs = job->numProgs;
925         job->stoppedProgs = 0;
926
927         if (inBg) {
928                 /* we don't wait for background jobs to return -- append it 
929                    to the list of backgrounded jobs and leave it alone */
930                 printf("[%d] %d\n", job->jobId,
931                            newJob->progs[newJob->numProgs - 1].pid);
932         } else {
933                 jobList->fg = job;
934
935                 /* move the new process group into the foreground */
936                 /* suppress messages when run from /linuxrc mag@sysgo.de */
937                 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
938                         perror("tcsetpgrp");
939         }
940
941         return 0;
942 }
943
944 static int setupRedirections(struct childProgram *prog)
945 {
946         int i;
947         int openfd;
948         int mode = O_RDONLY;
949         struct redirectionSpecifier *redir = prog->redirections;
950
951         for (i = 0; i < prog->numRedirections; i++, redir++) {
952                 switch (redir->type) {
953                 case REDIRECT_INPUT:
954                         mode = O_RDONLY;
955                         break;
956                 case REDIRECT_OVERWRITE:
957                         mode = O_RDWR | O_CREAT | O_TRUNC;
958                         break;
959                 case REDIRECT_APPEND:
960                         mode = O_RDWR | O_CREAT | O_APPEND;
961                         break;
962                 }
963
964                 openfd = open(redir->filename, mode, 0666);
965                 if (openfd < 0) {
966                         /* this could get lost if stderr has been redirected, but
967                            bash and ash both lose it as well (though zsh doesn't!) */
968                         errorMsg("error opening %s: %s\n", redir->filename,
969                                         strerror(errno));
970                         return 1;
971                 }
972
973                 if (openfd != redir->fd) {
974                         dup2(openfd, redir->fd);
975                         close(openfd);
976                 }
977         }
978
979         return 0;
980 }
981
982
983 static int busy_loop(FILE * input)
984 {
985         char *command;
986         char *nextCommand = NULL;
987         struct jobSet jobList = { NULL, NULL };
988         struct job newJob;
989         pid_t  parent_pgrp;
990         int i;
991         int status;
992         int inBg;
993
994         /* save current owner of TTY so we can restore it on exit */
995         parent_pgrp = tcgetpgrp(0);
996
997         command = (char *) calloc(BUFSIZ, sizeof(char));
998
999         /* don't pay any attention to this signal; it just confuses 
1000            things and isn't really meant for shells anyway */
1001         signal(SIGTTOU, SIG_IGN);
1002
1003         while (1) {
1004                 if (!jobList.fg) {
1005                         /* no job is in the foreground */
1006
1007                         /* see if any background processes have exited */
1008                         checkJobs(&jobList);
1009
1010                         if (!nextCommand) {
1011                                 if (getCommand(input, command))
1012                                         break;
1013                                 nextCommand = command;
1014                         }
1015
1016                         if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1017                                 newJob.numProgs) {
1018                                 runCommand(&newJob, &jobList, inBg);
1019                         } else {
1020                                 nextCommand=NULL;
1021                         }
1022                 } else {
1023                         /* a job is running in the foreground; wait for it */
1024                         i = 0;
1025                         while (!jobList.fg->progs[i].pid ||
1026                                    jobList.fg->progs[i].isStopped) i++;
1027
1028                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1029
1030                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
1031                                 /* the child exited */
1032                                 jobList.fg->runningProgs--;
1033                                 jobList.fg->progs[i].pid = 0;
1034
1035                                 if (!jobList.fg->runningProgs) {
1036                                         /* child exited */
1037
1038                                         removeJob(&jobList, jobList.fg);
1039                                         jobList.fg = NULL;
1040                                 }
1041                         } else {
1042                                 /* the child was stopped */
1043                                 jobList.fg->stoppedProgs++;
1044                                 jobList.fg->progs[i].isStopped = 1;
1045
1046                                 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1047                                         printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1048                                                    "Stopped", jobList.fg->text);
1049                                         jobList.fg = NULL;
1050                                 }
1051                         }
1052
1053                         if (!jobList.fg) {
1054                                 /* move the shell to the foreground */
1055                                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1056                                 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1057                                         perror("tcsetpgrp"); 
1058                         }
1059                 }
1060         }
1061         free(command);
1062
1063         /* return controlling TTY back to parent process group before exiting */
1064         if (tcsetpgrp(0, parent_pgrp))
1065                 perror("tcsetpgrp");
1066
1067         /* return exit status if called with "-c" */
1068         if (input == NULL && WIFEXITED(status))
1069                 return WEXITSTATUS(status);
1070         
1071         return 0;
1072 }
1073
1074
1075 int shell_main(int argc, char **argv)
1076 {
1077         FILE *input = stdin;
1078
1079         /* initialize the cwd */
1080         cwd = (char *) calloc(BUFSIZ, sizeof(char));
1081         if (cwd == 0) {
1082                 fatalError("out of memory\n");
1083         }
1084         getcwd(cwd, sizeof(char)*BUFSIZ);
1085
1086 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1087         cmdedit_init();
1088         signal(SIGWINCH, win_changed);
1089         win_changed(0);
1090 #endif
1091
1092         //if (argv[0] && argv[0][0] == '-') {
1093         //      builtin_source("/etc/profile");
1094         //}
1095
1096         if (argc < 2) {
1097                 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1098                 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1099         } else {
1100                 if (argv[1][0]=='-' && argv[1][1]=='c') {
1101                         int i;
1102                         local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1103                         if (local_pending_command == 0) {
1104                                 fatalError("out of memory\n");
1105                         }
1106                         for(i=2; i<argc; i++)
1107                         {
1108                                 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1109                                         local_pending_command = realloc(local_pending_command, 
1110                                                         strlen(local_pending_command) + strlen(argv[i]));
1111                                         if (local_pending_command==NULL) 
1112                                           fatalError("commands for -c option too long\n");
1113                                 }
1114                                 strcat(local_pending_command, argv[i]);
1115                                 if ( (i + 1) < argc)
1116                                   strcat(local_pending_command, " ");
1117                         }
1118                         input = NULL;
1119                           
1120                 }
1121                 else if (argv[1][0]=='-') {
1122                         usage(shell_usage);
1123                 }
1124                 else {
1125                         input = fopen(argv[1], "r");
1126                         if (!input) {
1127                                 fatalError("Couldn't open file '%s': %s\n", argv[1],
1128                                                    strerror(errno));
1129                         }
1130                 }
1131         }
1132
1133         return (busy_loop(input));
1134 }