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