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