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