164d6f5c5cfa3b0a2d6e325a231ba07d457a311f
[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 //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[9],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         my_getpwuid(user, geteuid());
682         
683         /* get HostName */
684         gethostname(buf, 255);
685         s = strchr(buf, '.');
686         if (s)
687                 *s = 0;
688         
689         if (source == stdin) {
690 #ifdef BB_FEATURE_SH_COMMAND_EDITING
691                 int len;
692
693                 /*
694                 ** enable command line editing only while a command line
695                 ** is actually being read; otherwise, we'll end up bequeathing
696                 ** atexit() handlers and other unwanted stuff to our
697                 ** child processes (rob@sysgo.de)
698                 */
699                 cmdedit_init();
700                 signal(SIGWINCH, win_changed);
701                 len=fprintf(stdout, "[%s@%s %s]%s", user, buf, 
702                                 get_last_path_component(cwd), prompt);
703                 fflush(stdout);
704                 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
705                 sprintf(promptStr, "[%s@%s %s]%s", user, buf, 
706                                 get_last_path_component(cwd), prompt);
707                 cmdedit_read_input(promptStr, command);
708                 free( promptStr);
709                 cmdedit_terminate();
710                 signal(SIGWINCH, SIG_DFL);
711                 return 0;
712 #else
713                 i=strlen(cwd);
714                 i--;
715                 if (i>1){
716                         while ((i>0) && (*(cwd+i)!='/') ) i--;
717                         if (*(cwd+i)=='/') i++;
718                 }
719                 
720                 fprintf(stdout, "[%s@%s %s]%s",user, buf, (cwd+i), prompt);
721                 fflush(stdout);
722 #endif
723         }
724
725         if (!fgets(command, BUFSIZ - 2, source)) {
726                 if (source == stdin)
727                         printf("\n");
728                 return 1;
729         }
730
731         /* remove trailing newline */
732         command[strlen(command) - 1] = '\0';
733
734         return 0;
735 }
736
737 #ifdef BB_FEATURE_SH_ENVIRONMENT
738 static char* itoa(register int i)
739 {
740         static char a[7]; /* Max 7 ints */
741         register char *b = a + sizeof(a) - 1;
742         int   sign = (i < 0);
743
744         if (sign)
745                 i = -i;
746         *b = 0;
747         do
748         {
749                 *--b = '0' + (i % 10);
750                 i /= 10;
751         }
752         while (i);
753         if (sign)
754                 *--b = '-';
755         return b;
756 }
757 #endif
758
759 static void globLastArgument(struct childProgram *prog, int *argcPtr,
760                                                          int *argcAllocedPtr)
761 {
762         int argc_l = *argcPtr;
763         int argcAlloced = *argcAllocedPtr;
764         int rc;
765         int flags;
766         int i;
767         char *src, *dst, *var;
768
769         if (argc_l > 1) {                               /* cmd->globResult is already initialized */
770                 flags = GLOB_APPEND;
771                 i = prog->globResult.gl_pathc;
772         } else {
773                 prog->freeGlob = 1;
774                 flags = 0;
775                 i = 0;
776         }
777         /* do shell variable substitution */
778         if(*prog->argv[argc_l - 1] == '$') {
779                 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
780                         prog->argv[argc_l - 1] = var;
781                 } 
782 #ifdef BB_FEATURE_SH_ENVIRONMENT
783                 else {
784                         switch(*(prog->argv[argc_l - 1] + 1)) {
785                                 case '?':
786                                         prog->argv[argc_l - 1] = itoa(lastReturnCode);
787                                         break;
788                                 case '$':
789                                         prog->argv[argc_l - 1] = itoa(getpid());
790                                         break;
791                                 case '#':
792                                         prog->argv[argc_l - 1] = itoa(argc-1);
793                                         break;
794                                 case '!':
795                                         if (lastBgPid==-1)
796                                                 *(prog->argv[argc_l - 1])='\0';
797                                         else
798                                                 prog->argv[argc_l - 1] = itoa(lastBgPid);
799                                         break;
800                                 case '0':case '1':case '2':case '3':case '4':
801                                 case '5':case '6':case '7':case '8':case '9':
802                                         {
803                                                 int index=*(prog->argv[argc_l - 1] + 1)-48;
804                                                 if (index >= argc) {
805                                                         *(prog->argv[argc_l - 1])='\0';
806                                                 } else {
807                                                         prog->argv[argc_l - 1] = argv[index];
808                                                 }
809                                         }
810                                         break;
811                         }
812                 }
813 #endif
814         }
815
816         if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
817                 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
818                 if (rc == GLOB_NOSPACE) {
819                         error_msg("out of space during glob operation\n");
820                         return;
821                 } else if (rc == GLOB_NOMATCH ||
822                            (!rc && (prog->globResult.gl_pathc - i) == 1 &&
823                                 strcmp(prog->argv[argc_l - 1],
824                                                 prog->globResult.gl_pathv[i]) == 0)) {
825                         /* we need to remove whatever \ quoting is still present */
826                         src = dst = prog->argv[argc_l - 1];
827                         while (*src) {
828                                 if (*src == '\\') {
829                                         src++; 
830                                         *dst++ = process_escape_sequence(&src);
831                                 } else { 
832                                         *dst++ = *src;
833                                         src++;
834                                 }
835                         }
836                         *dst = '\0';
837                 } else if (!rc) {
838                         argcAlloced += (prog->globResult.gl_pathc - i);
839                         prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
840                         memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
841                                    sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
842                         argc_l += (prog->globResult.gl_pathc - i - 1);
843                 }
844         }else{
845                         src = dst = prog->argv[argc_l - 1];
846                         while (*src) {
847                                 if (*src == '\\') {
848                                         src++; 
849                                         *dst++ = process_escape_sequence(&src);
850                                 } else { 
851                                         *dst++ = *src;
852                                         src++;
853                                 }
854                         }
855                         *dst = '\0';
856                         
857                         prog->globResult.gl_pathc=0;
858                         if (flags==0)
859                                 prog->globResult.gl_pathv=NULL;
860         }
861         *argcAllocedPtr = argcAlloced;
862         *argcPtr = argc_l;
863 }
864
865 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
866    line). If a valid command is found, commandPtr is set to point to
867    the beginning of the next command (if the original command had more 
868    then one job associated with it) or NULL if no more commands are 
869    present. */
870 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
871 {
872         char *command;
873         char *returnCommand = NULL;
874         char *src, *buf, *chptr;
875         int argc_l = 0;
876         int done = 0;
877         int argvAlloced;
878         int i;
879         char quote = '\0';
880         int count;
881         struct childProgram *prog;
882
883         /* skip leading white space */
884         while (**commandPtr && isspace(**commandPtr))
885                 (*commandPtr)++;
886
887         /* this handles empty lines or leading '#' characters */
888         if (!**commandPtr || (**commandPtr == '#')) {
889                 job->numProgs=0;
890                 return 0;
891         }
892
893         *inBg = 0;
894         job->numProgs = 1;
895         job->progs = xmalloc(sizeof(*job->progs));
896
897         /* We set the argv elements to point inside of this string. The 
898            memory is freed by freeJob(). Allocate twice the original
899            length in case we need to quote every single character.
900
901            Getting clean memory relieves us of the task of NULL 
902            terminating things and makes the rest of this look a bit 
903            cleaner (though it is, admittedly, a tad less efficient) */
904         job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
905         job->text = NULL;
906
907         prog = job->progs;
908         prog->numRedirections = 0;
909         prog->redirections = NULL;
910         prog->freeGlob = 0;
911         prog->isStopped = 0;
912
913         argvAlloced = 5;
914         prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
915         prog->argv[0] = job->cmdBuf;
916
917         buf = command;
918         src = *commandPtr;
919         while (*src && !done) {
920                 if (quote == *src) {
921                         quote = '\0';
922                 } else if (quote) {
923                         if (*src == '\\') {
924                                 src++;
925                                 if (!*src) {
926                                         error_msg("character expected after \\\n");
927                                         freeJob(job);
928                                         return 1;
929                                 }
930
931                                 /* in shell, "\'" should yield \' */
932                                 if (*src != quote) {
933                                         *buf++ = '\\';
934                                         *buf++ = '\\';
935                                 }
936                         } else if (*src == '*' || *src == '?' || *src == '[' ||
937                                            *src == ']') *buf++ = '\\';
938                         *buf++ = *src;
939                 } else if (isspace(*src)) {
940                         if (*prog->argv[argc_l]) {
941                                 buf++, argc_l++;
942                                 /* +1 here leaves room for the NULL which ends argv */
943                                 if ((argc_l + 1) == argvAlloced) {
944                                         argvAlloced += 5;
945                                         prog->argv = xrealloc(prog->argv,
946                                                                                   sizeof(*prog->argv) *
947                                                                                   argvAlloced);
948                                 }
949                                 globLastArgument(prog, &argc_l, &argvAlloced);
950                                 prog->argv[argc_l] = buf;
951                         }
952                 } else
953                         switch (*src) {
954                         case '"':
955                         case '\'':
956                                 quote = *src;
957                                 break;
958
959                         case '#':                       /* comment */
960                                 if (*(src-1)== '$')
961                                         *buf++ = *src;
962                                 else
963                                         done = 1;
964                                 break;
965
966                         case '>':                       /* redirections */
967                         case '<':
968                                 i = prog->numRedirections++;
969                                 prog->redirections = xrealloc(prog->redirections,
970                                                                                           sizeof(*prog->redirections) *
971                                                                                           (i + 1));
972
973                                 prog->redirections[i].fd = -1;
974                                 if (buf != prog->argv[argc_l]) {
975                                         /* the stuff before this character may be the file number 
976                                            being redirected */
977                                         prog->redirections[i].fd =
978                                                 strtol(prog->argv[argc_l], &chptr, 10);
979
980                                         if (*chptr && *prog->argv[argc_l]) {
981                                                 buf++, argc_l++;
982                                                 globLastArgument(prog, &argc_l, &argvAlloced);
983                                                 prog->argv[argc_l] = buf;
984                                         }
985                                 }
986
987                                 if (prog->redirections[i].fd == -1) {
988                                         if (*src == '>')
989                                                 prog->redirections[i].fd = 1;
990                                         else
991                                                 prog->redirections[i].fd = 0;
992                                 }
993
994                                 if (*src++ == '>') {
995                                         if (*src == '>')
996                                                 prog->redirections[i].type =
997                                                         REDIRECT_APPEND, src++;
998                                         else
999                                                 prog->redirections[i].type = REDIRECT_OVERWRITE;
1000                                 } else {
1001                                         prog->redirections[i].type = REDIRECT_INPUT;
1002                                 }
1003
1004                                 /* This isn't POSIX sh compliant. Oh well. */
1005                                 chptr = src;
1006                                 while (isspace(*chptr))
1007                                         chptr++;
1008
1009                                 if (!*chptr) {
1010                                         error_msg("file name expected after %c\n", *src);
1011                                         freeJob(job);
1012                                         job->numProgs=0;
1013                                         return 1;
1014                                 }
1015
1016                                 prog->redirections[i].filename = buf;
1017                                 while (*chptr && !isspace(*chptr))
1018                                         *buf++ = *chptr++;
1019
1020                                 src = chptr - 1;        /* we src++ later */
1021                                 prog->argv[argc_l] = ++buf;
1022                                 break;
1023
1024                         case '|':                       /* pipe */
1025                                 /* finish this command */
1026                                 if (*prog->argv[argc_l])
1027                                         argc_l++;
1028                                 if (!argc_l) {
1029                                         error_msg("empty command in pipe\n");
1030                                         freeJob(job);
1031                                         job->numProgs=0;
1032                                         return 1;
1033                                 }
1034                                 prog->argv[argc_l] = NULL;
1035
1036                                 /* and start the next */
1037                                 job->numProgs++;
1038                                 job->progs = xrealloc(job->progs,
1039                                                                           sizeof(*job->progs) * job->numProgs);
1040                                 prog = job->progs + (job->numProgs - 1);
1041                                 prog->numRedirections = 0;
1042                                 prog->redirections = NULL;
1043                                 prog->freeGlob = 0;
1044                                 prog->isStopped = 0;
1045                                 argc_l = 0;
1046
1047                                 argvAlloced = 5;
1048                                 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1049                                 prog->argv[0] = ++buf;
1050
1051                                 src++;
1052                                 while (*src && isspace(*src))
1053                                         src++;
1054
1055                                 if (!*src) {
1056                                         error_msg("empty command in pipe\n");
1057                                         freeJob(job);
1058                                         job->numProgs=0;
1059                                         return 1;
1060                                 }
1061                                 src--;                  /* we'll ++ it at the end of the loop */
1062
1063                                 break;
1064
1065                         case '&':                       /* background */
1066                                 *inBg = 1;
1067                         case ';':                       /* multiple commands */
1068                                 done = 1;
1069                                 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1070                                 break;
1071
1072 #ifdef BB_FEATURE_SH_BACKTICKS
1073                         case '`':
1074                                 /* Exec a backtick-ed command */
1075                                 {
1076                                         char* charptr1=NULL, *charptr2;
1077                                         char* ptr=NULL;
1078                                         struct job *newJob;
1079                                         struct jobSet njobList = { NULL, NULL };
1080                                         int pipefd[2];
1081                                         int size;
1082
1083                                         ptr=strchr(++src, '`');
1084                                         if (ptr==NULL) {
1085                                                 fprintf(stderr, "Unmatched '`' in command\n");
1086                                                 freeJob(job);
1087                                                 return 1;
1088                                         }
1089
1090                                         /* Make some space to hold just the backticked command */
1091                                         charptr1 = charptr2 = xmalloc(1+ptr-src);
1092                                         memcpy(charptr1, src, ptr-src);
1093                                         charptr1[ptr-src] = '\0';
1094                                         newJob = xmalloc(sizeof(struct job));
1095                                         /* Now parse and run the backticked command */
1096                                         if (!parseCommand(&charptr1, newJob, &njobList, inBg) 
1097                                                         && newJob->numProgs) {
1098                                                 pipe(pipefd);
1099                                                 runCommand(newJob, &njobList, 0, pipefd);
1100                                         }
1101                                         checkJobs(jobList);
1102                                         freeJob(newJob);
1103                                         free(charptr2);
1104                                         
1105                                         /* Make a copy of any stuff left over in the command 
1106                                          * line after the second backtick */
1107                                         charptr2 = xmalloc(strlen(ptr)+1);
1108                                         memcpy(charptr2, ptr+1, strlen(ptr));
1109
1110
1111                                         /* Copy the output from the backtick-ed command into the
1112                                          * command line, making extra room as needed  */
1113                                         --src;
1114                                         charptr1 = xmalloc(BUFSIZ);
1115                                         while ( (size=full_read(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1116                                                 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1117                                                 if (newSize > BUFSIZ) {
1118                                                         *commandPtr=xrealloc(*commandPtr, src - *commandPtr + 
1119                                                                         size + 1 + strlen(charptr2));
1120                                                 }
1121                                                 memcpy(src, charptr1, size); 
1122                                                 src+=size;
1123                                         }
1124                                         free(charptr1);
1125                                         close(pipefd[0]);
1126                                         if (*(src-1)=='\n')
1127                                                 --src;
1128
1129                                         /* Now paste into the *commandPtr all the stuff 
1130                                          * leftover after the second backtick */
1131                                         memcpy(src, charptr2, strlen(charptr2)+1);
1132                                         free(charptr2);
1133
1134                                         /* Now recursively call parseCommand to deal with the new
1135                                          * and improved version of the command line with the backtick
1136                                          * results expanded in place... */
1137                                         freeJob(job);
1138                                         return(parseCommand(commandPtr, job, jobList, inBg));
1139                                 }
1140                                 break;
1141 #endif // BB_FEATURE_SH_BACKTICKS
1142
1143                         case '\\':
1144                                 src++;
1145                                 if (!*src) {
1146                                         error_msg("character expected after \\\n");
1147                                         freeJob(job);
1148                                         return 1;
1149                                 }
1150                                 if (*src == '*' || *src == '[' || *src == ']'
1151                                         || *src == '?') *buf++ = '\\';
1152                                 /* fallthrough */
1153                         default:
1154                                 *buf++ = *src;
1155                         }
1156
1157                 src++;
1158         }
1159
1160         if (*prog->argv[argc_l]) {
1161                 argc_l++;
1162                 globLastArgument(prog, &argc_l, &argvAlloced);
1163         }
1164         if (!argc_l) {
1165                 freeJob(job);
1166                 return 0;
1167         }
1168         prog->argv[argc_l] = NULL;
1169
1170         if (!returnCommand) {
1171                 job->text = xmalloc(strlen(*commandPtr) + 1);
1172                 strcpy(job->text, *commandPtr);
1173         } else {
1174                 /* This leaves any trailing spaces, which is a bit sloppy */
1175                 count = returnCommand - *commandPtr;
1176                 job->text = xmalloc(count + 1);
1177                 strncpy(job->text, *commandPtr, count);
1178                 job->text[count] = '\0';
1179         }
1180
1181         *commandPtr = returnCommand;
1182         
1183         return 0;
1184 }
1185
1186 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1187 {
1188         struct job *theJob;
1189         int i;
1190         int nextin, nextout;
1191         int pipefds[2];                         /* pipefd[0] is for reading */
1192         struct builtInCommand *x;
1193 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1194         struct BB_applet search_applet, *applet;
1195 #endif
1196
1197         nextin = 0, nextout = 1;
1198         for (i = 0; i < newJob->numProgs; i++) {
1199                 if ((i + 1) < newJob->numProgs) {
1200                         pipe(pipefds);
1201                         nextout = pipefds[1];
1202                 } else {
1203                         if (outPipe[1]!=-1) {
1204                                 nextout = outPipe[1];
1205                         } else {
1206                                 nextout = 1;
1207                         }
1208                 }
1209
1210 #ifdef BB_FEATURE_SH_ENVIRONMENT
1211                 if (showXtrace==TRUE) {
1212                         int j;
1213                         fprintf(stderr, "+ ");
1214                         for (j = 0; newJob->progs[i].argv[j]; j++)
1215                                 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1216                         fprintf(stderr, "\n");
1217                 }
1218 #endif
1219
1220                 /* Check if the command matches any non-forking builtins */
1221                 for (x = bltins; x->cmd; x++) {
1222                         if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1223                                 return(x->function(newJob, jobList));
1224                         }
1225                 }
1226
1227                 if (!(newJob->progs[i].pid = fork())) {
1228                         signal(SIGTTOU, SIG_DFL);
1229
1230                         if (outPipe[1]!=-1) {
1231                                 close(outPipe[0]);
1232                         }
1233                         if (nextin != 0) {
1234                                 dup2(nextin, 0);
1235                                 close(nextin);
1236                         }
1237
1238                         if (nextout != 1) {
1239                                 dup2(nextout, 1);
1240                                 dup2(nextout, 2);
1241                                 close(nextout);
1242                                 close(pipefds[0]);
1243                         }
1244
1245                         /* explicit redirections override pipes */
1246                         setupRedirections(newJob->progs + i);
1247
1248                         /* Check if the command matches any of the other builtins */
1249                         for (x = bltins_forking; x->cmd; x++) {
1250                                 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1251                                         applet_name=x->cmd;
1252                                         exit (x->function(newJob, jobList));
1253                                 }
1254                         }
1255 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1256                         /* Check if the command matches any busybox internal
1257                          * commands ("applets") here.  Following discussions from
1258                          * November 2000 on busybox@opensource.lineo.com, don't use
1259                          * get_last_path_component().  This way explicit (with
1260                          * slashes) filenames will never be interpreted as an
1261                          * applet, just like with builtins.  This way the user can
1262                          * override an applet with an explicit filename reference.
1263                          * The only downside to this change is that an explicit
1264                          * /bin/foo invocation fill fork and exec /bin/foo, even if
1265                          * /bin/foo is a symlink to busybox.
1266                          */
1267                         search_applet.name = newJob->progs[i].argv[0];
1268
1269 #ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
1270                         /* If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then
1271                          * if you run /bin/cat, it will use BusyBox cat even if 
1272                          * /bin/cat exists on the filesystem and is _not_ busybox.
1273                          * Some systems want this, others do not.  Choose wisely.  :-)
1274                          */
1275                         search_applet.name = get_last_path_component(search_applet.name);
1276 #endif
1277
1278                         /* Do a binary search to find the applet entry given the name. */
1279                         applet = bsearch(&search_applet, applets, NUM_APPLETS,
1280                                         sizeof(struct BB_applet), applet_name_compare);
1281                         if (applet != NULL) {
1282                                 int argc_l;
1283                                 char** argv=newJob->progs[i].argv;
1284                                 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1285                                 applet_name=applet->name;
1286                                 optind = 1;
1287                                 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1288                         }
1289 #endif
1290
1291                         execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1292                         error_msg_and_die("%s: %s\n", newJob->progs[i].argv[0],
1293                                         strerror(errno));
1294                 }
1295                 if (outPipe[1]!=-1) {
1296                         close(outPipe[1]);
1297                 }
1298
1299                 /* put our child in the process group whose leader is the
1300                    first process in this pipe */
1301                 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1302                 if (nextin != 0)
1303                         close(nextin);
1304                 if (nextout != 1)
1305                         close(nextout);
1306
1307                 /* If there isn't another process, nextin is garbage 
1308                    but it doesn't matter */
1309                 nextin = pipefds[0];
1310         }
1311
1312         newJob->pgrp = newJob->progs[0].pid;
1313
1314         /* find the ID for the theJob to use */
1315         newJob->jobId = 1;
1316         for (theJob = jobList->head; theJob; theJob = theJob->next)
1317                 if (theJob->jobId >= newJob->jobId)
1318                         newJob->jobId = theJob->jobId + 1;
1319
1320         /* add the theJob to the list of running jobs */
1321         if (!jobList->head) {
1322                 theJob = jobList->head = xmalloc(sizeof(*theJob));
1323         } else {
1324                 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1325                 theJob->next = xmalloc(sizeof(*theJob));
1326                 theJob = theJob->next;
1327         }
1328
1329         *theJob = *newJob;
1330         theJob->next = NULL;
1331         theJob->runningProgs = theJob->numProgs;
1332         theJob->stoppedProgs = 0;
1333
1334         if (inBg) {
1335                 /* we don't wait for background theJobs to return -- append it 
1336                    to the list of backgrounded theJobs and leave it alone */
1337                 printf("[%d] %d\n", theJob->jobId,
1338                            newJob->progs[newJob->numProgs - 1].pid);
1339 #ifdef BB_FEATURE_SH_ENVIRONMENT
1340                 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1341 #endif
1342         } else {
1343                 jobList->fg = theJob;
1344
1345                 /* move the new process group into the foreground */
1346                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1347                 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1348                         perror("tcsetpgrp");
1349         }
1350
1351         return 0;
1352 }
1353
1354 static int busy_loop(FILE * input)
1355 {
1356         char *command;
1357         char *nextCommand = NULL;
1358         struct job newJob;
1359         pid_t  parent_pgrp;
1360         int i;
1361         int inBg;
1362         int status;
1363         newJob.jobContext = REGULAR_JOB_CONTEXT;
1364
1365         /* save current owner of TTY so we can restore it on exit */
1366         parent_pgrp = tcgetpgrp(0);
1367
1368         command = (char *) xcalloc(BUFSIZ, sizeof(char));
1369
1370         /* don't pay any attention to this signal; it just confuses 
1371            things and isn't really meant for shells anyway */
1372         signal(SIGTTOU, SIG_IGN);
1373
1374         while (1) {
1375                 if (!jobList.fg) {
1376                         /* no job is in the foreground */
1377
1378                         /* see if any background processes have exited */
1379                         checkJobs(&jobList);
1380
1381                         if (!nextCommand) {
1382                                 if (getCommand(input, command))
1383                                         break;
1384                                 nextCommand = command;
1385                         }
1386
1387                         if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1388                                 newJob.numProgs) {
1389                                 int pipefds[2] = {-1,-1};
1390                                 runCommand(&newJob, &jobList, inBg, pipefds);
1391                         }
1392                         else {
1393                                 free(command);
1394                                 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1395                                 nextCommand = NULL;
1396                         }
1397                 } else {
1398                         /* a job is running in the foreground; wait for it */
1399                         i = 0;
1400                         while (!jobList.fg->progs[i].pid ||
1401                                    jobList.fg->progs[i].isStopped == 1) i++;
1402
1403                         waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1404
1405                         if (WIFEXITED(status) || WIFSIGNALED(status)) {
1406                                 /* the child exited */
1407                                 jobList.fg->runningProgs--;
1408                                 jobList.fg->progs[i].pid = 0;
1409
1410 #ifdef BB_FEATURE_SH_ENVIRONMENT
1411                                 lastReturnCode=WEXITSTATUS(status);
1412 #endif
1413                                 debug_printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1414                                 if (!jobList.fg->runningProgs) {
1415                                         /* child exited */
1416
1417                                         removeJob(&jobList, jobList.fg);
1418                                         jobList.fg = NULL;
1419                                 }
1420                         } else {
1421                                 /* the child was stopped */
1422                                 jobList.fg->stoppedProgs++;
1423                                 jobList.fg->progs[i].isStopped = 1;
1424
1425                                 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1426                                         printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1427                                                    "Stopped", jobList.fg->text);
1428                                         jobList.fg = NULL;
1429                                 }
1430                         }
1431
1432                         if (!jobList.fg) {
1433                                 /* move the shell to the foreground */
1434                                 /* suppress messages when run from /linuxrc mag@sysgo.de */
1435                                 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1436                                         perror("tcsetpgrp"); 
1437                         }
1438                 }
1439         }
1440         free(command);
1441
1442         /* return controlling TTY back to parent process group before exiting */
1443         if (tcsetpgrp(0, parent_pgrp))
1444                 perror("tcsetpgrp");
1445
1446         /* return exit status if called with "-c" */
1447         if (input == NULL && WIFEXITED(status))
1448                 return WEXITSTATUS(status);
1449         
1450         return 0;
1451 }
1452
1453
1454 #ifdef BB_FEATURE_CLEAN_UP
1455 void free_memory(void)
1456 {
1457         if (promptStr)
1458                 free(promptStr);
1459         if (cwd)
1460                 free(cwd);
1461         if (local_pending_command)
1462                 free(local_pending_command);
1463
1464         if (jobList.fg && !jobList.fg->runningProgs) {
1465                 removeJob(&jobList, jobList.fg);
1466         }
1467 }
1468 #endif
1469
1470
1471 int shell_main(int argc_l, char **argv_l)
1472 {
1473         int opt, interactive=FALSE;
1474         FILE *input = stdin;
1475         argc = argc_l;
1476         argv = argv_l;
1477
1478
1479         if (argv[0] && argv[0][0] == '-') {
1480                   FILE *input;
1481                   input = fopen("/etc/profile", "r");
1482                   if (!input) {
1483                           fprintf(stdout, "Couldn't open file '/etc/profile'\n");
1484                   } else {
1485                           /* Now run the file */
1486                           busy_loop(input);
1487                           fclose(input);
1488                   }
1489         }
1490
1491         while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
1492                 switch (opt) {
1493                         case 'c':
1494                                 input = NULL;
1495                                 if (local_pending_command != 0)
1496                                         error_msg_and_die("multiple -c arguments\n");
1497                                 local_pending_command = xstrdup(argv[optind]);
1498                                 optind++;
1499                                 argv = argv+optind;
1500                                 break;
1501 #ifdef BB_FEATURE_SH_ENVIRONMENT
1502                         case 'x':
1503                                 showXtrace = TRUE;
1504                                 break;
1505 #endif
1506                         case 'i':
1507                                 interactive = TRUE;
1508                                 break;
1509                         default:
1510                                 usage(shell_usage);
1511                 }
1512         }
1513         /* A shell is interactive if the `-i' flag was given, or if all of
1514          * the following conditions are met:
1515          *        no -c command
1516          *    no arguments remaining or the -s flag given
1517          *    standard input is a terminal
1518          *    standard output is a terminal
1519          *    Refer to Posix.2, the description of the `sh' utility. */
1520         if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1521                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1522                 /* Looks like they want an interactive shell */
1523                 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1524                 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1525         } else if (local_pending_command==NULL) {
1526                 //fprintf(stdout, "optind=%d  argv[optind]='%s'\n", optind, argv[optind]);
1527                 input = xfopen(argv[optind], "r");
1528         }
1529
1530         /* initialize the cwd -- this is never freed...*/
1531         cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1532         getcwd(cwd, sizeof(char)*MAX_LINE);
1533
1534 #ifdef BB_FEATURE_CLEAN_UP
1535         atexit(free_memory);
1536 #endif
1537
1538         win_changed(0);
1539         return (busy_loop(input));
1540 }