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