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, 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 */
116 static struct builtInCommand bltins[] = {
117 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
118 {"cd", "Change working directory", "cd [dir]", builtin_cd},
119 {"exit", "Exit from shell()", "exit", builtin_exit},
120 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
121 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
122 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
123 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
124 {"read", "Input environment variable", "read [VAR]", builtin_read},
125 {NULL, NULL, NULL, NULL}
128 /* Table of built-in functions */
129 static struct builtInCommand bltins_forking[] = {
130 {"env", "Print all environment variables", "env", builtin_env},
131 {"pwd", "Print current directory", "pwd", builtin_pwd},
132 {".", "Source-in and run commands in a file", ". filename", builtin_source},
133 {"help", "List shell built-in commands", "help", builtin_help},
134 {NULL, NULL, NULL, NULL}
137 static const char shell_usage[] =
139 " or: sh -c command [args]...\n"
140 #ifndef BB_FEATURE_TRIVIAL_HELP
141 "\nlash: The BusyBox command interpreter (shell).\n\n"
145 static char *prompt = "# ";
146 static char *cwd = NULL;
147 static char *local_pending_command = NULL;
149 #ifdef BB_FEATURE_SH_COMMAND_EDITING
150 void win_changed(int sig)
152 struct winsize win = { 0, 0 };
153 ioctl(0, TIOCGWINSZ, &win);
154 if (win.ws_col > 0) {
155 cmdedit_setwidth( win.ws_col - 1);
161 /* built-in 'cd <path>' handler */
162 static int builtin_cd(struct job *cmd, struct jobSet *junk)
166 if (!cmd->progs[0].argv[1] == 1)
167 newdir = getenv("HOME");
169 newdir = cmd->progs[0].argv[1];
171 printf("cd: %s: %s\n", newdir, strerror(errno));
174 getcwd(cwd, sizeof(cwd));
179 /* built-in 'env' handler */
180 static int builtin_env(struct job *dummy, struct jobSet *junk)
184 for (e = environ; *e; e++) {
185 fprintf(stdout, "%s\n", *e);
190 /* built-in 'exit' handler */
191 static int builtin_exit(struct job *cmd, struct jobSet *junk)
193 if (!cmd->progs[0].argv[1] == 1)
196 return(atoi(cmd->progs[0].argv[1]));
199 /* built-in 'fg' and 'bg' handler */
200 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
203 struct job *job=NULL;
205 if (!jobList->head) {
206 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
207 fprintf(stderr, "%s: exactly one argument is expected\n",
208 cmd->progs[0].argv[0]);
211 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
212 fprintf(stderr, "%s: bad argument '%s'\n",
213 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
215 for (job = jobList->head; job; job = job->next) {
216 if (job->jobId == jobNum) {
226 fprintf(stderr, "%s: unknown job %d\n",
227 cmd->progs[0].argv[0], jobNum);
231 if (*cmd->progs[0].argv[0] == 'f') {
232 /* Make this job the foreground job */
233 /* suppress messages when run from /linuxrc mag@sysgo.de */
234 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
239 /* Restart the processes in the job */
240 for (i = 0; i < job->numProgs; i++)
241 job->progs[i].isStopped = 0;
243 kill(-job->pgrp, SIGCONT);
245 job->stoppedProgs = 0;
250 /* built-in 'help' handler */
251 static int builtin_help(struct job *cmd, struct jobSet *junk)
253 struct builtInCommand *x;
255 fprintf(stdout, "\nBuilt-in commands:\n");
256 fprintf(stdout, "-------------------\n");
257 for (x = bltins; x->cmd; x++) {
258 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
260 for (x = bltins_forking; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
263 fprintf(stdout, "\n\n");
267 /* built-in 'jobs' handler */
268 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
273 for (job = jobList->head; job; job = job->next) {
274 if (job->runningProgs == job->stoppedProgs)
275 statusString = "Stopped";
277 statusString = "Running";
279 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
285 /* built-in 'pwd' handler */
286 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
288 getcwd(cwd, sizeof(cwd));
289 fprintf(stdout, "%s\n", cwd);
293 /* built-in 'export VAR=value' handler */
294 static int builtin_export(struct job *cmd, struct jobSet *junk)
298 if (!cmd->progs[0].argv[1] == 1) {
299 return (builtin_env(cmd, junk));
301 res = putenv(cmd->progs[0].argv[1]);
303 fprintf(stdout, "export: %s\n", strerror(errno));
307 /* built-in 'read VAR' handler */
308 static int builtin_read(struct job *cmd, struct jobSet *junk)
310 int res = 0, len, newlen;
312 char string[MAX_READ];
314 if (cmd->progs[0].argv[1]) {
315 /* argument (VAR) given: put "VAR=" into buffer */
316 strcpy(string, cmd->progs[0].argv[1]);
317 len = strlen(string);
320 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
321 newlen = strlen(string);
323 string[--newlen] = '\0'; /* chomp trailing newline */
325 ** string should now contain "VAR=<value>"
326 ** copy it (putenv() won't do that, so we must make sure
327 ** the string resides in a static buffer!)
330 if((s = strdup(string)))
333 fprintf(stdout, "read: %s\n", strerror(errno));
336 fgets(string, sizeof(string), stdin);
341 /* Built-in '.' handler (read-in and execute commands from file) */
342 static int builtin_source(struct job *cmd, struct jobSet *junk)
347 if (!cmd->progs[0].argv[1] == 1)
350 input = fopen(cmd->progs[0].argv[1], "r");
352 fprintf(stdout, "Couldn't open file '%s'\n",
353 cmd->progs[0].argv[1]);
357 /* Now run the file */
358 status = busy_loop(input);
362 /* built-in 'unset VAR' handler */
363 static int builtin_unset(struct job *cmd, struct jobSet *junk)
365 if (!cmd->progs[0].argv[1] == 1) {
366 fprintf(stdout, "unset: parameter required.\n");
369 unsetenv(cmd->progs[0].argv[1]);
373 /* free up all memory from a job */
374 static void freeJob(struct job *cmd)
378 for (i = 0; i < cmd->numProgs; i++) {
379 free(cmd->progs[i].argv);
380 if (cmd->progs[i].redirections)
381 free(cmd->progs[i].redirections);
382 if (cmd->progs[i].freeGlob)
383 globfree(&cmd->progs[i].globResult);
391 /* remove a job from the jobList */
392 static void removeJob(struct jobSet *jobList, struct job *job)
397 if (job == jobList->head) {
398 jobList->head = job->next;
400 prevJob = jobList->head;
401 while (prevJob->next != job)
402 prevJob = prevJob->next;
403 prevJob->next = job->next;
409 /* Checks to see if any background processes have exited -- if they
410 have, figure out why and see if a job has completed */
411 static void checkJobs(struct jobSet *jobList)
418 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
419 for (job = jobList->head; job; job = job->next) {
421 while (progNum < job->numProgs &&
422 job->progs[progNum].pid != childpid) progNum++;
423 if (progNum < job->numProgs)
427 if (WIFEXITED(status) || WIFSIGNALED(status)) {
430 job->progs[progNum].pid = 0;
432 if (!job->runningProgs) {
433 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
434 removeJob(jobList, job);
439 job->progs[progNum].isStopped = 1;
441 if (job->stoppedProgs == job->numProgs) {
442 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
448 if (childpid == -1 && errno != ECHILD)
452 static int getCommand(FILE * source, char *command)
454 if (source == NULL) {
455 if (local_pending_command) {
456 /* a command specified (-c option): return it & mark it done */
457 strcpy(command, local_pending_command);
458 free(local_pending_command);
459 local_pending_command = NULL;
465 if (source == stdin) {
466 #ifdef BB_FEATURE_SH_COMMAND_EDITING
469 len=fprintf(stdout, "%s %s", cwd, prompt);
471 promptStr=(char*)malloc(sizeof(char)*(len+1));
472 sprintf(promptStr, "%s %s", cwd, prompt);
473 cmdedit_read_input(promptStr, command);
477 fprintf(stdout, "%s %s", cwd, prompt);
482 if (!fgets(command, BUFSIZ - 2, source)) {
488 /* remove trailing newline */
489 command[strlen(command) - 1] = '\0';
494 static void globLastArgument(struct childProgram *prog, int *argcPtr,
498 int argcAlloced = *argcAllocedPtr;
502 char *src, *dst, *var;
504 if (argc > 1) { /* cmd->globResult is already initialized */
506 i = prog->globResult.gl_pathc;
512 /* do shell variable substitution */
513 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
514 prog->argv[argc - 1] = var;
516 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
517 if (rc == GLOB_NOSPACE) {
518 fprintf(stderr, "out of space during glob operation\n");
520 } else if (rc == GLOB_NOMATCH ||
521 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
522 !strcmp(prog->argv[argc - 1],
523 prog->globResult.gl_pathv[i]))) {
524 /* we need to remove whatever \ quoting is still present */
525 src = dst = prog->argv[argc - 1];
533 argcAlloced += (prog->globResult.gl_pathc - i);
535 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
536 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
537 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
538 argc += (prog->globResult.gl_pathc - i - 1);
541 *argcAllocedPtr = argcAlloced;
545 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
546 line). If a valid command is found, commandPtr is set to point to
547 the beginning of the next command (if the original command had more
548 then one job associated with it) or NULL if no more commands are
550 static int parseCommand(char **commandPtr, struct job *job, int *isBg)
553 char *returnCommand = NULL;
554 char *src, *buf, *chptr;
561 struct childProgram *prog;
563 /* skip leading white space */
564 while (**commandPtr && isspace(**commandPtr))
567 /* this handles empty lines or leading '#' characters */
568 if (!**commandPtr || (**commandPtr == '#')) {
576 job->progs = malloc(sizeof(*job->progs));
578 /* We set the argv elements to point inside of this string. The
579 memory is freed by freeJob(). Allocate twice the original
580 length in case we need to quote every single character.
582 Getting clean memory relieves us of the task of NULL
583 terminating things and makes the rest of this look a bit
584 cleaner (though it is, admittedly, a tad less efficient) */
585 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
589 prog->numRedirections = 0;
590 prog->redirections = NULL;
595 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
596 prog->argv[0] = job->cmdBuf;
600 while (*src && !done) {
607 fprintf(stderr, "character expected after \\\n");
612 /* in shell, "\'" should yield \' */
615 } else if (*src == '*' || *src == '?' || *src == '[' ||
616 *src == ']') *buf++ = '\\';
618 } else if (isspace(*src)) {
619 if (*prog->argv[argc]) {
621 /* +1 here leaves room for the NULL which ends argv */
622 if ((argc + 1) == argvAlloced) {
624 prog->argv = realloc(prog->argv,
625 sizeof(*prog->argv) *
628 globLastArgument(prog, &argc, &argvAlloced);
629 prog->argv[argc] = buf;
638 case '#': /* comment */
642 case '>': /* redirections */
644 i = prog->numRedirections++;
645 prog->redirections = realloc(prog->redirections,
646 sizeof(*prog->redirections) *
649 prog->redirections[i].fd = -1;
650 if (buf != prog->argv[argc]) {
651 /* the stuff before this character may be the file number
653 prog->redirections[i].fd =
654 strtol(prog->argv[argc], &chptr, 10);
656 if (*chptr && *prog->argv[argc]) {
658 globLastArgument(prog, &argc, &argvAlloced);
659 prog->argv[argc] = buf;
663 if (prog->redirections[i].fd == -1) {
665 prog->redirections[i].fd = 1;
667 prog->redirections[i].fd = 0;
672 prog->redirections[i].type =
673 REDIRECT_APPEND, src++;
675 prog->redirections[i].type = REDIRECT_OVERWRITE;
677 prog->redirections[i].type = REDIRECT_INPUT;
680 /* This isn't POSIX sh compliant. Oh well. */
682 while (isspace(*chptr))
686 fprintf(stderr, "file name expected after %c\n", *src);
691 prog->redirections[i].filename = buf;
692 while (*chptr && !isspace(*chptr))
695 src = chptr - 1; /* we src++ later */
696 prog->argv[argc] = ++buf;
700 /* finish this command */
701 if (*prog->argv[argc])
704 fprintf(stderr, "empty command in pipe\n");
708 prog->argv[argc] = NULL;
710 /* and start the next */
712 job->progs = realloc(job->progs,
713 sizeof(*job->progs) * job->numProgs);
714 prog = job->progs + (job->numProgs - 1);
715 prog->numRedirections = 0;
716 prog->redirections = NULL;
721 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
722 prog->argv[0] = ++buf;
725 while (*src && isspace(*src))
729 fprintf(stderr, "empty command in pipe\n");
732 src--; /* we'll ++ it at the end of the loop */
736 case '&': /* background */
738 case ';': /* multiple commands */
740 returnCommand = *commandPtr + (src - *commandPtr) + 1;
747 fprintf(stderr, "character expected after \\\n");
750 if (*src == '*' || *src == '[' || *src == ']'
751 || *src == '?') *buf++ = '\\';
760 if (*prog->argv[argc]) {
762 globLastArgument(prog, &argc, &argvAlloced);
768 prog->argv[argc] = NULL;
770 if (!returnCommand) {
771 job->text = malloc(strlen(*commandPtr) + 1);
772 strcpy(job->text, *commandPtr);
774 /* This leaves any trailing spaces, which is a bit sloppy */
775 count = returnCommand - *commandPtr;
776 job->text = malloc(count + 1);
777 strncpy(job->text, *commandPtr, count);
778 job->text[count] = '\0';
781 *commandPtr = returnCommand;
787 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
792 int pipefds[2]; /* pipefd[0] is for reading */
793 struct builtInCommand *x;
794 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
795 const struct BB_applet *a = applets;
799 nextin = 0, nextout = 1;
800 for (i = 0; i < newJob.numProgs; i++) {
801 if ((i + 1) < newJob.numProgs) {
803 nextout = pipefds[1];
808 /* Check if the command matches any non-forking builtins */
809 for (x = bltins; x->cmd; x++) {
810 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
811 return (x->function(&newJob, jobList));
815 if (!(newJob.progs[i].pid = fork())) {
816 signal(SIGTTOU, SIG_DFL);
828 /* explicit redirections override pipes */
829 setupRedirections(newJob.progs + i);
831 /* Check if the command matches any of the other builtins */
832 for (x = bltins_forking; x->cmd; x++) {
833 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
834 exit (x->function(&newJob, jobList));
837 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
838 /* Check if the command matches any busybox internal commands here */
839 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
840 * works, but '/bin/cat' doesn't ) */
841 while (a->name != 0) {
842 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
844 char** argv=newJob.progs[i].argv;
845 for(argc=0;*argv!=NULL; argv++, argc++);
846 exit((*(a->main)) (argc, newJob.progs[i].argv));
852 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
853 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
857 /* put our child in the process group whose leader is the
858 first process in this pipe */
859 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
866 /* If there isn't another process, nextin is garbage
867 but it doesn't matter */
871 newJob.pgrp = newJob.progs[0].pid;
873 /* find the ID for the job to use */
875 for (job = jobList->head; job; job = job->next)
876 if (job->jobId >= newJob.jobId)
877 newJob.jobId = job->jobId + 1;
879 /* add the job to the list of running jobs */
880 if (!jobList->head) {
881 job = jobList->head = malloc(sizeof(*job));
883 for (job = jobList->head; job->next; job = job->next);
884 job->next = malloc(sizeof(*job));
890 job->runningProgs = job->numProgs;
891 job->stoppedProgs = 0;
894 /* we don't wait for background jobs to return -- append it
895 to the list of backgrounded jobs and leave it alone */
896 printf("[%d] %d\n", job->jobId,
897 newJob.progs[newJob.numProgs - 1].pid);
901 /* move the new process group into the foreground */
902 /* suppress messages when run from /linuxrc mag@sysgo.de */
903 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
910 static int setupRedirections(struct childProgram *prog)
915 struct redirectionSpecifier *redir = prog->redirections;
917 for (i = 0; i < prog->numRedirections; i++, redir++) {
918 switch (redir->type) {
922 case REDIRECT_OVERWRITE:
923 mode = O_RDWR | O_CREAT | O_TRUNC;
925 case REDIRECT_APPEND:
926 mode = O_RDWR | O_CREAT | O_APPEND;
930 openfd = open(redir->filename, mode, 0666);
932 /* this could get lost if stderr has been redirected, but
933 bash and ash both lose it as well (though zsh doesn't!) */
934 fprintf(stderr, "error opening %s: %s\n", redir->filename,
939 if (openfd != redir->fd) {
940 dup2(openfd, redir->fd);
949 static int busy_loop(FILE * input)
952 char *nextCommand = NULL;
953 struct jobSet jobList = { NULL, NULL };
960 /* save current owner of TTY so we can restore it on exit */
961 parent_pgrp = tcgetpgrp(0);
963 command = (char *) calloc(BUFSIZ, sizeof(char));
965 /* don't pay any attention to this signal; it just confuses
966 things and isn't really meant for shells anyway */
967 signal(SIGTTOU, SIG_IGN);
971 /* no job is in the foreground */
973 /* see if any background processes have exited */
977 if (getCommand(input, command))
979 nextCommand = command;
982 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
984 runCommand(newJob, &jobList, inBg);
987 /* a job is running in the foreground; wait for it */
989 while (!jobList.fg->progs[i].pid ||
990 jobList.fg->progs[i].isStopped) i++;
992 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
994 if (WIFEXITED(status) || WIFSIGNALED(status)) {
995 /* the child exited */
996 jobList.fg->runningProgs--;
997 jobList.fg->progs[i].pid = 0;
999 if (!jobList.fg->runningProgs) {
1002 removeJob(&jobList, jobList.fg);
1006 /* the child was stopped */
1007 jobList.fg->stoppedProgs++;
1008 jobList.fg->progs[i].isStopped = 1;
1010 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1011 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1012 "Stopped", jobList.fg->text);
1018 /* move the shell to the foreground */
1019 /* suppress messages when run from /linuxrc mag@sysgo.de */
1020 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1021 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 // builtin_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));