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