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 /* Some function prototypes */
93 static int shell_cd(struct job *cmd, struct jobSet *junk);
94 static int shell_env(struct job *dummy, struct jobSet *junk);
95 static int shell_exit(struct job *cmd, struct jobSet *junk);
96 static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97 static int shell_help(struct job *cmd, struct jobSet *junk);
98 static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99 static int shell_pwd(struct job *dummy, struct jobSet *junk);
100 static int shell_export(struct job *cmd, struct jobSet *junk);
101 static int shell_source(struct job *cmd, struct jobSet *jobList);
102 static int shell_unset(struct job *cmd, struct jobSet *junk);
103 static int shell_read(struct job *cmd, struct jobSet *junk);
105 static void checkJobs(struct jobSet *jobList);
106 static int getCommand(FILE * source, char *command);
107 static int parseCommand(char **commandPtr, struct job *job, int *isBg);
108 static int setupRedirections(struct childProgram *prog);
109 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
110 static int busy_loop(FILE * input);
113 /* Table of built-in functions */
114 static struct builtInCommand bltins[] = {
115 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
116 {"cd", "Change working directory", "cd [dir]", shell_cd},
117 {"exit", "Exit from shell()", "exit", shell_exit},
118 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
119 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
120 {"export", "Set environment variable", "export [VAR=value]", shell_export},
121 {"unset", "Unset environment variable", "unset VAR", shell_unset},
122 {"read", "Input environment variable", "read [VAR]", shell_read},
123 {NULL, NULL, NULL, NULL}
126 /* Table of built-in functions */
127 static struct builtInCommand bltins_forking[] = {
128 {"env", "Print all environment variables", "env", shell_env},
129 {"pwd", "Print current directory", "pwd", shell_pwd},
130 {".", "Source-in and run commands in a file", ". filename", shell_source},
131 {"help", "List shell built-in commands", "help", shell_help},
132 {NULL, NULL, NULL, NULL}
135 static const char shell_usage[] =
137 " or: sh -c command [args]...\n"
138 #ifndef BB_FEATURE_TRIVIAL_HELP
139 "\nlash: The BusyBox command interpreter (shell).\n\n"
143 static char *prompt = "# ";
144 static char *cwd = NULL;
145 static char *local_pending_command = NULL;
147 #ifdef BB_FEATURE_SH_COMMAND_EDITING
148 void win_changed(int sig)
150 struct winsize win = { 0, 0 };
151 ioctl(0, TIOCGWINSZ, &win);
152 if (win.ws_col > 0) {
153 cmdedit_setwidth( win.ws_col - 1);
159 /* built-in 'cd <path>' handler */
160 static int shell_cd(struct job *cmd, struct jobSet *junk)
164 if (!cmd->progs[0].argv[1] == 1)
165 newdir = getenv("HOME");
167 newdir = cmd->progs[0].argv[1];
169 printf("cd: %s: %s\n", newdir, strerror(errno));
172 getcwd(cwd, sizeof(cwd));
177 /* built-in 'env' handler */
178 static int shell_env(struct job *dummy, struct jobSet *junk)
182 for (e = environ; *e; e++) {
183 fprintf(stdout, "%s\n", *e);
188 /* built-in 'exit' handler */
189 static int shell_exit(struct job *cmd, struct jobSet *junk)
191 if (!cmd->progs[0].argv[1] == 1)
194 return(atoi(cmd->progs[0].argv[1]));
197 /* built-in 'fg' and 'bg' handler */
198 static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
201 struct job *job=NULL;
203 if (!jobList->head) {
204 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
205 fprintf(stderr, "%s: exactly one argument is expected\n",
206 cmd->progs[0].argv[0]);
209 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
210 fprintf(stderr, "%s: bad argument '%s'\n",
211 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
213 for (job = jobList->head; job; job = job->next) {
214 if (job->jobId == jobNum) {
224 fprintf(stderr, "%s: unknown job %d\n",
225 cmd->progs[0].argv[0], jobNum);
229 if (*cmd->progs[0].argv[0] == 'f') {
230 /* Make this job the foreground job */
231 /* suppress messages when run from /linuxrc mag@sysgo.de */
232 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
237 /* Restart the processes in the job */
238 for (i = 0; i < job->numProgs; i++)
239 job->progs[i].isStopped = 0;
241 kill(-job->pgrp, SIGCONT);
243 job->stoppedProgs = 0;
248 /* built-in 'help' handler */
249 static int shell_help(struct job *cmd, struct jobSet *junk)
251 struct builtInCommand *x;
253 fprintf(stdout, "\nBuilt-in commands:\n");
254 fprintf(stdout, "-------------------\n");
255 for (x = bltins; x->cmd; x++) {
256 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
258 for (x = bltins_forking; x->cmd; x++) {
259 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
261 fprintf(stdout, "\n\n");
265 /* built-in 'jobs' handler */
266 static int shell_jobs(struct job *dummy, struct jobSet *jobList)
271 for (job = jobList->head; job; job = job->next) {
272 if (job->runningProgs == job->stoppedProgs)
273 statusString = "Stopped";
275 statusString = "Running";
277 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
283 /* built-in 'pwd' handler */
284 static int shell_pwd(struct job *dummy, struct jobSet *junk)
286 getcwd(cwd, sizeof(cwd));
287 fprintf(stdout, "%s\n", cwd);
291 /* built-in 'export VAR=value' handler */
292 static int shell_export(struct job *cmd, struct jobSet *junk)
296 if (!cmd->progs[0].argv[1] == 1) {
297 return (shell_env(cmd, junk));
299 res = putenv(cmd->progs[0].argv[1]);
301 fprintf(stdout, "export: %s\n", strerror(errno));
305 /* built-in 'read VAR' handler */
306 static int shell_read(struct job *cmd, struct jobSet *junk)
308 int res = 0, len, newlen;
310 char string[MAX_READ];
312 if (cmd->progs[0].argv[1]) {
313 /* argument (VAR) given: put "VAR=" into buffer */
314 strcpy(string, cmd->progs[0].argv[1]);
315 len = strlen(string);
318 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
319 newlen = strlen(string);
321 string[--newlen] = '\0'; /* chomp trailing newline */
323 ** string should now contain "VAR=<value>"
324 ** copy it (putenv() won't do that, so we must make sure
325 ** the string resides in a static buffer!)
328 if((s = strdup(string)))
331 fprintf(stdout, "read: %s\n", strerror(errno));
334 fgets(string, sizeof(string), stdin);
339 /* Built-in '.' handler (read-in and execute commands from file) */
340 static int shell_source(struct job *cmd, struct jobSet *junk)
345 if (!cmd->progs[0].argv[1] == 1)
348 input = fopen(cmd->progs[0].argv[1], "r");
350 fprintf(stdout, "Couldn't open file '%s'\n",
351 cmd->progs[0].argv[1]);
355 /* Now run the file */
356 status = busy_loop(input);
360 /* built-in 'unset VAR' handler */
361 static int shell_unset(struct job *cmd, struct jobSet *junk)
363 if (!cmd->progs[0].argv[1] == 1) {
364 fprintf(stdout, "unset: parameter required.\n");
367 unsetenv(cmd->progs[0].argv[1]);
371 /* free up all memory from a job */
372 static void freeJob(struct job *cmd)
376 for (i = 0; i < cmd->numProgs; i++) {
377 free(cmd->progs[i].argv);
378 if (cmd->progs[i].redirections)
379 free(cmd->progs[i].redirections);
380 if (cmd->progs[i].freeGlob)
381 globfree(&cmd->progs[i].globResult);
389 /* remove a job from the jobList */
390 static void removeJob(struct jobSet *jobList, struct job *job)
395 if (job == jobList->head) {
396 jobList->head = job->next;
398 prevJob = jobList->head;
399 while (prevJob->next != job)
400 prevJob = prevJob->next;
401 prevJob->next = job->next;
407 /* Checks to see if any background processes have exited -- if they
408 have, figure out why and see if a job has completed */
409 static void checkJobs(struct jobSet *jobList)
416 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
417 for (job = jobList->head; job; job = job->next) {
419 while (progNum < job->numProgs &&
420 job->progs[progNum].pid != childpid) progNum++;
421 if (progNum < job->numProgs)
425 if (WIFEXITED(status) || WIFSIGNALED(status)) {
428 job->progs[progNum].pid = 0;
430 if (!job->runningProgs) {
431 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
432 removeJob(jobList, job);
437 job->progs[progNum].isStopped = 1;
439 if (job->stoppedProgs == job->numProgs) {
440 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
446 if (childpid == -1 && errno != ECHILD)
450 static int getCommand(FILE * source, char *command)
452 if (source == NULL) {
453 if (local_pending_command) {
454 /* a command specified (-c option): return it & mark it done */
455 strcpy(command, local_pending_command);
456 free(local_pending_command);
457 local_pending_command = NULL;
463 if (source == stdin) {
464 #ifdef BB_FEATURE_SH_COMMAND_EDITING
467 len=fprintf(stdout, "%s %s", cwd, prompt);
469 promptStr=(char*)malloc(sizeof(char)*(len+1));
470 sprintf(promptStr, "%s %s", cwd, prompt);
471 cmdedit_read_input(promptStr, command);
475 fprintf(stdout, "%s %s", cwd, prompt);
480 if (!fgets(command, BUFSIZ - 2, source)) {
486 /* remove trailing newline */
487 command[strlen(command) - 1] = '\0';
492 static void globLastArgument(struct childProgram *prog, int *argcPtr,
496 int argcAlloced = *argcAllocedPtr;
500 char *src, *dst, *var;
502 if (argc > 1) { /* cmd->globResult is already initialized */
504 i = prog->globResult.gl_pathc;
510 /* do shell variable substitution */
511 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
512 prog->argv[argc - 1] = var;
514 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
515 if (rc == GLOB_NOSPACE) {
516 fprintf(stderr, "out of space during glob operation\n");
518 } else if (rc == GLOB_NOMATCH ||
519 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
520 !strcmp(prog->argv[argc - 1],
521 prog->globResult.gl_pathv[i]))) {
522 /* we need to remove whatever \ quoting is still present */
523 src = dst = prog->argv[argc - 1];
531 argcAlloced += (prog->globResult.gl_pathc - i);
533 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
534 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
535 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
536 argc += (prog->globResult.gl_pathc - i - 1);
539 *argcAllocedPtr = argcAlloced;
543 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
544 line). If a valid command is found, commandPtr is set to point to
545 the beginning of the next command (if the original command had more
546 then one job associated with it) or NULL if no more commands are
548 static int parseCommand(char **commandPtr, struct job *job, int *isBg)
551 char *returnCommand = NULL;
552 char *src, *buf, *chptr;
559 struct childProgram *prog;
561 /* skip leading white space */
562 while (**commandPtr && isspace(**commandPtr))
565 /* this handles empty lines or leading '#' characters */
566 if (!**commandPtr || (**commandPtr == '#')) {
574 job->progs = malloc(sizeof(*job->progs));
576 /* We set the argv elements to point inside of this string. The
577 memory is freed by freeJob(). Allocate twice the original
578 length in case we need to quote every single character.
580 Getting clean memory relieves us of the task of NULL
581 terminating things and makes the rest of this look a bit
582 cleaner (though it is, admittedly, a tad less efficient) */
583 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
587 prog->numRedirections = 0;
588 prog->redirections = NULL;
593 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
594 prog->argv[0] = job->cmdBuf;
598 while (*src && !done) {
605 fprintf(stderr, "character expected after \\\n");
610 /* in shell, "\'" should yield \' */
613 } else if (*src == '*' || *src == '?' || *src == '[' ||
614 *src == ']') *buf++ = '\\';
616 } else if (isspace(*src)) {
617 if (*prog->argv[argc]) {
619 /* +1 here leaves room for the NULL which ends argv */
620 if ((argc + 1) == argvAlloced) {
622 prog->argv = realloc(prog->argv,
623 sizeof(*prog->argv) *
626 globLastArgument(prog, &argc, &argvAlloced);
627 prog->argv[argc] = buf;
636 case '#': /* comment */
640 case '>': /* redirections */
642 i = prog->numRedirections++;
643 prog->redirections = realloc(prog->redirections,
644 sizeof(*prog->redirections) *
647 prog->redirections[i].fd = -1;
648 if (buf != prog->argv[argc]) {
649 /* the stuff before this character may be the file number
651 prog->redirections[i].fd =
652 strtol(prog->argv[argc], &chptr, 10);
654 if (*chptr && *prog->argv[argc]) {
656 globLastArgument(prog, &argc, &argvAlloced);
657 prog->argv[argc] = buf;
661 if (prog->redirections[i].fd == -1) {
663 prog->redirections[i].fd = 1;
665 prog->redirections[i].fd = 0;
670 prog->redirections[i].type =
671 REDIRECT_APPEND, src++;
673 prog->redirections[i].type = REDIRECT_OVERWRITE;
675 prog->redirections[i].type = REDIRECT_INPUT;
678 /* This isn't POSIX sh compliant. Oh well. */
680 while (isspace(*chptr))
684 fprintf(stderr, "file name expected after %c\n", *src);
689 prog->redirections[i].filename = buf;
690 while (*chptr && !isspace(*chptr))
693 src = chptr - 1; /* we src++ later */
694 prog->argv[argc] = ++buf;
698 /* finish this command */
699 if (*prog->argv[argc])
702 fprintf(stderr, "empty command in pipe\n");
706 prog->argv[argc] = NULL;
708 /* and start the next */
710 job->progs = realloc(job->progs,
711 sizeof(*job->progs) * job->numProgs);
712 prog = job->progs + (job->numProgs - 1);
713 prog->numRedirections = 0;
714 prog->redirections = NULL;
719 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
720 prog->argv[0] = ++buf;
723 while (*src && isspace(*src))
727 fprintf(stderr, "empty command in pipe\n");
730 src--; /* we'll ++ it at the end of the loop */
734 case '&': /* background */
736 case ';': /* multiple commands */
738 returnCommand = *commandPtr + (src - *commandPtr) + 1;
745 fprintf(stderr, "character expected after \\\n");
748 if (*src == '*' || *src == '[' || *src == ']'
749 || *src == '?') *buf++ = '\\';
758 if (*prog->argv[argc]) {
760 globLastArgument(prog, &argc, &argvAlloced);
766 prog->argv[argc] = NULL;
768 if (!returnCommand) {
769 job->text = malloc(strlen(*commandPtr) + 1);
770 strcpy(job->text, *commandPtr);
772 /* This leaves any trailing spaces, which is a bit sloppy */
773 count = returnCommand - *commandPtr;
774 job->text = malloc(count + 1);
775 strncpy(job->text, *commandPtr, count);
776 job->text[count] = '\0';
779 *commandPtr = returnCommand;
785 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
790 int pipefds[2]; /* pipefd[0] is for reading */
791 struct builtInCommand *x;
792 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
793 const struct BB_applet *a = applets;
797 nextin = 0, nextout = 1;
798 for (i = 0; i < newJob.numProgs; i++) {
799 if ((i + 1) < newJob.numProgs) {
801 nextout = pipefds[1];
806 /* Match any built-ins here */
807 for (x = bltins; x->cmd; x++) {
808 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
809 return (x->function(&newJob, jobList));
813 if (!(newJob.progs[i].pid = fork())) {
814 signal(SIGTTOU, SIG_DFL);
826 /* explicit redirections override pipes */
827 setupRedirections(newJob.progs + i);
829 /* Match any built-ins here */
830 for (x = bltins_forking; x->cmd; x++) {
831 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
832 exit (x->function(&newJob, jobList));
835 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
836 /* Handle busybox internals here */
837 while (a->name != 0) {
838 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
840 char** argv=newJob.progs[i].argv;
841 for(argc=0;*argv!=NULL; argv++, argc++);
842 exit((*(a->main)) (argc, newJob.progs[i].argv));
848 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
849 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
853 /* put our child in the process group whose leader is the
854 first process in this pipe */
855 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
862 /* If there isn't another process, nextin is garbage
863 but it doesn't matter */
867 newJob.pgrp = newJob.progs[0].pid;
869 /* find the ID for the job to use */
871 for (job = jobList->head; job; job = job->next)
872 if (job->jobId >= newJob.jobId)
873 newJob.jobId = job->jobId + 1;
875 /* add the job to the list of running jobs */
876 if (!jobList->head) {
877 job = jobList->head = malloc(sizeof(*job));
879 for (job = jobList->head; job->next; job = job->next);
880 job->next = malloc(sizeof(*job));
886 job->runningProgs = job->numProgs;
887 job->stoppedProgs = 0;
890 /* we don't wait for background jobs to return -- append it
891 to the list of backgrounded jobs and leave it alone */
892 printf("[%d] %d\n", job->jobId,
893 newJob.progs[newJob.numProgs - 1].pid);
897 /* move the new process group into the foreground */
898 /* suppress messages when run from /linuxrc mag@sysgo.de */
899 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
906 static int setupRedirections(struct childProgram *prog)
911 struct redirectionSpecifier *redir = prog->redirections;
913 for (i = 0; i < prog->numRedirections; i++, redir++) {
914 switch (redir->type) {
918 case REDIRECT_OVERWRITE:
919 mode = O_RDWR | O_CREAT | O_TRUNC;
921 case REDIRECT_APPEND:
922 mode = O_RDWR | O_CREAT | O_APPEND;
926 openfd = open(redir->filename, mode, 0666);
928 /* this could get lost if stderr has been redirected, but
929 bash and ash both lose it as well (though zsh doesn't!) */
930 fprintf(stderr, "error opening %s: %s\n", redir->filename,
935 if (openfd != redir->fd) {
936 dup2(openfd, redir->fd);
945 static int busy_loop(FILE * input)
948 char *nextCommand = NULL;
949 struct jobSet jobList = { NULL, NULL };
956 /* save current owner of TTY so we can restore it on exit */
957 parent_pgrp = tcgetpgrp(0);
959 command = (char *) calloc(BUFSIZ, sizeof(char));
961 /* don't pay any attention to this signal; it just confuses
962 things and isn't really meant for shells anyway */
963 signal(SIGTTOU, SIG_IGN);
967 /* no job is in the foreground */
969 /* see if any background processes have exited */
973 if (getCommand(input, command))
975 nextCommand = command;
978 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
980 runCommand(newJob, &jobList, inBg);
983 /* a job is running in the foreground; wait for it */
985 while (!jobList.fg->progs[i].pid ||
986 jobList.fg->progs[i].isStopped) i++;
988 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
990 if (WIFEXITED(status) || WIFSIGNALED(status)) {
991 /* the child exited */
992 jobList.fg->runningProgs--;
993 jobList.fg->progs[i].pid = 0;
995 if (!jobList.fg->runningProgs) {
998 removeJob(&jobList, jobList.fg);
1002 /* the child was stopped */
1003 jobList.fg->stoppedProgs++;
1004 jobList.fg->progs[i].isStopped = 1;
1006 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1007 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1008 "Stopped", jobList.fg->text);
1014 /* move the shell to the foreground */
1015 /* suppress messages when run from /linuxrc mag@sysgo.de */
1016 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1017 perror("tcsetpgrp");
1023 /* return controlling TTY back to parent process group before exiting */
1024 if (tcsetpgrp(0, parent_pgrp))
1025 perror("tcsetpgrp");
1027 /* return controlling TTY back to parent process group before exiting */
1028 if (tcsetpgrp(0, parent_pgrp))
1029 perror("tcsetpgrp");
1031 /* return exit status if called with "-c" */
1032 if (input == NULL && WIFEXITED(status))
1033 return WEXITSTATUS(status);
1039 int shell_main(int argc, char **argv)
1041 FILE *input = stdin;
1043 /* initialize the cwd */
1044 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1046 fatalError("sh: out of memory\n");
1048 getcwd(cwd, sizeof(char)*BUFSIZ);
1050 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1052 signal(SIGWINCH, win_changed);
1056 //if (argv[0] && argv[0][0] == '-') {
1057 // shell_source("/etc/profile");
1061 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1062 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1064 if (argv[1][0]=='-' && argv[1][1]=='c') {
1066 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1067 if (local_pending_command == 0) {
1068 fatalError("sh: out of memory\n");
1070 for(i=2; i<argc; i++)
1072 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1073 local_pending_command = realloc(local_pending_command,
1074 strlen(local_pending_command) + strlen(argv[i]));
1075 if (local_pending_command==NULL)
1076 fatalError("sh: commands for -c option too long\n");
1078 strcat(local_pending_command, argv[i]);
1079 if ( (i + 1) < argc)
1080 strcat(local_pending_command, " ");
1085 else if (argv[1][0]=='-') {
1089 input = fopen(argv[1], "r");
1091 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
1097 return (busy_loop(input));