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