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