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