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