7f5b9060207c30bb20c33de7a2e688c2948577d0
[oweals/busybox.git] / sh.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * lash -- the BusyBox Lame-Ass SHell
4  *
5  * Copyright (C) 2000 by Lineo, inc.
6  * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7  *
8  * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9  * under the following liberal license: "We have placed this source code in the
10  * public domain. Use it in any project, free or commercial."
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  *
26  */
27
28
29 //#define BB_FEATURE_SH_BACKTICKS
30 //#define BB_FEATURE_SH_IF_EXPRESSIONS
31 #define BB_FEATURE_SH_ENVIRONMENT
32 //#define DEBUG_SHELL
33
34
35 #include "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                                         *dst++ = *src;
802                                 src++;
803                         }
804                         *dst = '\0';
805                 } else if (!rc) {
806                         argcAlloced += (prog->globResult.gl_pathc - i);
807                         prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
808                         memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
809                                    sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
810                         argc_l += (prog->globResult.gl_pathc - i - 1);
811                 }
812         }else{
813                         src = dst = prog->argv[argc_l - 1];
814                         while (*src) {
815                                 if (*src != '\\')
816                                         *dst++ = *src;
817                                 src++;
818                         }
819                         *dst = '\0';
820                         prog->globResult.gl_pathc=0;
821                         if (flags==0)
822                                 prog->globResult.gl_pathv=NULL;
823         }
824         *argcAllocedPtr = argcAlloced;
825         *argcPtr = argc_l;
826 }
827
828 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
829    line). If a valid command is found, commandPtr is set to point to
830    the beginning of the next command (if the original command had more 
831    then one job associated with it) or NULL if no more commands are 
832    present. */
833 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
834 {
835         char *command;
836         char *returnCommand = NULL;
837         char *src, *buf, *chptr;
838         int argc_l = 0;
839         int done = 0;
840         int argvAlloced;
841         int i;
842         char quote = '\0';
843         int count;
844         struct childProgram *prog;
845
846         /* skip leading white space */
847         while (**commandPtr && isspace(**commandPtr))
848                 (*commandPtr)++;
849
850         /* this handles empty lines or leading '#' characters */
851         if (!**commandPtr || (**commandPtr == '#')) {
852                 job->numProgs=0;
853                 return 0;
854         }
855
856         *inBg = 0;
857         job->numProgs = 1;
858         job->progs = xmalloc(sizeof(*job->progs));
859
860         /* We set the argv elements to point inside of this string. The 
861            memory is freed by freeJob(). Allocate twice the original
862            length in case we need to quote every single character.
863
864            Getting clean memory relieves us of the task of NULL 
865            terminating things and makes the rest of this look a bit 
866            cleaner (though it is, admittedly, a tad less efficient) */
867         job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
868         job->text = NULL;
869
870         prog = job->progs;
871         prog->numRedirections = 0;
872         prog->redirections = NULL;
873         prog->freeGlob = 0;
874         prog->isStopped = 0;
875
876         argvAlloced = 5;
877         prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
878         prog->argv[0] = job->cmdBuf;
879
880         buf = command;
881         src = *commandPtr;
882         while (*src && !done) {
883                 if (quote == *src) {
884                         quote = '\0';
885                 } else if (quote) {
886                         if (*src == '\\') {
887                                 src++;
888                                 if (!*src) {
889                                         errorMsg("character expected after \\\n");
890                                         freeJob(job);
891                                         return 1;
892                                 }
893
894                                 /* in shell, "\'" should yield \' */
895                                 if (*src != quote)
896                                         *buf++ = '\\';
897                         } else if (*src == '*' || *src == '?' || *src == '[' ||
898                                            *src == ']') *buf++ = '\\';
899                         *buf++ = *src;
900                 } else if (isspace(*src)) {
901                         if (*prog->argv[argc_l]) {
902                                 buf++, argc_l++;
903                                 /* +1 here leaves room for the NULL which ends argv */
904                                 if ((argc_l + 1) == argvAlloced) {
905                                         argvAlloced += 5;
906                                         prog->argv = xrealloc(prog->argv,
907                                                                                   sizeof(*prog->argv) *
908                                                                                   argvAlloced);
909                                 }
910                                 globLastArgument(prog, &argc_l, &argvAlloced);
911                                 prog->argv[argc_l] = buf;
912                         }
913                 } else
914                         switch (*src) {
915                         case '"':
916                         case '\'':
917                                 quote = *src;
918                                 break;
919
920                         case '#':                       /* comment */
921                                 if (*(src-1)== '$')
922                                         *buf++ = *src;
923                                 else
924                                         done = 1;
925                                 break;
926
927                         case '>':                       /* redirections */
928                         case '<':
929                                 i = prog->numRedirections++;
930                                 prog->redirections = xrealloc(prog->redirections,
931                                                                                           sizeof(*prog->redirections) *
932                                                                                           (i + 1));
933
934                                 prog->redirections[i].fd = -1;
935                                 if (buf != prog->argv[argc_l]) {
936                                         /* the stuff before this character may be the file number 
937                                            being redirected */
938                                         prog->redirections[i].fd =
939                                                 strtol(prog->argv[argc_l], &chptr, 10);
940
941                                         if (*chptr && *prog->argv[argc_l]) {
942                                                 buf++, argc_l++;
943                                                 globLastArgument(prog, &argc_l, &argvAlloced);
944                                                 prog->argv[argc_l] = buf;
945                                         }
946                                 }
947
948                                 if (prog->redirections[i].fd == -1) {
949                                         if (*src == '>')
950                                                 prog->redirections[i].fd = 1;
951                                         else
952                                                 prog->redirections[i].fd = 0;
953                                 }
954
955                                 if (*src++ == '>') {
956                                         if (*src == '>')
957                                                 prog->redirections[i].type =
958                                                         REDIRECT_APPEND, src++;
959                                         else
960                                                 prog->redirections[i].type = REDIRECT_OVERWRITE;
961                                 } else {
962                                         prog->redirections[i].type = REDIRECT_INPUT;
963                                 }
964
965                                 /* This isn't POSIX sh compliant. Oh well. */
966                                 chptr = src;
967                                 while (isspace(*chptr))
968                                         chptr++;
969
970                                 if (!*chptr) {
971                                         errorMsg("file name expected after %c\n", *src);
972                                         freeJob(job);
973                                         job->numProgs=0;
974                                         return 1;
975                                 }
976
977                                 prog->redirections[i].filename = buf;
978                                 while (*chptr && !isspace(*chptr))
979                                         *buf++ = *chptr++;
980
981                                 src = chptr - 1;        /* we src++ later */
982                                 prog->argv[argc_l] = ++buf;
983                                 break;
984
985                         case '|':                       /* pipe */
986                                 /* finish this command */
987                                 if (*prog->argv[argc_l])
988                                         argc_l++;
989                                 if (!argc_l) {
990                                         errorMsg("empty command in pipe\n");
991                                         freeJob(job);
992                                         job->numProgs=0;
993                                         return 1;
994                                 }
995                                 prog->argv[argc_l] = NULL;
996
997                                 /* and start the next */
998                                 job->numProgs++;
999                                 job->progs = xrealloc(job->progs,
1000                                                                           sizeof(*job->progs) * job->numProgs);
1001                                 prog = job->progs + (job->numProgs - 1);
1002                                 prog->numRedirections = 0;
1003                                 prog->redirections = NULL;
1004                                 prog->freeGlob = 0;
1005                                 prog->isStopped = 0;
1006                                 argc_l = 0;
1007
1008                                 argvAlloced = 5;
1009                                 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1010                                 prog->argv[0] = ++buf;
1011
1012                                 src++;
1013                                 while (*src && isspace(*src))
1014                                         src++;
1015
1016                                 if (!*src) {
1017                                         errorMsg("empty command in pipe\n");
1018                                         freeJob(job);
1019                                         job->numProgs=0;
1020                                         return 1;
1021                                 }
1022                                 src--;                  /* we'll ++ it at the end of the loop */
1023
1024                                 break;
1025
1026                         case '&':                       /* background */
1027                                 *inBg = 1;
1028                         case ';':                       /* multiple commands */
1029                                 done = 1;
1030                                 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1031                                 break;
1032
1033 #ifdef BB_FEATURE_SH_BACKTICKS
1034                         case '`':
1035                                 /* Exec a backtick-ed command */
1036                                 {
1037                                         char* charptr1=NULL, *charptr2;
1038                                         char* ptr=NULL;
1039                                         struct job *newJob;
1040                                         struct jobSet njobList = { NULL, NULL };
1041                                         int pipefd[2];
1042                                         int size;
1043
1044                                         ptr=strchr(++src, '`');
1045                                         if (ptr==NULL) {
1046                                                 fprintf(stderr, "Unmatched '`' in command\n");
1047                                                 freeJob(job);
1048                                                 return 1;
1049                                         }
1050
1051                                         /* Make some space to hold just the backticked command */
1052                                         charptr1 = charptr2 = xmalloc(1+ptr-src);
1053                                         memcpy(charptr1, src, ptr-src);
1054                                         charptr1[ptr-src] = '\0';
1055                                         newJob = xmalloc(sizeof(struct job));
1056                                         /* Now parse and run the backticked command */
1057                                         if (!parseCommand(&charptr1, newJob, &njobList, inBg) 
1058                                                         && newJob->numProgs) {
1059                                                 pipe(pipefd);
1060                                                 runCommand(newJob, &njobList, 0, pipefd);
1061                                         }
1062                                         checkJobs(jobList);
1063                                         freeJob(newJob);
1064                                         free(charptr2);
1065                                         
1066                                         /* Make a copy of any stuff left over in the command 
1067                                          * line after the second backtick */
1068                                         charptr2 = xmalloc(strlen(ptr)+1);
1069                                         memcpy(charptr2, ptr+1, strlen(ptr));
1070
1071
1072                                         /* Copy the output from the backtick-ed command into the
1073                                          * command line, making extra room as needed  */
1074                                         --src;
1075                                         charptr1 = xmalloc(BUFSIZ);
1076                                         while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1077                                                 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1078                                                 if (newSize > BUFSIZ) {
1079                                                         *commandPtr=xrealloc(*commandPtr, src - *commandPtr + 
1080                                                                         size + 1 + strlen(charptr2));
1081                                                 }
1082                                                 memcpy(src, charptr1, size); 
1083                                                 src+=size;
1084                                         }
1085                                         free(charptr1);
1086                                         close(pipefd[0]);
1087                                         if (*(src-1)=='\n')
1088                                                 --src;
1089
1090                                         /* Now paste into the *commandPtr all the stuff 
1091                                          * leftover after the second backtick */
1092                                         memcpy(src, charptr2, strlen(charptr2)+1);
1093                                         free(charptr2);
1094
1095                                         /* Now recursively call parseCommand to deal with the new
1096                                          * and improved version of the command line with the backtick
1097                                          * results expanded in place... */
1098                                         freeJob(job);
1099                                         return(parseCommand(commandPtr, job, jobList, inBg));
1100                                 }
1101                                 break;
1102 #endif // BB_FEATURE_SH_BACKTICKS
1103
1104                         case '\\':
1105                                 src++;
1106                                 if (!*src) {
1107                                         errorMsg("character expected after \\\n");
1108                                         freeJob(job);
1109                                         return 1;
1110                                 }
1111                                 if (*src == '*' || *src == '[' || *src == ']'
1112                                         || *src == '?') *buf++ = '\\';
1113                                 /* fallthrough */
1114                         default:
1115                                 *buf++ = *src;
1116                         }
1117
1118                 src++;
1119         }
1120
1121         if (*prog->argv[argc_l]) {
1122                 argc_l++;
1123                 globLastArgument(prog, &argc_l, &argvAlloced);
1124         }
1125         if (!argc_l) {
1126                 freeJob(job);
1127                 return 0;
1128         }
1129         prog->argv[argc_l] = NULL;
1130
1131         if (!returnCommand) {
1132                 job->text = xmalloc(strlen(*commandPtr) + 1);
1133                 strcpy(job->text, *commandPtr);
1134         } else {
1135                 /* This leaves any trailing spaces, which is a bit sloppy */
1136                 count = returnCommand - *commandPtr;
1137                 job->text = xmalloc(count + 1);
1138                 strncpy(job->text, *commandPtr, count);
1139                 job->text[count] = '\0';
1140         }
1141
1142         *commandPtr = returnCommand;
1143         
1144         return 0;
1145 }
1146
1147 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1148 {
1149         struct job *theJob;
1150         int i;
1151         int nextin, nextout;
1152         int pipefds[2];                         /* pipefd[0] is for reading */
1153         struct builtInCommand *x;
1154 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1155         struct BB_applet search_applet, *applet;
1156 #endif
1157
1158         nextin = 0, nextout = 1;
1159         for (i = 0; i < newJob->numProgs; i++) {
1160                 if ((i + 1) < newJob->numProgs) {
1161                         pipe(pipefds);
1162                         nextout = pipefds[1];
1163                 } else {
1164                         if (outPipe[1]!=-1) {
1165                                 nextout = outPipe[1];
1166                         } else {
1167                                 nextout = 1;
1168                         }
1169                 }
1170
1171 #ifdef BB_FEATURE_SH_ENVIRONMENT
1172                 if (showXtrace==TRUE) {
1173                         int j;
1174                         fprintf(stderr, "+ ");
1175                         for (j = 0; newJob->progs[i].argv[j]; j++)
1176                                 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1177                         fprintf(stderr, "\n");
1178                 }
1179 #endif
1180
1181                 /* Check if the command matches any non-forking builtins */
1182                 for (x = bltins; x->cmd; x++) {
1183                         if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1184                                 return(x->function(newJob, jobList));
1185                         }
1186                 }
1187
1188                 if (!(newJob->progs[i].pid = fork())) {
1189                         signal(SIGTTOU, SIG_DFL);
1190
1191                         if (outPipe[1]!=-1) {
1192                                 close(outPipe[0]);
1193                         }
1194                         if (nextin != 0) {
1195                                 dup2(nextin, 0);
1196                                 close(nextin);
1197                         }
1198
1199                         if (nextout != 1) {
1200                                 dup2(nextout, 1);
1201                                 dup2(nextout, 2);
1202                                 close(nextout);
1203                                 close(pipefds[0]);
1204                         }
1205
1206                         /* explicit redirections override pipes */
1207                         setupRedirections(newJob->progs + i);
1208
1209                         /* Check if the command matches any of the other builtins */
1210                         for (x = bltins_forking; x->cmd; x++) {
1211                                 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1212                                         applet_name=x->cmd;
1213                                         exit (x->function(newJob, jobList));
1214                                 }
1215                         }
1216 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1217                         /* Check if the command matches any busybox internal
1218                          * commands ("applets") here.  Following discussions from
1219                          * November 2000 on busybox@opensource.lineo.com, don't use
1220                          * get_last_path_component().  This way explicit (with
1221                          * slashes) filenames will never be interpreted as an
1222                          * applet, just like with builtins.  This way the user can
1223                          * override an applet with an explicit filename reference.
1224                          * The only downside to this change is that an explicit
1225                          * /bin/foo invocation fill fork and exec /bin/foo, even if
1226                          * /bin/foo is a symlink to busybox.
1227                          */
1228                         search_applet.name = newJob->progs[i].argv[0];
1229
1230 #ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
1231                         /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
1232                          * if you run /bin/cat, it will use BusyBox cat even if 
1233                          * /bin/cat exists on the filesystem and is _not_ busybox.
1234                          * Some systems want this, others do not.  Choose wisely.  :-)
1235                          */
1236                         search_applet.name = get_last_path_component(search_applet.name);
1237 #endif
1238
1239                         /* Do a binary search to find the applet entry given the name. */
1240                         applet = bsearch(&search_applet, applets, NUM_APPLETS,
1241                                         sizeof(struct BB_applet), applet_name_compare);
1242                         if (applet != NULL) {
1243                                 int argc_l;
1244                                 char** argv=newJob->progs[i].argv;
1245                                 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1246                                 applet_name=applet->name;
1247                                 optind = 1;
1248                                 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1249                         }
1250 #endif
1251
1252                         execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1253                         fatalError("%s: %s\n", newJob->progs[i].argv[0],
1254                                         strerror(errno));
1255                 }
1256                 if (outPipe[1]!=-1) {
1257                         close(outPipe[1]);
1258                 }
1259
1260                 /* put our child in the process group whose leader is the
1261                    first process in this pipe */
1262                 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1263                 if (nextin != 0)
1264                         close(nextin);
1265                 if (nextout != 1)
1266                         close(nextout);
1267
1268                 /* If there isn't another process, nextin is garbage 
1269                    but it doesn't matter */
1270                 nextin = pipefds[0];
1271         }
1272
1273         newJob->pgrp = newJob->progs[0].pid;
1274
1275         /* find the ID for the theJob to use */
1276         newJob->jobId = 1;
1277         for (theJob = jobList->head; theJob; theJob = theJob->next)
1278                 if (theJob->jobId >= newJob->jobId)
1279                         newJob->jobId = theJob->jobId + 1;
1280
1281         /* add the theJob to the list of running jobs */
1282         if (!jobList->head) {
1283                 theJob = jobList->head = xmalloc(sizeof(*theJob));
1284         } else {
1285                 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1286                 theJob->next = xmalloc(sizeof(*theJob));
1287                 theJob = theJob->next;
1288         }
1289
1290         *theJob = *newJob;
1291         theJob->next = NULL;
1292         theJob->runningProgs = theJob->numProgs;
1293         theJob->stoppedProgs = 0;
1294
1295         if (inBg) {
1296                 /* we don't wait for background theJobs to return -- append it 
1297                    to the list of backgrounded theJobs and leave it alone */
1298                 printf("[%d] %d\n", theJob->jobId,
1299                            newJob->progs[newJob->numProgs - 1].pid);
1300 #ifdef BB_FEATURE_SH_ENVIRONMENT
1301                 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1302 #endif
1303         } else {
1304                 jobList->fg = theJob;
1305
1306                 /* move the new process group into the foreground */
1307                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1308                 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1309                         perror("tcsetpgrp");
1310         }
1311
1312         return 0;
1313 }
1314
1315 static int busy_loop(FILE * input)
1316 {
1317         char *command;
1318         char *nextCommand = NULL;
1319         struct job newJob;
1320         pid_t  parent_pgrp;
1321         int i;
1322         int inBg;
1323         int status;
1324         newJob.jobContext = REGULAR_JOB_CONTEXT;
1325
1326         /* save current owner of TTY so we can restore it on exit */
1327         parent_pgrp = tcgetpgrp(0);
1328
1329         command = (char *) xcalloc(BUFSIZ, sizeof(char));
1330
1331         /* don't pay any attention to this signal; it just confuses 
1332            things and isn't really meant for shells anyway */
1333         signal(SIGTTOU, SIG_IGN);
1334
1335         while (1) {
1336                 if (!jobList.fg) {
1337                         /* no job is in the foreground */
1338
1339                         /* see if any background processes have exited */
1340                         checkJobs(&jobList);
1341
1342                         if (!nextCommand) {
1343                                 if (getCommand(input, command))
1344                                         break;
1345                                 nextCommand = command;
1346                         }
1347
1348                         if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1349                                 newJob.numProgs) {
1350                                 int pipefds[2] = {-1,-1};
1351                                 runCommand(&newJob, &jobList, inBg, pipefds);
1352                         }
1353                         else {
1354                                 free(command);
1355                                 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1356                                 nextCommand = NULL;
1357                         }
1358                 } else {
1359                         /* a job is running in the foreground; wait for it */
1360                         i = 0;
1361                         while (!jobList.fg->progs[i].pid ||
1362                                    jobList.fg->progs[i].isStopped == 1) i++;
1363
1364                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1365
1366                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
1367                                 /* the child exited */
1368                                 jobList.fg->runningProgs--;
1369                                 jobList.fg->progs[i].pid = 0;
1370
1371 #ifdef BB_FEATURE_SH_ENVIRONMENT
1372                                 lastReturnCode=WEXITSTATUS(status);
1373 #endif
1374 #if 0
1375                                 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1376 #endif
1377                                 if (!jobList.fg->runningProgs) {
1378                                         /* child exited */
1379
1380                                         removeJob(&jobList, jobList.fg);
1381                                         jobList.fg = NULL;
1382                                 }
1383                         } else {
1384                                 /* the child was stopped */
1385                                 jobList.fg->stoppedProgs++;
1386                                 jobList.fg->progs[i].isStopped = 1;
1387
1388                                 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1389                                         printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1390                                                    "Stopped", jobList.fg->text);
1391                                         jobList.fg = NULL;
1392                                 }
1393                         }
1394
1395                         if (!jobList.fg) {
1396                                 /* move the shell to the foreground */
1397                                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1398                                 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1399                                         perror("tcsetpgrp"); 
1400                         }
1401                 }
1402         }
1403         free(command);
1404
1405         /* return controlling TTY back to parent process group before exiting */
1406         if (tcsetpgrp(0, parent_pgrp))
1407                 perror("tcsetpgrp");
1408
1409         /* return exit status if called with "-c" */
1410         if (input == NULL && WIFEXITED(status))
1411                 return WEXITSTATUS(status);
1412         
1413         return 0;
1414 }
1415
1416
1417 #ifdef BB_FEATURE_CLEAN_UP
1418 void free_memory(void)
1419 {
1420         if (promptStr)
1421                 free(promptStr);
1422         if (cwd)
1423                 free(cwd);
1424         if (local_pending_command)
1425                 free(local_pending_command);
1426
1427         if (jobList.fg && !jobList.fg->runningProgs) {
1428                 removeJob(&jobList, jobList.fg);
1429         }
1430 }
1431 #endif
1432
1433
1434 int shell_main(int argc_l, char **argv_l)
1435 {
1436         int opt, interactive=FALSE;
1437         FILE *input = stdin;
1438         argc = argc_l;
1439         argv = argv_l;
1440
1441
1442         //if (argv[0] && argv[0][0] == '-') {
1443         //      builtin_source("/etc/profile");
1444         //}
1445
1446         while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1447                 switch (opt) {
1448                         case 'c':
1449                                 input = NULL;
1450                                 if (local_pending_command != 0)
1451                                         fatalError("multiple -c arguments\n");
1452                                 local_pending_command = xstrdup(argv[optind]);
1453                                 optind++;
1454                                 argv = argv+optind;
1455                                 break;
1456 #ifdef BB_FEATURE_SH_ENVIRONMENT
1457                         case 'x':
1458                                 showXtrace = TRUE;
1459                                 break;
1460 #endif
1461                         case 'i':
1462                                 interactive = TRUE;
1463                                 break;
1464                         default:
1465                                 usage(shell_usage);
1466                 }
1467         }
1468         /* A shell is interactive if the `-i' flag was given, or if all of
1469          * the following conditions are met:
1470          *        no -c command
1471          *    no arguments remaining or the -s flag given
1472          *    standard input is a terminal
1473          *    standard output is a terminal
1474          *    Refer to Posix.2, the description of the `sh' utility. */
1475         if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1476                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1477                 /* Looks like they want an interactive shell */
1478                 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1479                 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1480         } else if (local_pending_command==NULL) {
1481                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1482                 input = xfopen(argv[optind], "r");
1483         }
1484
1485         /* initialize the cwd -- this is never freed...*/
1486         cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1487         getcwd(cwd, sizeof(char)*MAX_LINE);
1488
1489 #ifdef BB_FEATURE_CLEAN_UP
1490         atexit(free_memory);
1491 #endif
1492
1493 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1494         win_changed(0);
1495 #endif
1496
1497         return (busy_loop(input));
1498 }