1 /* vi: set sw=4 ts=4: */
3 * lash -- the BusyBox Lame-Ass SHell
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
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."
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.
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.
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
37 #include <sys/ioctl.h>
40 #ifdef BB_FEATURE_SH_COMMAND_EDITING
44 #define MAX_READ 128 /* size of input buffer for `read' builtin */
45 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
48 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
53 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
57 struct redirectionSpecifier {
58 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
64 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */
74 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */
77 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */
85 struct builtInCommand {
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
92 /* function prototypes for builtins */
93 static int builtin_cd(struct job *cmd, struct jobSet *junk);
94 static int builtin_env(struct job *dummy, struct jobSet *junk);
95 static int builtin_exit(struct job *cmd, struct jobSet *junk);
96 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
97 static int builtin_help(struct job *cmd, struct jobSet *junk);
98 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
99 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
100 static int builtin_export(struct job *cmd, struct jobSet *junk);
101 static int builtin_source(struct job *cmd, struct jobSet *jobList);
102 static int builtin_unset(struct job *cmd, struct jobSet *junk);
103 static int builtin_read(struct job *cmd, struct jobSet *junk);
106 /* function prototypes for shell stuff */
107 static void checkJobs(struct jobSet *jobList);
108 static int getCommand(FILE * source, char *command);
109 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
110 static int setupRedirections(struct childProgram *prog);
111 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg);
112 static int busy_loop(FILE * input);
115 /* Table of built-in functions (these are non-forking builtins, meaning they
116 * can change global variables in the parent shell process but they will not
117 * work with pipes and redirects; 'unset foo | whatever' will not work) */
118 static struct builtInCommand bltins[] = {
119 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
120 {"cd", "Change working directory", "cd [dir]", builtin_cd},
121 {"exit", "Exit from shell()", "exit", builtin_exit},
122 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
123 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
124 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
125 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
126 {"read", "Input environment variable", "read [VAR]", builtin_read},
127 {NULL, NULL, NULL, NULL}
130 /* Table of forking built-in functions (things that fork cannot change global
131 * variables in the parent process, such as the current working directory) */
132 static struct builtInCommand bltins_forking[] = {
133 {"env", "Print all environment variables", "env", builtin_env},
134 {"pwd", "Print current directory", "pwd", builtin_pwd},
135 {".", "Source-in and run commands in a file", ". filename", builtin_source},
136 {"help", "List shell built-in commands", "help", builtin_help},
137 {NULL, NULL, NULL, NULL}
140 static char *prompt = "# ";
141 static char *cwd = NULL;
142 static char *local_pending_command = NULL;
144 #ifdef BB_FEATURE_SH_COMMAND_EDITING
145 void win_changed(int junk)
147 struct winsize win = { 0, 0, 0, 0 };
148 ioctl(0, TIOCGWINSZ, &win);
149 if (win.ws_col > 0) {
150 cmdedit_setwidth( win.ws_col - 1);
156 /* built-in 'cd <path>' handler */
157 static int builtin_cd(struct job *cmd, struct jobSet *junk)
161 if (!cmd->progs[0].argv[1] == 1)
162 newdir = getenv("HOME");
164 newdir = cmd->progs[0].argv[1];
166 printf("cd: %s: %s\n", newdir, strerror(errno));
169 getcwd(cwd, sizeof(cwd));
174 /* built-in 'env' handler */
175 static int builtin_env(struct job *dummy, struct jobSet *junk)
179 for (e = environ; *e; e++) {
180 fprintf(stdout, "%s\n", *e);
185 /* built-in 'exit' handler */
186 static int builtin_exit(struct job *cmd, struct jobSet *junk)
188 if (!cmd->progs[0].argv[1] == 1)
191 return(atoi(cmd->progs[0].argv[1]));
194 /* built-in 'fg' and 'bg' handler */
195 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
198 struct job *job=NULL;
200 if (!jobList->head) {
201 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
202 errorMsg("%s: exactly one argument is expected\n",
203 cmd->progs[0].argv[0]);
206 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
207 errorMsg("%s: bad argument '%s'\n",
208 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
210 for (job = jobList->head; job; job = job->next) {
211 if (job->jobId == jobNum) {
221 errorMsg("%s: unknown job %d\n",
222 cmd->progs[0].argv[0], jobNum);
226 if (*cmd->progs[0].argv[0] == 'f') {
227 /* Make this job the foreground job */
228 /* suppress messages when run from /linuxrc mag@sysgo.de */
229 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
234 /* Restart the processes in the job */
235 for (i = 0; i < job->numProgs; i++)
236 job->progs[i].isStopped = 0;
238 kill(-job->pgrp, SIGCONT);
240 job->stoppedProgs = 0;
245 /* built-in 'help' handler */
246 static int builtin_help(struct job *dummy, struct jobSet *junk)
248 struct builtInCommand *x;
250 fprintf(stdout, "\nBuilt-in commands:\n");
251 fprintf(stdout, "-------------------\n");
252 for (x = bltins; x->cmd; x++) {
253 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
255 for (x = bltins_forking; x->cmd; x++) {
256 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
258 fprintf(stdout, "\n\n");
262 /* built-in 'jobs' handler */
263 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
268 for (job = jobList->head; job; job = job->next) {
269 if (job->runningProgs == job->stoppedProgs)
270 statusString = "Stopped";
272 statusString = "Running";
274 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
280 /* built-in 'pwd' handler */
281 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
283 getcwd(cwd, sizeof(cwd));
284 fprintf(stdout, "%s\n", cwd);
288 /* built-in 'export VAR=value' handler */
289 static int builtin_export(struct job *cmd, struct jobSet *junk)
293 if (!cmd->progs[0].argv[1] == 1) {
294 return (builtin_env(cmd, junk));
296 res = putenv(cmd->progs[0].argv[1]);
298 fprintf(stdout, "export: %s\n", strerror(errno));
302 /* built-in 'read VAR' handler */
303 static int builtin_read(struct job *cmd, struct jobSet *junk)
305 int res = 0, len, newlen;
307 char string[MAX_READ];
309 if (cmd->progs[0].argv[1]) {
310 /* argument (VAR) given: put "VAR=" into buffer */
311 strcpy(string, cmd->progs[0].argv[1]);
312 len = strlen(string);
315 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
316 newlen = strlen(string);
318 string[--newlen] = '\0'; /* chomp trailing newline */
320 ** string should now contain "VAR=<value>"
321 ** copy it (putenv() won't do that, so we must make sure
322 ** the string resides in a static buffer!)
325 if((s = strdup(string)))
328 fprintf(stdout, "read: %s\n", strerror(errno));
331 fgets(string, sizeof(string), stdin);
336 /* Built-in '.' handler (read-in and execute commands from file) */
337 static int builtin_source(struct job *cmd, struct jobSet *junk)
342 if (!cmd->progs[0].argv[1] == 1)
345 input = fopen(cmd->progs[0].argv[1], "r");
347 fprintf(stdout, "Couldn't open file '%s'\n",
348 cmd->progs[0].argv[1]);
352 /* Now run the file */
353 status = busy_loop(input);
357 /* built-in 'unset VAR' handler */
358 static int builtin_unset(struct job *cmd, struct jobSet *junk)
360 if (!cmd->progs[0].argv[1] == 1) {
361 fprintf(stdout, "unset: parameter required.\n");
364 unsetenv(cmd->progs[0].argv[1]);
368 /* free up all memory from a job */
369 static void freeJob(struct job *cmd)
373 for (i = 0; i < cmd->numProgs; i++) {
374 free(cmd->progs[i].argv);
375 if (cmd->progs[i].redirections)
376 free(cmd->progs[i].redirections);
377 if (cmd->progs[i].freeGlob)
378 globfree(&cmd->progs[i].globResult);
384 memset(cmd, 0, sizeof(struct job));
387 /* remove a job from the jobList */
388 static void removeJob(struct jobSet *jobList, struct job *job)
393 if (job == jobList->head) {
394 jobList->head = job->next;
396 prevJob = jobList->head;
397 while (prevJob->next != job)
398 prevJob = prevJob->next;
399 prevJob->next = job->next;
405 /* Checks to see if any background processes have exited -- if they
406 have, figure out why and see if a job has completed */
407 static void checkJobs(struct jobSet *jobList)
414 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
415 for (job = jobList->head; job; job = job->next) {
417 while (progNum < job->numProgs &&
418 job->progs[progNum].pid != childpid) progNum++;
419 if (progNum < job->numProgs)
423 if (WIFEXITED(status) || WIFSIGNALED(status)) {
426 job->progs[progNum].pid = 0;
428 if (!job->runningProgs) {
429 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
430 removeJob(jobList, job);
435 job->progs[progNum].isStopped = 1;
437 if (job->stoppedProgs == job->numProgs) {
438 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
444 if (childpid == -1 && errno != ECHILD)
448 static int getCommand(FILE * source, char *command)
450 if (source == NULL) {
451 if (local_pending_command) {
452 /* a command specified (-c option): return it & mark it done */
453 strcpy(command, local_pending_command);
454 free(local_pending_command);
455 local_pending_command = NULL;
461 if (source == stdin) {
462 #ifdef BB_FEATURE_SH_COMMAND_EDITING
465 len=fprintf(stdout, "%s %s", cwd, prompt);
467 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
468 sprintf(promptStr, "%s %s", cwd, prompt);
469 cmdedit_read_input(promptStr, command);
473 fprintf(stdout, "%s %s", cwd, prompt);
478 if (!fgets(command, BUFSIZ - 2, source)) {
484 /* remove trailing newline */
485 command[strlen(command) - 1] = '\0';
490 static void globLastArgument(struct childProgram *prog, int *argcPtr,
494 int argcAlloced = *argcAllocedPtr;
498 char *src, *dst, *var;
500 if (argc > 1) { /* cmd->globResult is already initialized */
502 i = prog->globResult.gl_pathc;
508 /* do shell variable substitution */
509 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
510 prog->argv[argc - 1] = var;
512 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
513 if (rc == GLOB_NOSPACE) {
514 errorMsg("out of space during glob operation\n");
516 } else if (rc == GLOB_NOMATCH ||
517 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
518 !strcmp(prog->argv[argc - 1],
519 prog->globResult.gl_pathv[i]))) {
520 /* we need to remove whatever \ quoting is still present */
521 src = dst = prog->argv[argc - 1];
529 argcAlloced += (prog->globResult.gl_pathc - i);
531 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
532 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
533 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
534 argc += (prog->globResult.gl_pathc - i - 1);
537 *argcAllocedPtr = argcAlloced;
541 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
542 line). If a valid command is found, commandPtr is set to point to
543 the beginning of the next command (if the original command had more
544 then one job associated with it) or NULL if no more commands are
546 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
549 char *returnCommand = NULL;
550 char *src, *buf, *chptr;
557 struct childProgram *prog;
559 /* skip leading white space */
560 while (**commandPtr && isspace(**commandPtr))
563 /* this handles empty lines or leading '#' characters */
564 if (!**commandPtr || (**commandPtr == '#')) {
571 job->progs = xmalloc(sizeof(*job->progs));
573 /* We set the argv elements to point inside of this string. The
574 memory is freed by freeJob(). Allocate twice the original
575 length in case we need to quote every single character.
577 Getting clean memory relieves us of the task of NULL
578 terminating things and makes the rest of this look a bit
579 cleaner (though it is, admittedly, a tad less efficient) */
580 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
584 prog->numRedirections = 0;
585 prog->redirections = NULL;
590 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
591 prog->argv[0] = job->cmdBuf;
595 while (*src && !done) {
602 errorMsg("character expected after \\\n");
607 /* in shell, "\'" should yield \' */
610 } else if (*src == '*' || *src == '?' || *src == '[' ||
611 *src == ']') *buf++ = '\\';
613 } else if (isspace(*src)) {
614 if (*prog->argv[argc]) {
616 /* +1 here leaves room for the NULL which ends argv */
617 if ((argc + 1) == argvAlloced) {
619 prog->argv = realloc(prog->argv,
620 sizeof(*prog->argv) *
623 globLastArgument(prog, &argc, &argvAlloced);
624 prog->argv[argc] = buf;
633 case '#': /* comment */
637 case '>': /* redirections */
639 i = prog->numRedirections++;
640 prog->redirections = realloc(prog->redirections,
641 sizeof(*prog->redirections) *
644 prog->redirections[i].fd = -1;
645 if (buf != prog->argv[argc]) {
646 /* the stuff before this character may be the file number
648 prog->redirections[i].fd =
649 strtol(prog->argv[argc], &chptr, 10);
651 if (*chptr && *prog->argv[argc]) {
653 globLastArgument(prog, &argc, &argvAlloced);
654 prog->argv[argc] = buf;
658 if (prog->redirections[i].fd == -1) {
660 prog->redirections[i].fd = 1;
662 prog->redirections[i].fd = 0;
667 prog->redirections[i].type =
668 REDIRECT_APPEND, src++;
670 prog->redirections[i].type = REDIRECT_OVERWRITE;
672 prog->redirections[i].type = REDIRECT_INPUT;
675 /* This isn't POSIX sh compliant. Oh well. */
677 while (isspace(*chptr))
681 errorMsg("file name expected after %c\n", *src);
687 prog->redirections[i].filename = buf;
688 while (*chptr && !isspace(*chptr))
691 src = chptr - 1; /* we src++ later */
692 prog->argv[argc] = ++buf;
696 /* finish this command */
697 if (*prog->argv[argc])
700 errorMsg("empty command in pipe1\n");
705 prog->argv[argc] = NULL;
707 /* and start the next */
709 job->progs = realloc(job->progs,
710 sizeof(*job->progs) * job->numProgs);
711 prog = job->progs + (job->numProgs - 1);
712 prog->numRedirections = 0;
713 prog->redirections = NULL;
718 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
719 prog->argv[0] = ++buf;
722 while (*src && isspace(*src))
726 errorMsg("empty command in pipe2\n");
731 src--; /* we'll ++ it at the end of the loop */
735 case '&': /* background */
737 case ';': /* multiple commands */
739 returnCommand = *commandPtr + (src - *commandPtr) + 1;
745 errorMsg("character expected after \\\n");
749 if (*src == '*' || *src == '[' || *src == ']'
750 || *src == '?') *buf++ = '\\';
753 /* Exec a backtick-ed command */
759 ptr=strchr(++src, '`');
761 fprintf(stderr, "Unmatched '`' in command\n");
766 newcmd = xmalloc(1+ptr-src);
767 snprintf(newcmd, 1+ptr-src, src);
769 if (!parseCommand(&newcmd, &newJob, jobList, isBg) &&
771 runCommand(&newJob, jobList, *isBg);
774 /* Clip out the the backticked command from the string */
775 memmove(--src, ptr, strlen(ptr)+1);
786 if (*prog->argv[argc]) {
788 globLastArgument(prog, &argc, &argvAlloced);
794 prog->argv[argc] = NULL;
796 if (!returnCommand) {
797 job->text = xmalloc(strlen(*commandPtr) + 1);
798 strcpy(job->text, *commandPtr);
800 /* This leaves any trailing spaces, which is a bit sloppy */
801 count = returnCommand - *commandPtr;
802 job->text = xmalloc(count + 1);
803 strncpy(job->text, *commandPtr, count);
804 job->text[count] = '\0';
807 *commandPtr = returnCommand;
813 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg)
818 int pipefds[2]; /* pipefd[0] is for reading */
819 struct builtInCommand *x;
820 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
821 const struct BB_applet *a = applets;
825 nextin = 0, nextout = 1;
826 for (i = 0; i < newJob->numProgs; i++) {
827 if ((i + 1) < newJob->numProgs) {
829 nextout = pipefds[1];
834 /* Check if the command matches any non-forking builtins */
835 for (x = bltins; x->cmd; x++) {
836 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
837 return (x->function(newJob, jobList));
841 if (!(newJob->progs[i].pid = fork())) {
842 signal(SIGTTOU, SIG_DFL);
854 /* explicit redirections override pipes */
855 setupRedirections(newJob->progs + i);
857 /* Check if the command matches any of the other builtins */
858 for (x = bltins_forking; x->cmd; x++) {
859 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
860 exit (x->function(newJob, jobList));
863 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
864 /* Check if the command matches any busybox internal commands here */
865 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
866 * works, but '/bin/cat' doesn't ) */
867 while (a->name != 0) {
868 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
870 char** argv=newJob->progs[i].argv;
871 for(argc=0;*argv!=NULL; argv++, argc++);
872 exit((*(a->main)) (argc, newJob->progs[i].argv));
878 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
879 fatalError("%s: %s\n", newJob->progs[i].argv[0],
883 /* put our child in the process group whose leader is the
884 first process in this pipe */
885 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
892 /* If there isn't another process, nextin is garbage
893 but it doesn't matter */
897 newJob->pgrp = newJob->progs[0].pid;
899 /* find the ID for the job to use */
901 for (job = jobList->head; job; job = job->next)
902 if (job->jobId >= newJob->jobId)
903 newJob->jobId = job->jobId + 1;
905 /* add the job to the list of running jobs */
906 if (!jobList->head) {
907 job = jobList->head = xmalloc(sizeof(*job));
909 for (job = jobList->head; job->next; job = job->next);
910 job->next = xmalloc(sizeof(*job));
916 job->runningProgs = job->numProgs;
917 job->stoppedProgs = 0;
920 /* we don't wait for background jobs to return -- append it
921 to the list of backgrounded jobs and leave it alone */
922 printf("[%d] %d\n", job->jobId,
923 newJob->progs[newJob->numProgs - 1].pid);
927 /* move the new process group into the foreground */
928 /* suppress messages when run from /linuxrc mag@sysgo.de */
929 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
936 static int setupRedirections(struct childProgram *prog)
941 struct redirectionSpecifier *redir = prog->redirections;
943 for (i = 0; i < prog->numRedirections; i++, redir++) {
944 switch (redir->type) {
948 case REDIRECT_OVERWRITE:
949 mode = O_RDWR | O_CREAT | O_TRUNC;
951 case REDIRECT_APPEND:
952 mode = O_RDWR | O_CREAT | O_APPEND;
956 openfd = open(redir->filename, mode, 0666);
958 /* this could get lost if stderr has been redirected, but
959 bash and ash both lose it as well (though zsh doesn't!) */
960 errorMsg("error opening %s: %s\n", redir->filename,
965 if (openfd != redir->fd) {
966 dup2(openfd, redir->fd);
975 static int busy_loop(FILE * input)
978 char *nextCommand = NULL;
979 struct jobSet jobList = { NULL, NULL };
986 /* save current owner of TTY so we can restore it on exit */
987 parent_pgrp = tcgetpgrp(0);
989 command = (char *) calloc(BUFSIZ, sizeof(char));
991 /* don't pay any attention to this signal; it just confuses
992 things and isn't really meant for shells anyway */
993 signal(SIGTTOU, SIG_IGN);
997 /* no job is in the foreground */
999 /* see if any background processes have exited */
1000 checkJobs(&jobList);
1003 if (getCommand(input, command))
1005 nextCommand = command;
1008 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1010 runCommand(&newJob, &jobList, inBg);
1015 /* a job is running in the foreground; wait for it */
1017 while (!jobList.fg->progs[i].pid ||
1018 jobList.fg->progs[i].isStopped) i++;
1020 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1022 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1023 /* the child exited */
1024 jobList.fg->runningProgs--;
1025 jobList.fg->progs[i].pid = 0;
1027 if (!jobList.fg->runningProgs) {
1030 removeJob(&jobList, jobList.fg);
1034 /* the child was stopped */
1035 jobList.fg->stoppedProgs++;
1036 jobList.fg->progs[i].isStopped = 1;
1038 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1039 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1040 "Stopped", jobList.fg->text);
1046 /* move the shell to the foreground */
1047 /* suppress messages when run from /linuxrc mag@sysgo.de */
1048 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1049 perror("tcsetpgrp");
1055 /* return controlling TTY back to parent process group before exiting */
1056 if (tcsetpgrp(0, parent_pgrp))
1057 perror("tcsetpgrp");
1059 /* return exit status if called with "-c" */
1060 if (input == NULL && WIFEXITED(status))
1061 return WEXITSTATUS(status);
1067 int shell_main(int argc, char **argv)
1069 FILE *input = stdin;
1071 /* initialize the cwd */
1072 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1074 fatalError("out of memory\n");
1076 getcwd(cwd, sizeof(char)*BUFSIZ);
1078 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1080 signal(SIGWINCH, win_changed);
1084 //if (argv[0] && argv[0][0] == '-') {
1085 // builtin_source("/etc/profile");
1089 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1090 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1092 if (argv[1][0]=='-' && argv[1][1]=='c') {
1094 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1095 if (local_pending_command == 0) {
1096 fatalError("out of memory\n");
1098 for(i=2; i<argc; i++)
1100 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1101 local_pending_command = realloc(local_pending_command,
1102 strlen(local_pending_command) + strlen(argv[i]));
1103 if (local_pending_command==NULL)
1104 fatalError("commands for -c option too long\n");
1106 strcat(local_pending_command, argv[i]);
1107 if ( (i + 1) < argc)
1108 strcat(local_pending_command, " ");
1113 else if (argv[1][0]=='-') {
1117 input = fopen(argv[1], "r");
1119 fatalError("Couldn't open file '%s': %s\n", argv[1],
1125 return (busy_loop(input));