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