cbaf16ef8b6c42bf3ca53c640d3ff17a2a30c6d0
[oweals/busybox.git] / lash.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * lash -- the BusyBox Lame-Ass SHell
4  *
5  * Copyright (C) 2000 by Lineo, inc.
6  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7  *
8  * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9  * under the following liberal license: "We have placed this source code in the
10  * public domain. Use it in any project, free or commercial."
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  *
26  */
27
28
29 //#define BB_FEATURE_SH_BACKTICKS
30 //#define BB_FEATURE_SH_IF_EXPRESSIONS
31 //#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                                         memcpy(charptr1, src, ptr-src);
1027                                         charptr1[ptr-src] = '\0';
1028                                         newJob = xmalloc(sizeof(struct job));
1029                                         /* Now parse and run the backticked command */
1030                                         if (!parseCommand(&charptr1, newJob, &njobList, inBg) 
1031                                                         && newJob->numProgs) {
1032                                                 pipe(pipefd);
1033                                                 runCommand(newJob, &njobList, 0, pipefd);
1034                                         }
1035                                         checkJobs(jobList);
1036                                         freeJob(newJob);
1037                                         free(charptr2);
1038                                         
1039                                         /* Make a copy of any stuff left over in the command 
1040                                          * line after the second backtick */
1041                                         charptr2 = xmalloc(strlen(ptr)+1);
1042                                         memcpy(charptr2, ptr+1, strlen(ptr));
1043
1044
1045                                         /* Copy the output from the backtick-ed command into the
1046                                          * command line, making extra room as needed  */
1047                                         --src;
1048                                         charptr1 = xmalloc(BUFSIZ);
1049                                         while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1050                                                 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1051                                                 if (newSize > BUFSIZ) {
1052                                                         *commandPtr=xrealloc(*commandPtr, src - *commandPtr + 
1053                                                                         size + 1 + strlen(charptr2));
1054                                                 }
1055                                                 memcpy(src, charptr1, size); 
1056                                                 src+=size;
1057                                         }
1058                                         free(charptr1);
1059                                         close(pipefd[0]);
1060                                         if (*(src-1)=='\n')
1061                                                 --src;
1062
1063                                         /* Now paste into the *commandPtr all the stuff 
1064                                          * leftover after the second backtick */
1065                                         memcpy(src, charptr2, strlen(charptr2)+1);
1066                                         free(charptr2);
1067
1068                                         /* Now recursively call parseCommand to deal with the new
1069                                          * and improved version of the command line with the backtick
1070                                          * results expanded in place... */
1071                                         freeJob(job);
1072                                         return(parseCommand(commandPtr, job, jobList, inBg));
1073                                 }
1074                                 break;
1075 #endif // BB_FEATURE_SH_BACKTICKS
1076
1077                         case '\\':
1078                                 src++;
1079                                 if (!*src) {
1080                                         errorMsg("character expected after \\\n");
1081                                         freeJob(job);
1082                                         return 1;
1083                                 }
1084                                 if (*src == '*' || *src == '[' || *src == ']'
1085                                         || *src == '?') *buf++ = '\\';
1086                                 /* fallthrough */
1087                         default:
1088                                 *buf++ = *src;
1089                         }
1090
1091                 src++;
1092         }
1093
1094         if (*prog->argv[argc_l]) {
1095                 argc_l++;
1096                 globLastArgument(prog, &argc_l, &argvAlloced);
1097         }
1098         if (!argc_l) {
1099                 freeJob(job);
1100                 return 0;
1101         }
1102         prog->argv[argc_l] = NULL;
1103
1104         if (!returnCommand) {
1105                 job->text = xmalloc(strlen(*commandPtr) + 1);
1106                 strcpy(job->text, *commandPtr);
1107         } else {
1108                 /* This leaves any trailing spaces, which is a bit sloppy */
1109                 count = returnCommand - *commandPtr;
1110                 job->text = xmalloc(count + 1);
1111                 strncpy(job->text, *commandPtr, count);
1112                 job->text[count] = '\0';
1113         }
1114
1115         *commandPtr = returnCommand;
1116
1117         return 0;
1118 }
1119
1120 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1121 {
1122         struct job *theJob;
1123         int i;
1124         int nextin, nextout;
1125         int pipefds[2];                         /* pipefd[0] is for reading */
1126         struct builtInCommand *x;
1127 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1128         const struct BB_applet *a = applets;
1129 #endif
1130
1131
1132         nextin = 0, nextout = 1;
1133         for (i = 0; i < newJob->numProgs; i++) {
1134                 if ((i + 1) < newJob->numProgs) {
1135                         pipe(pipefds);
1136                         nextout = pipefds[1];
1137                 } else {
1138                         if (outPipe[1]!=-1) {
1139                                 nextout = outPipe[1];
1140                         } else {
1141                                 nextout = 1;
1142                         }
1143                 }
1144
1145 #ifdef BB_FEATURE_SH_ENVIRONMENT
1146                 if (showXtrace==TRUE) {
1147                         int j;
1148                         fprintf(stderr, "+ ");
1149                         for (j = 0; newJob->progs[i].argv[j]; j++)
1150                                 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1151                         fprintf(stderr, "\n");
1152                 }
1153 #endif
1154
1155                 /* Check if the command matches any non-forking builtins */
1156                 for (x = bltins; x->cmd; x++) {
1157                         if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1158                                 return(x->function(newJob, jobList));
1159                         }
1160                 }
1161
1162                 if (!(newJob->progs[i].pid = fork())) {
1163                         signal(SIGTTOU, SIG_DFL);
1164
1165                         if (outPipe[1]!=-1) {
1166                                 close(outPipe[0]);
1167                         }
1168                         if (nextin != 0) {
1169                                 dup2(nextin, 0);
1170                                 close(nextin);
1171                         }
1172
1173                         if (nextout != 1) {
1174                                 dup2(nextout, 1);
1175                                 dup2(nextout, 2);
1176                                 close(nextout);
1177                                 close(pipefds[0]);
1178                         }
1179
1180                         /* explicit redirections override pipes */
1181                         setupRedirections(newJob->progs + i);
1182
1183                         /* Check if the command matches any of the other builtins */
1184                         for (x = bltins_forking; x->cmd; x++) {
1185                                 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1186                                         applet_name=x->cmd;
1187                                         exit (x->function(newJob, jobList));
1188                                 }
1189                         }
1190 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1191                         /* Check if the command matches any busybox internal commands here */
1192                         while (a->name != 0) {
1193                                 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
1194                                         int argc_l;
1195                                         char** argv=newJob->progs[i].argv;
1196                                         for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1197                                         applet_name=a->name;
1198                                         optind = 1;
1199                                         exit((*(a->main)) (argc_l, newJob->progs[i].argv));
1200                                 }
1201                                 a++;
1202                         }
1203 #endif
1204
1205                         execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1206                         fatalError("%s: %s\n", newJob->progs[i].argv[0],
1207                                            strerror(errno));
1208                 }
1209                 if (outPipe[1]!=-1) {
1210                         close(outPipe[1]);
1211                 }
1212
1213                 /* put our child in the process group whose leader is the
1214                    first process in this pipe */
1215                 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1216                 if (nextin != 0)
1217                         close(nextin);
1218                 if (nextout != 1)
1219                         close(nextout);
1220
1221                 /* If there isn't another process, nextin is garbage 
1222                    but it doesn't matter */
1223                 nextin = pipefds[0];
1224         }
1225
1226         newJob->pgrp = newJob->progs[0].pid;
1227
1228         /* find the ID for the theJob to use */
1229         newJob->jobId = 1;
1230         for (theJob = jobList->head; theJob; theJob = theJob->next)
1231                 if (theJob->jobId >= newJob->jobId)
1232                         newJob->jobId = theJob->jobId + 1;
1233
1234         /* add the theJob to the list of running jobs */
1235         if (!jobList->head) {
1236                 theJob = jobList->head = xmalloc(sizeof(*theJob));
1237         } else {
1238                 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1239                 theJob->next = xmalloc(sizeof(*theJob));
1240                 theJob = theJob->next;
1241         }
1242
1243         *theJob = *newJob;
1244         theJob->next = NULL;
1245         theJob->runningProgs = theJob->numProgs;
1246         theJob->stoppedProgs = 0;
1247
1248         if (inBg) {
1249                 /* we don't wait for background theJobs to return -- append it 
1250                    to the list of backgrounded theJobs and leave it alone */
1251                 printf("[%d] %d\n", theJob->jobId,
1252                            newJob->progs[newJob->numProgs - 1].pid);
1253 #ifdef BB_FEATURE_SH_ENVIRONMENT
1254                 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1255 #endif
1256         } else {
1257                 jobList->fg = theJob;
1258
1259                 /* move the new process group into the foreground */
1260                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1261                 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1262                         perror("tcsetpgrp");
1263         }
1264
1265         return 0;
1266 }
1267
1268 static int busy_loop(FILE * input)
1269 {
1270         char *command;
1271         char *nextCommand = NULL;
1272         struct job newJob;
1273         pid_t  parent_pgrp;
1274         int i;
1275         int inBg;
1276         int status;
1277         newJob.jobContext = REGULAR_JOB_CONTEXT;
1278
1279         /* save current owner of TTY so we can restore it on exit */
1280         parent_pgrp = tcgetpgrp(0);
1281
1282         command = (char *) xcalloc(BUFSIZ, sizeof(char));
1283
1284         /* don't pay any attention to this signal; it just confuses 
1285            things and isn't really meant for shells anyway */
1286         signal(SIGTTOU, SIG_IGN);
1287
1288         while (1) {
1289                 if (!jobList.fg) {
1290                         /* no job is in the foreground */
1291
1292                         /* see if any background processes have exited */
1293                         checkJobs(&jobList);
1294
1295                         if (!nextCommand) {
1296                                 if (getCommand(input, command))
1297                                         break;
1298                                 nextCommand = command;
1299                         }
1300
1301                         if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1302                                 newJob.numProgs) {
1303                                 int pipefds[2] = {-1,-1};
1304                                 runCommand(&newJob, &jobList, inBg, pipefds);
1305                         }
1306                         else {
1307                                 free(command);
1308                                 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1309                                 nextCommand = NULL;
1310                         }
1311                 } else {
1312                         /* a job is running in the foreground; wait for it */
1313                         i = 0;
1314                         while (!jobList.fg->progs[i].pid ||
1315                                    jobList.fg->progs[i].isStopped == 1) i++;
1316
1317                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1318
1319                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
1320                                 /* the child exited */
1321                                 jobList.fg->runningProgs--;
1322                                 jobList.fg->progs[i].pid = 0;
1323
1324 #ifdef BB_FEATURE_SH_ENVIRONMENT
1325                                 lastReturnCode=WEXITSTATUS(status);
1326 #endif
1327 #if 0
1328                                 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1329 #endif
1330                                 if (!jobList.fg->runningProgs) {
1331                                         /* child exited */
1332
1333                                         removeJob(&jobList, jobList.fg);
1334                                         jobList.fg = NULL;
1335                                 }
1336                         } else {
1337                                 /* the child was stopped */
1338                                 jobList.fg->stoppedProgs++;
1339                                 jobList.fg->progs[i].isStopped = 1;
1340
1341                                 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1342                                         printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1343                                                    "Stopped", jobList.fg->text);
1344                                         jobList.fg = NULL;
1345                                 }
1346                         }
1347
1348                         if (!jobList.fg) {
1349                                 /* move the shell to the foreground */
1350                                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1351                                 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1352                                         perror("tcsetpgrp"); 
1353                         }
1354                 }
1355         }
1356         free(command);
1357
1358         /* return controlling TTY back to parent process group before exiting */
1359         if (tcsetpgrp(0, parent_pgrp))
1360                 perror("tcsetpgrp");
1361
1362         /* return exit status if called with "-c" */
1363         if (input == NULL && WIFEXITED(status))
1364                 return WEXITSTATUS(status);
1365         
1366         return 0;
1367 }
1368
1369
1370 #ifdef BB_FEATURE_CLEAN_UP
1371 void free_memory(void)
1372 {
1373         if (promptStr)
1374                 free(promptStr);
1375         if (cwd)
1376                 free(cwd);
1377         if (local_pending_command)
1378                 free(local_pending_command);
1379
1380         if (jobList.fg && !jobList.fg->runningProgs) {
1381                 removeJob(&jobList, jobList.fg);
1382         }
1383 }
1384 #endif
1385
1386
1387 int shell_main(int argc_l, char **argv_l)
1388 {
1389         int opt, interactive=FALSE;
1390         FILE *input = stdin;
1391         argc = argc_l;
1392         argv = argv_l;
1393
1394
1395         //if (argv[0] && argv[0][0] == '-') {
1396         //      builtin_source("/etc/profile");
1397         //}
1398
1399         while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1400                 switch (opt) {
1401                         case 'c':
1402                                 input = NULL;
1403                                 if (local_pending_command != 0)
1404                                         fatalError("multiple -c arguments\n");
1405                                 local_pending_command = xstrdup(argv[optind]);
1406                                 optind++;
1407                                 argv = argv+optind;
1408                                 break;
1409 #ifdef BB_FEATURE_SH_ENVIRONMENT
1410                         case 'x':
1411                                 showXtrace = TRUE;
1412                                 break;
1413 #endif
1414                         case 'i':
1415                                 interactive = TRUE;
1416                                 break;
1417                         default:
1418                                 usage(shell_usage);
1419                 }
1420         }
1421         /* A shell is interactive if the `-i' flag was given, or if all of
1422          * the following conditions are met:
1423          *        no -c command
1424          *    no arguments remaining or the -s flag given
1425          *    standard input is a terminal
1426          *    standard output is a terminal
1427          *    Refer to Posix.2, the description of the `sh' utility. */
1428         if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1429                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1430                 /* Looks like they want an interactive shell */
1431                 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1432                 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1433         } else if (local_pending_command==NULL) {
1434                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1435                 input = fopen(argv[optind], "r");
1436                 if (!input) {
1437                         fatalError("%s: %s\n", argv[optind], strerror(errno));
1438                 }
1439         }
1440
1441         /* initialize the cwd -- this is never freed...*/
1442         cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1443         getcwd(cwd, sizeof(char)*MAX_LINE);
1444
1445 #ifdef BB_FEATURE_CLEAN_UP
1446         atexit(free_memory);
1447 #endif
1448
1449 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1450         win_changed(0);
1451 #endif
1452
1453         return (busy_loop(input));
1454 }