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