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