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