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