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