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