a2969082f12ada8f92b804a2627b15897b051c90
[oweals/busybox.git] / shell / lash.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * lash -- the BusyBox Lame-Ass SHell
4  *
5  * Copyright (C) 2000 by Lineo, inc.
6  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7  *
8  * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9  * under the following liberal license: "We have placed this source code in the
10  * public domain. Use it in any project, free or commercial."
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  *
26  */
27
28
29 //#define BB_FEATURE_SH_BACKTICKS
30 //#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         {".", "Source-in and run commands in a file", builtin_source},
149 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
150         {"if", NULL, builtin_if},
151         {"then", NULL, builtin_then},
152         {"else", NULL, builtin_else},
153         {"fi", NULL, builtin_fi},
154 #endif
155         {NULL, NULL, NULL}
156 };
157
158 /* Table of forking built-in functions (things that fork cannot change global
159  * variables in the parent process, such as the current working directory) */
160 static struct builtInCommand bltins_forking[] = {
161         {"env", "Print all environment variables", builtin_env},
162         {"pwd", "Print current directory", builtin_pwd},
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         exit (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         fclose(input);
504         return (status);
505 }
506
507 /* built-in 'unset VAR' handler */
508 static int builtin_unset(struct job *cmd, struct jobSet *junk)
509 {
510         if (!cmd->progs[0].argv[1] == 1) {
511                 fprintf(stdout, "unset: parameter required.\n");
512                 return FALSE;
513         }
514         unsetenv(cmd->progs[0].argv[1]);
515         return TRUE;
516 }
517
518 /* free up all memory from a job */
519 static void freeJob(struct job *cmd)
520 {
521         int i;
522
523         for (i = 0; i < cmd->numProgs; i++) {
524                 free(cmd->progs[i].argv);
525                 if (cmd->progs[i].redirections)
526                         free(cmd->progs[i].redirections);
527                 if (cmd->progs[i].freeGlob)
528                         globfree(&cmd->progs[i].globResult);
529         }
530         free(cmd->progs);
531         if (cmd->text)
532                 free(cmd->text);
533         free(cmd->cmdBuf);
534         memset(cmd, 0, sizeof(struct job));
535 }
536
537 /* remove a job from the jobList */
538 static void removeJob(struct jobSet *jobList, struct job *job)
539 {
540         struct job *prevJob;
541
542         freeJob(job);
543         if (job == jobList->head) {
544                 jobList->head = job->next;
545         } else {
546                 prevJob = jobList->head;
547                 while (prevJob->next != job)
548                         prevJob = prevJob->next;
549                 prevJob->next = job->next;
550         }
551
552         free(job);
553 }
554
555 /* Checks to see if any background processes have exited -- if they 
556    have, figure out why and see if a job has completed */
557 static void checkJobs(struct jobSet *jobList)
558 {
559         struct job *job;
560         pid_t childpid;
561         int status;
562         int progNum = 0;
563
564         while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
565                 for (job = jobList->head; job; job = job->next) {
566                         progNum = 0;
567                         while (progNum < job->numProgs &&
568                                    job->progs[progNum].pid != childpid) progNum++;
569                         if (progNum < job->numProgs)
570                                 break;
571                 }
572
573                 /* This happens on backticked commands */
574                 if(job==NULL)
575                         return;
576
577                 if (WIFEXITED(status) || WIFSIGNALED(status)) {
578                         /* child exited */
579                         job->runningProgs--;
580                         job->progs[progNum].pid = 0;
581
582                         if (!job->runningProgs) {
583                                 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
584                                 removeJob(jobList, job);
585                         }
586                 } else {
587                         /* child stopped */
588                         job->stoppedProgs++;
589                         job->progs[progNum].isStopped = 1;
590
591                         if (job->stoppedProgs == job->numProgs) {
592                                 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
593                                            job->text);
594                         }
595                 }
596         }
597
598         if (childpid == -1 && errno != ECHILD)
599                 perror("waitpid");
600 }
601
602 static int setupRedirections(struct childProgram *prog)
603 {
604         int i;
605         int openfd;
606         int mode = O_RDONLY;
607         struct redirectionSpecifier *redir = prog->redirections;
608
609         for (i = 0; i < prog->numRedirections; i++, redir++) {
610                 switch (redir->type) {
611                 case REDIRECT_INPUT:
612                         mode = O_RDONLY;
613                         break;
614                 case REDIRECT_OVERWRITE:
615                         mode = O_RDWR | O_CREAT | O_TRUNC;
616                         break;
617                 case REDIRECT_APPEND:
618                         mode = O_RDWR | O_CREAT | O_APPEND;
619                         break;
620                 }
621
622                 openfd = open(redir->filename, mode, 0666);
623                 if (openfd < 0) {
624                         /* this could get lost if stderr has been redirected, but
625                            bash and ash both lose it as well (though zsh doesn't!) */
626                         errorMsg("error opening %s: %s\n", redir->filename,
627                                         strerror(errno));
628                         return 1;
629                 }
630
631                 if (openfd != redir->fd) {
632                         dup2(openfd, redir->fd);
633                         close(openfd);
634                 }
635         }
636
637         return 0;
638 }
639
640
641 static int getCommand(FILE * source, char *command)
642 {
643         if (source == NULL) {
644                 if (local_pending_command) {
645                         /* a command specified (-c option): return it & mark it done */
646                         strcpy(command, local_pending_command);
647                         free(local_pending_command);
648                         local_pending_command = NULL;
649                         return 0;
650                 }
651                 return 1;
652         }
653
654         if (source == stdin) {
655 #ifdef BB_FEATURE_SH_COMMAND_EDITING
656                 int len;
657
658                 /*
659                 ** enable command line editing only while a command line
660                 ** is actually being read; otherwise, we'll end up bequeathing
661                 ** atexit() handlers and other unwanted stuff to our
662                 ** child processes (rob@sysgo.de)
663                 */
664                 cmdedit_init();
665                 signal(SIGWINCH, win_changed);
666                 len=fprintf(stdout, "%s %s", cwd, prompt);
667                 fflush(stdout);
668                 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
669                 sprintf(promptStr, "%s %s", cwd, prompt);
670                 cmdedit_read_input(promptStr, command);
671                 free( promptStr);
672                 cmdedit_terminate();
673                 signal(SIGWINCH, SIG_DFL);
674                 return 0;
675 #else
676                 fprintf(stdout, "%s %s", cwd, prompt);
677                 fflush(stdout);
678 #endif
679         }
680
681         if (!fgets(command, BUFSIZ - 2, source)) {
682                 if (source == stdin)
683                         printf("\n");
684                 return 1;
685         }
686
687         /* remove trailing newline */
688         command[strlen(command) - 1] = '\0';
689
690         return 0;
691 }
692
693 #ifdef BB_FEATURE_SH_ENVIRONMENT
694 #define __MAX_INT_CHARS 7
695 static char* itoa(register int i)
696 {
697         static char a[__MAX_INT_CHARS];
698         register char *b = a + sizeof(a) - 1;
699         int   sign = (i < 0);
700
701         if (sign)
702                 i = -i;
703         *b = 0;
704         do
705         {
706                 *--b = '0' + (i % 10);
707                 i /= 10;
708         }
709         while (i);
710         if (sign)
711                 *--b = '-';
712         return b;
713 }
714 #endif
715
716 static void globLastArgument(struct childProgram *prog, int *argcPtr,
717                                                          int *argcAllocedPtr)
718 {
719         int argc_l = *argcPtr;
720         int argcAlloced = *argcAllocedPtr;
721         int rc;
722         int flags;
723         int i;
724         char *src, *dst, *var;
725
726         if (argc_l > 1) {                               /* cmd->globResult is already initialized */
727                 flags = GLOB_APPEND;
728                 i = prog->globResult.gl_pathc;
729         } else {
730                 prog->freeGlob = 1;
731                 flags = 0;
732                 i = 0;
733         }
734         /* do shell variable substitution */
735         if(*prog->argv[argc_l - 1] == '$') {
736                 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
737                         prog->argv[argc_l - 1] = var;
738                 } 
739 #ifdef BB_FEATURE_SH_ENVIRONMENT
740                 else {
741                         switch(*(prog->argv[argc_l - 1] + 1)) {
742                                 case '?':
743                                         prog->argv[argc_l - 1] = itoa(lastReturnCode);
744                                         break;
745                                 case '$':
746                                         prog->argv[argc_l - 1] = itoa(getpid());
747                                         break;
748                                 case '#':
749                                         prog->argv[argc_l - 1] = itoa(argc-1);
750                                         break;
751                                 case '!':
752                                         if (lastBgPid==-1)
753                                                 *(prog->argv[argc_l - 1])='\0';
754                                         else
755                                                 prog->argv[argc_l - 1] = itoa(lastBgPid);
756                                         break;
757                                 case '0':case '1':case '2':case '3':case '4':
758                                 case '5':case '6':case '7':case '8':case '9':
759                                         {
760                                                 int index=*(prog->argv[argc_l - 1] + 1)-48;
761                                                 if (index >= argc) {
762                                                         *(prog->argv[argc_l - 1])='\0';
763                                                 } else {
764                                                         prog->argv[argc_l - 1] = argv[index];
765                                                 }
766                                         }
767                                         break;
768                         }
769                 }
770 #endif
771         }
772
773         rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
774         if (rc == GLOB_NOSPACE) {
775                 errorMsg("out of space during glob operation\n");
776                 return;
777         } else if (rc == GLOB_NOMATCH ||
778                            (!rc && (prog->globResult.gl_pathc - i) == 1 &&
779                                 strcmp(prog->argv[argc_l - 1],
780                                                 prog->globResult.gl_pathv[i]) == 0)) {
781                 /* we need to remove whatever \ quoting is still present */
782                 src = dst = prog->argv[argc_l - 1];
783                 while (*src) {
784                         if (*src != '\\')
785                                 *dst++ = *src;
786                         src++;
787                 }
788                 *dst = '\0';
789         } else if (!rc) {
790                 argcAlloced += (prog->globResult.gl_pathc - i);
791                 prog->argv = xrealloc(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 = xcalloc(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 = xrealloc(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 = xrealloc(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 = xrealloc(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 #ifdef BB_FEATURE_SH_BACKTICKS
1007                         case '`':
1008                                 /* Exec a backtick-ed command */
1009                                 {
1010                                         char* charptr1=NULL, *charptr2;
1011                                         char* ptr=NULL;
1012                                         struct job *newJob;
1013                                         struct jobSet njobList = { NULL, NULL };
1014                                         int pipefd[2];
1015                                         int size;
1016
1017                                         ptr=strchr(++src, '`');
1018                                         if (ptr==NULL) {
1019                                                 fprintf(stderr, "Unmatched '`' in command\n");
1020                                                 freeJob(job);
1021                                                 return 1;
1022                                         }
1023
1024                                         /* Make some space to hold just the backticked command */
1025                                         charptr1 = charptr2 = xmalloc(1+ptr-src);
1026                                         snprintf(charptr1, 1+ptr-src, src);
1027                                         newJob = xmalloc(sizeof(struct job));
1028                                         /* Now parse and run the backticked command */
1029                                         if (!parseCommand(&charptr1, newJob, &njobList, inBg) 
1030                                                         && newJob->numProgs) {
1031                                                 pipe(pipefd);
1032                                                 runCommand(newJob, &njobList, 0, pipefd);
1033                                         }
1034                                         checkJobs(jobList);
1035                                         freeJob(newJob);
1036                                         free(charptr2);
1037                                         
1038                                         /* Make a copy of any stuff left over in the command 
1039                                          * line after the second backtick */
1040                                         charptr2 = xmalloc(strlen(ptr)+1);
1041                                         memcpy(charptr2, ptr+1, strlen(ptr));
1042
1043
1044                                         /* Copy the output from the backtick-ed command into the
1045                                          * command line, making extra room as needed  */
1046                                         --src;
1047                                         charptr1 = xmalloc(BUFSIZ);
1048                                         while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1049                                                 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1050                                                 if (newSize > BUFSIZ) {
1051                                                         *commandPtr=xrealloc(*commandPtr, src - *commandPtr + 
1052                                                                         size + 1 + strlen(charptr2));
1053                                                 }
1054                                                 memcpy(src, charptr1, size); 
1055                                                 src+=size;
1056                                         }
1057                                         free(charptr1);
1058                                         close(pipefd[0]);
1059                                         if (*(src-1)=='\n')
1060                                                 --src;
1061
1062                                         /* Now paste into the *commandPtr all the stuff 
1063                                          * leftover after the second backtick */
1064                                         memcpy(src, charptr2, strlen(charptr2)+1);
1065                                         free(charptr2);
1066
1067                                         /* Now recursively call parseCommand to deal with the new
1068                                          * and improved version of the command line with the backtick
1069                                          * results expanded in place... */
1070                                         freeJob(job);
1071                                         return(parseCommand(commandPtr, job, jobList, inBg));
1072                                 }
1073                                 break;
1074 #endif // BB_FEATURE_SH_BACKTICKS
1075
1076                         case '\\':
1077                                 src++;
1078                                 if (!*src) {
1079                                         errorMsg("character expected after \\\n");
1080                                         freeJob(job);
1081                                         return 1;
1082                                 }
1083                                 if (*src == '*' || *src == '[' || *src == ']'
1084                                         || *src == '?') *buf++ = '\\';
1085                                 /* fallthrough */
1086                         default:
1087                                 *buf++ = *src;
1088                         }
1089
1090                 src++;
1091         }
1092
1093         if (*prog->argv[argc_l]) {
1094                 argc_l++;
1095                 globLastArgument(prog, &argc_l, &argvAlloced);
1096         }
1097         if (!argc_l) {
1098                 freeJob(job);
1099                 return 0;
1100         }
1101         prog->argv[argc_l] = NULL;
1102
1103         if (!returnCommand) {
1104                 job->text = xmalloc(strlen(*commandPtr) + 1);
1105                 strcpy(job->text, *commandPtr);
1106         } else {
1107                 /* This leaves any trailing spaces, which is a bit sloppy */
1108                 count = returnCommand - *commandPtr;
1109                 job->text = xmalloc(count + 1);
1110                 strncpy(job->text, *commandPtr, count);
1111                 job->text[count] = '\0';
1112         }
1113
1114         *commandPtr = returnCommand;
1115
1116         return 0;
1117 }
1118
1119 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1120 {
1121         struct job *theJob;
1122         int i;
1123         int nextin, nextout;
1124         int pipefds[2];                         /* pipefd[0] is for reading */
1125         struct builtInCommand *x;
1126 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1127         const struct BB_applet *a = applets;
1128 #endif
1129
1130
1131         nextin = 0, nextout = 1;
1132         for (i = 0; i < newJob->numProgs; i++) {
1133                 if ((i + 1) < newJob->numProgs) {
1134                         pipe(pipefds);
1135                         nextout = pipefds[1];
1136                 } else {
1137                         if (outPipe[1]!=-1) {
1138                                 nextout = outPipe[1];
1139                         } else {
1140                                 nextout = 1;
1141                         }
1142                 }
1143
1144 #ifdef BB_FEATURE_SH_ENVIRONMENT
1145                 if (showXtrace==TRUE) {
1146                         int j;
1147                         fprintf(stderr, "+ ");
1148                         for (j = 0; newJob->progs[i].argv[j]; j++)
1149                                 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1150                         fprintf(stderr, "\n");
1151                 }
1152 #endif
1153
1154                 /* Check if the command matches any non-forking builtins */
1155                 for (x = bltins; x->cmd; x++) {
1156                         if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1157                                 return(x->function(newJob, jobList));
1158                         }
1159                 }
1160
1161                 if (!(newJob->progs[i].pid = fork())) {
1162                         signal(SIGTTOU, SIG_DFL);
1163
1164                         if (outPipe[1]!=-1) {
1165                                 close(outPipe[0]);
1166                         }
1167                         if (nextin != 0) {
1168                                 dup2(nextin, 0);
1169                                 close(nextin);
1170                         }
1171
1172                         if (nextout != 1) {
1173                                 dup2(nextout, 1);
1174                                 dup2(nextout, 2);
1175                                 close(nextout);
1176                                 close(pipefds[0]);
1177                         }
1178
1179                         /* explicit redirections override pipes */
1180                         setupRedirections(newJob->progs + i);
1181
1182                         /* Check if the command matches any of the other builtins */
1183                         for (x = bltins_forking; x->cmd; x++) {
1184                                 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1185                                         applet_name=x->cmd;
1186                                         exit (x->function(newJob, jobList));
1187                                 }
1188                         }
1189 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1190                         /* Check if the command matches any busybox internal commands here */
1191                         while (a->name != 0) {
1192                                 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
1193                                         int argc_l;
1194                                         char** argv=newJob->progs[i].argv;
1195                                         for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1196                                         applet_name=a->name;
1197                                         optind = 1;
1198                                         exit((*(a->main)) (argc_l, newJob->progs[i].argv));
1199                                 }
1200                                 a++;
1201                         }
1202 #endif
1203
1204                         execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1205                         fatalError("%s: %s\n", newJob->progs[i].argv[0],
1206                                            strerror(errno));
1207                 }
1208                 if (outPipe[1]!=-1) {
1209                         close(outPipe[1]);
1210                 }
1211
1212                 /* put our child in the process group whose leader is the
1213                    first process in this pipe */
1214                 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1215                 if (nextin != 0)
1216                         close(nextin);
1217                 if (nextout != 1)
1218                         close(nextout);
1219
1220                 /* If there isn't another process, nextin is garbage 
1221                    but it doesn't matter */
1222                 nextin = pipefds[0];
1223         }
1224
1225         newJob->pgrp = newJob->progs[0].pid;
1226
1227         /* find the ID for the theJob to use */
1228         newJob->jobId = 1;
1229         for (theJob = jobList->head; theJob; theJob = theJob->next)
1230                 if (theJob->jobId >= newJob->jobId)
1231                         newJob->jobId = theJob->jobId + 1;
1232
1233         /* add the theJob to the list of running jobs */
1234         if (!jobList->head) {
1235                 theJob = jobList->head = xmalloc(sizeof(*theJob));
1236         } else {
1237                 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1238                 theJob->next = xmalloc(sizeof(*theJob));
1239                 theJob = theJob->next;
1240         }
1241
1242         *theJob = *newJob;
1243         theJob->next = NULL;
1244         theJob->runningProgs = theJob->numProgs;
1245         theJob->stoppedProgs = 0;
1246
1247         if (inBg) {
1248                 /* we don't wait for background theJobs to return -- append it 
1249                    to the list of backgrounded theJobs and leave it alone */
1250                 printf("[%d] %d\n", theJob->jobId,
1251                            newJob->progs[newJob->numProgs - 1].pid);
1252 #ifdef BB_FEATURE_SH_ENVIRONMENT
1253                 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1254 #endif
1255         } else {
1256                 jobList->fg = theJob;
1257
1258                 /* move the new process group into the foreground */
1259                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1260                 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1261                         perror("tcsetpgrp");
1262         }
1263
1264         return 0;
1265 }
1266
1267 static int busy_loop(FILE * input)
1268 {
1269         char *command;
1270         char *nextCommand = NULL;
1271         struct job newJob;
1272         pid_t  parent_pgrp;
1273         int i;
1274         int inBg;
1275         int status;
1276         newJob.jobContext = REGULAR_JOB_CONTEXT;
1277
1278         /* save current owner of TTY so we can restore it on exit */
1279         parent_pgrp = tcgetpgrp(0);
1280
1281         command = (char *) xcalloc(BUFSIZ, sizeof(char));
1282
1283         /* don't pay any attention to this signal; it just confuses 
1284            things and isn't really meant for shells anyway */
1285         signal(SIGTTOU, SIG_IGN);
1286
1287         while (1) {
1288                 if (!jobList.fg) {
1289                         /* no job is in the foreground */
1290
1291                         /* see if any background processes have exited */
1292                         checkJobs(&jobList);
1293
1294                         if (!nextCommand) {
1295                                 if (getCommand(input, command))
1296                                         break;
1297                                 nextCommand = command;
1298                         }
1299
1300                         if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1301                                 newJob.numProgs) {
1302                                 int pipefds[2] = {-1,-1};
1303                                 runCommand(&newJob, &jobList, inBg, pipefds);
1304                         }
1305                         else {
1306                                 free(command);
1307                                 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1308                                 nextCommand = NULL;
1309                         }
1310                 } else {
1311                         /* a job is running in the foreground; wait for it */
1312                         i = 0;
1313                         while (!jobList.fg->progs[i].pid ||
1314                                    jobList.fg->progs[i].isStopped == 1) i++;
1315
1316                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1317
1318                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
1319                                 /* the child exited */
1320                                 jobList.fg->runningProgs--;
1321                                 jobList.fg->progs[i].pid = 0;
1322
1323 #ifdef BB_FEATURE_SH_ENVIRONMENT
1324                                 lastReturnCode=WEXITSTATUS(status);
1325 #endif
1326 #if 0
1327                                 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1328 #endif
1329                                 if (!jobList.fg->runningProgs) {
1330                                         /* child exited */
1331
1332                                         removeJob(&jobList, jobList.fg);
1333                                         jobList.fg = NULL;
1334                                 }
1335                         } else {
1336                                 /* the child was stopped */
1337                                 jobList.fg->stoppedProgs++;
1338                                 jobList.fg->progs[i].isStopped = 1;
1339
1340                                 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1341                                         printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1342                                                    "Stopped", jobList.fg->text);
1343                                         jobList.fg = NULL;
1344                                 }
1345                         }
1346
1347                         if (!jobList.fg) {
1348                                 /* move the shell to the foreground */
1349                                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1350                                 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1351                                         perror("tcsetpgrp"); 
1352                         }
1353                 }
1354         }
1355         free(command);
1356
1357         /* return controlling TTY back to parent process group before exiting */
1358         if (tcsetpgrp(0, parent_pgrp))
1359                 perror("tcsetpgrp");
1360
1361         /* return exit status if called with "-c" */
1362         if (input == NULL && WIFEXITED(status))
1363                 return WEXITSTATUS(status);
1364         
1365         return 0;
1366 }
1367
1368
1369 #ifdef BB_FEATURE_CLEAN_UP
1370 void free_memory(void)
1371 {
1372         if (promptStr)
1373                 free(promptStr);
1374         if (cwd)
1375                 free(cwd);
1376         if (local_pending_command)
1377                 free(local_pending_command);
1378
1379         if (jobList.fg && !jobList.fg->runningProgs) {
1380                 removeJob(&jobList, jobList.fg);
1381         }
1382 }
1383 #endif
1384
1385
1386 int shell_main(int argc_l, char **argv_l)
1387 {
1388         int opt, interactive=FALSE;
1389         FILE *input = stdin;
1390         argc = argc_l;
1391         argv = argv_l;
1392
1393
1394         //if (argv[0] && argv[0][0] == '-') {
1395         //      builtin_source("/etc/profile");
1396         //}
1397
1398         while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1399                 switch (opt) {
1400                         case 'c':
1401                                 input = NULL;
1402                                 if (local_pending_command != 0)
1403                                         fatalError("multiple -c arguments\n");
1404                                 local_pending_command = xstrdup(argv[optind]);
1405                                 optind++;
1406                                 argv = argv+optind;
1407                                 break;
1408 #ifdef BB_FEATURE_SH_ENVIRONMENT
1409                         case 'x':
1410                                 showXtrace = TRUE;
1411                                 break;
1412 #endif
1413                         case 'i':
1414                                 interactive = TRUE;
1415                                 break;
1416                         default:
1417                                 usage(shell_usage);
1418                 }
1419         }
1420         /* A shell is interactive if the `-i' flag was given, or if all of
1421          * the following conditions are met:
1422          *        no -c command
1423          *    no arguments remaining or the -s flag given
1424          *    standard input is a terminal
1425          *    standard output is a terminal
1426          *    Refer to Posix.2, the description of the `sh' utility. */
1427         if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1428                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
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 (local_pending_command==NULL) {
1433                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1434                 input = fopen(argv[optind], "r");
1435                 if (!input) {
1436                         fatalError("%s: %s\n", argv[optind], strerror(errno));
1437                 }
1438         }
1439
1440         /* initialize the cwd -- this is never freed...*/
1441         cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1442         getcwd(cwd, sizeof(char)*MAX_LINE);
1443
1444 #ifdef BB_FEATURE_CLEAN_UP
1445         atexit(free_memory);
1446 #endif
1447
1448 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1449         win_changed(0);
1450 #endif
1451
1452         return (busy_loop(input));
1453 }