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 (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 const char shell_usage[] =
142 " or: sh -c command [args]...\n"
143 #ifndef BB_FEATURE_TRIVIAL_HELP
144 "\nlash: The BusyBox command interpreter (shell).\n\n"
148 static char *prompt = "# ";
149 static char *cwd = NULL;
150 static char *local_pending_command = NULL;
152 #ifdef BB_FEATURE_SH_COMMAND_EDITING
153 void win_changed(int sig)
155 struct winsize win = { 0, 0 };
156 ioctl(0, TIOCGWINSZ, &win);
157 if (win.ws_col > 0) {
158 cmdedit_setwidth( win.ws_col - 1);
164 /* built-in 'cd <path>' handler */
165 static int builtin_cd(struct job *cmd, struct jobSet *junk)
169 if (!cmd->progs[0].argv[1] == 1)
170 newdir = getenv("HOME");
172 newdir = cmd->progs[0].argv[1];
174 printf("cd: %s: %s\n", newdir, strerror(errno));
177 getcwd(cwd, sizeof(cwd));
182 /* built-in 'env' handler */
183 static int builtin_env(struct job *dummy, struct jobSet *junk)
187 for (e = environ; *e; e++) {
188 fprintf(stdout, "%s\n", *e);
193 /* built-in 'exit' handler */
194 static int builtin_exit(struct job *cmd, struct jobSet *junk)
196 if (!cmd->progs[0].argv[1] == 1)
199 return(atoi(cmd->progs[0].argv[1]));
202 /* built-in 'fg' and 'bg' handler */
203 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
206 struct job *job=NULL;
208 if (!jobList->head) {
209 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
210 fprintf(stderr, "%s: exactly one argument is expected\n",
211 cmd->progs[0].argv[0]);
214 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
215 fprintf(stderr, "%s: bad argument '%s'\n",
216 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
218 for (job = jobList->head; job; job = job->next) {
219 if (job->jobId == jobNum) {
229 fprintf(stderr, "%s: unknown job %d\n",
230 cmd->progs[0].argv[0], jobNum);
234 if (*cmd->progs[0].argv[0] == 'f') {
235 /* Make this job the foreground job */
236 /* suppress messages when run from /linuxrc mag@sysgo.de */
237 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
242 /* Restart the processes in the job */
243 for (i = 0; i < job->numProgs; i++)
244 job->progs[i].isStopped = 0;
246 kill(-job->pgrp, SIGCONT);
248 job->stoppedProgs = 0;
253 /* built-in 'help' handler */
254 static int builtin_help(struct job *cmd, struct jobSet *junk)
256 struct builtInCommand *x;
258 fprintf(stdout, "\nBuilt-in commands:\n");
259 fprintf(stdout, "-------------------\n");
260 for (x = bltins; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
263 for (x = bltins_forking; x->cmd; x++) {
264 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
266 fprintf(stdout, "\n\n");
270 /* built-in 'jobs' handler */
271 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
276 for (job = jobList->head; job; job = job->next) {
277 if (job->runningProgs == job->stoppedProgs)
278 statusString = "Stopped";
280 statusString = "Running";
282 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
288 /* built-in 'pwd' handler */
289 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
291 getcwd(cwd, sizeof(cwd));
292 fprintf(stdout, "%s\n", cwd);
296 /* built-in 'export VAR=value' handler */
297 static int builtin_export(struct job *cmd, struct jobSet *junk)
301 if (!cmd->progs[0].argv[1] == 1) {
302 return (builtin_env(cmd, junk));
304 res = putenv(cmd->progs[0].argv[1]);
306 fprintf(stdout, "export: %s\n", strerror(errno));
310 /* built-in 'read VAR' handler */
311 static int builtin_read(struct job *cmd, struct jobSet *junk)
313 int res = 0, len, newlen;
315 char string[MAX_READ];
317 if (cmd->progs[0].argv[1]) {
318 /* argument (VAR) given: put "VAR=" into buffer */
319 strcpy(string, cmd->progs[0].argv[1]);
320 len = strlen(string);
323 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
324 newlen = strlen(string);
326 string[--newlen] = '\0'; /* chomp trailing newline */
328 ** string should now contain "VAR=<value>"
329 ** copy it (putenv() won't do that, so we must make sure
330 ** the string resides in a static buffer!)
333 if((s = strdup(string)))
336 fprintf(stdout, "read: %s\n", strerror(errno));
339 fgets(string, sizeof(string), stdin);
344 /* Built-in '.' handler (read-in and execute commands from file) */
345 static int builtin_source(struct job *cmd, struct jobSet *junk)
350 if (!cmd->progs[0].argv[1] == 1)
353 input = fopen(cmd->progs[0].argv[1], "r");
355 fprintf(stdout, "Couldn't open file '%s'\n",
356 cmd->progs[0].argv[1]);
360 /* Now run the file */
361 status = busy_loop(input);
365 /* built-in 'unset VAR' handler */
366 static int builtin_unset(struct job *cmd, struct jobSet *junk)
368 if (!cmd->progs[0].argv[1] == 1) {
369 fprintf(stdout, "unset: parameter required.\n");
372 unsetenv(cmd->progs[0].argv[1]);
376 /* free up all memory from a job */
377 static void freeJob(struct job *cmd)
381 for (i = 0; i < cmd->numProgs; i++) {
382 free(cmd->progs[i].argv);
383 if (cmd->progs[i].redirections)
384 free(cmd->progs[i].redirections);
385 if (cmd->progs[i].freeGlob)
386 globfree(&cmd->progs[i].globResult);
394 /* remove a job from the jobList */
395 static void removeJob(struct jobSet *jobList, struct job *job)
400 if (job == jobList->head) {
401 jobList->head = job->next;
403 prevJob = jobList->head;
404 while (prevJob->next != job)
405 prevJob = prevJob->next;
406 prevJob->next = job->next;
412 /* Checks to see if any background processes have exited -- if they
413 have, figure out why and see if a job has completed */
414 static void checkJobs(struct jobSet *jobList)
421 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
422 for (job = jobList->head; job; job = job->next) {
424 while (progNum < job->numProgs &&
425 job->progs[progNum].pid != childpid) progNum++;
426 if (progNum < job->numProgs)
430 if (WIFEXITED(status) || WIFSIGNALED(status)) {
433 job->progs[progNum].pid = 0;
435 if (!job->runningProgs) {
436 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
437 removeJob(jobList, job);
442 job->progs[progNum].isStopped = 1;
444 if (job->stoppedProgs == job->numProgs) {
445 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
451 if (childpid == -1 && errno != ECHILD)
455 static int getCommand(FILE * source, char *command)
457 if (source == NULL) {
458 if (local_pending_command) {
459 /* a command specified (-c option): return it & mark it done */
460 strcpy(command, local_pending_command);
461 free(local_pending_command);
462 local_pending_command = NULL;
468 if (source == stdin) {
469 #ifdef BB_FEATURE_SH_COMMAND_EDITING
472 len=fprintf(stdout, "%s %s", cwd, prompt);
474 promptStr=(char*)malloc(sizeof(char)*(len+1));
475 sprintf(promptStr, "%s %s", cwd, prompt);
476 cmdedit_read_input(promptStr, command);
480 fprintf(stdout, "%s %s", cwd, prompt);
485 if (!fgets(command, BUFSIZ - 2, source)) {
491 /* remove trailing newline */
492 command[strlen(command) - 1] = '\0';
497 static void globLastArgument(struct childProgram *prog, int *argcPtr,
501 int argcAlloced = *argcAllocedPtr;
505 char *src, *dst, *var;
507 if (argc > 1) { /* cmd->globResult is already initialized */
509 i = prog->globResult.gl_pathc;
515 /* do shell variable substitution */
516 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
517 prog->argv[argc - 1] = var;
519 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
520 if (rc == GLOB_NOSPACE) {
521 fprintf(stderr, "out of space during glob operation\n");
523 } else if (rc == GLOB_NOMATCH ||
524 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
525 !strcmp(prog->argv[argc - 1],
526 prog->globResult.gl_pathv[i]))) {
527 /* we need to remove whatever \ quoting is still present */
528 src = dst = prog->argv[argc - 1];
536 argcAlloced += (prog->globResult.gl_pathc - i);
538 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
539 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
540 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
541 argc += (prog->globResult.gl_pathc - i - 1);
544 *argcAllocedPtr = argcAlloced;
548 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
549 line). If a valid command is found, commandPtr is set to point to
550 the beginning of the next command (if the original command had more
551 then one job associated with it) or NULL if no more commands are
553 static int parseCommand(char **commandPtr, struct job *job, int *isBg)
556 char *returnCommand = NULL;
557 char *src, *buf, *chptr;
564 struct childProgram *prog;
566 /* skip leading white space */
567 while (**commandPtr && isspace(**commandPtr))
570 /* this handles empty lines or leading '#' characters */
571 if (!**commandPtr || (**commandPtr == '#')) {
579 job->progs = malloc(sizeof(*job->progs));
581 /* We set the argv elements to point inside of this string. The
582 memory is freed by freeJob(). Allocate twice the original
583 length in case we need to quote every single character.
585 Getting clean memory relieves us of the task of NULL
586 terminating things and makes the rest of this look a bit
587 cleaner (though it is, admittedly, a tad less efficient) */
588 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
592 prog->numRedirections = 0;
593 prog->redirections = NULL;
598 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
599 prog->argv[0] = job->cmdBuf;
603 while (*src && !done) {
610 fprintf(stderr, "character expected after \\\n");
615 /* in shell, "\'" should yield \' */
618 } else if (*src == '*' || *src == '?' || *src == '[' ||
619 *src == ']') *buf++ = '\\';
621 } else if (isspace(*src)) {
622 if (*prog->argv[argc]) {
624 /* +1 here leaves room for the NULL which ends argv */
625 if ((argc + 1) == argvAlloced) {
627 prog->argv = realloc(prog->argv,
628 sizeof(*prog->argv) *
631 globLastArgument(prog, &argc, &argvAlloced);
632 prog->argv[argc] = buf;
641 case '#': /* comment */
645 case '>': /* redirections */
647 i = prog->numRedirections++;
648 prog->redirections = realloc(prog->redirections,
649 sizeof(*prog->redirections) *
652 prog->redirections[i].fd = -1;
653 if (buf != prog->argv[argc]) {
654 /* the stuff before this character may be the file number
656 prog->redirections[i].fd =
657 strtol(prog->argv[argc], &chptr, 10);
659 if (*chptr && *prog->argv[argc]) {
661 globLastArgument(prog, &argc, &argvAlloced);
662 prog->argv[argc] = buf;
666 if (prog->redirections[i].fd == -1) {
668 prog->redirections[i].fd = 1;
670 prog->redirections[i].fd = 0;
675 prog->redirections[i].type =
676 REDIRECT_APPEND, src++;
678 prog->redirections[i].type = REDIRECT_OVERWRITE;
680 prog->redirections[i].type = REDIRECT_INPUT;
683 /* This isn't POSIX sh compliant. Oh well. */
685 while (isspace(*chptr))
689 fprintf(stderr, "file name expected after %c\n", *src);
694 prog->redirections[i].filename = buf;
695 while (*chptr && !isspace(*chptr))
698 src = chptr - 1; /* we src++ later */
699 prog->argv[argc] = ++buf;
703 /* finish this command */
704 if (*prog->argv[argc])
707 fprintf(stderr, "empty command in pipe\n");
711 prog->argv[argc] = NULL;
713 /* and start the next */
715 job->progs = realloc(job->progs,
716 sizeof(*job->progs) * job->numProgs);
717 prog = job->progs + (job->numProgs - 1);
718 prog->numRedirections = 0;
719 prog->redirections = NULL;
724 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
725 prog->argv[0] = ++buf;
728 while (*src && isspace(*src))
732 fprintf(stderr, "empty command in pipe\n");
735 src--; /* we'll ++ it at the end of the loop */
739 case '&': /* background */
741 case ';': /* multiple commands */
743 returnCommand = *commandPtr + (src - *commandPtr) + 1;
750 fprintf(stderr, "character expected after \\\n");
753 if (*src == '*' || *src == '[' || *src == ']'
754 || *src == '?') *buf++ = '\\';
763 if (*prog->argv[argc]) {
765 globLastArgument(prog, &argc, &argvAlloced);
771 prog->argv[argc] = NULL;
773 if (!returnCommand) {
774 job->text = malloc(strlen(*commandPtr) + 1);
775 strcpy(job->text, *commandPtr);
777 /* This leaves any trailing spaces, which is a bit sloppy */
778 count = returnCommand - *commandPtr;
779 job->text = malloc(count + 1);
780 strncpy(job->text, *commandPtr, count);
781 job->text[count] = '\0';
784 *commandPtr = returnCommand;
790 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
795 int pipefds[2]; /* pipefd[0] is for reading */
796 struct builtInCommand *x;
797 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
798 const struct BB_applet *a = applets;
802 nextin = 0, nextout = 1;
803 for (i = 0; i < newJob.numProgs; i++) {
804 if ((i + 1) < newJob.numProgs) {
806 nextout = pipefds[1];
811 /* Check if the command matches any non-forking builtins */
812 for (x = bltins; x->cmd; x++) {
813 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
814 return (x->function(&newJob, jobList));
818 if (!(newJob.progs[i].pid = fork())) {
819 signal(SIGTTOU, SIG_DFL);
831 /* explicit redirections override pipes */
832 setupRedirections(newJob.progs + i);
834 /* Check if the command matches any of the other builtins */
835 for (x = bltins_forking; x->cmd; x++) {
836 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
837 exit (x->function(&newJob, jobList));
840 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
841 /* Check if the command matches any busybox internal commands here */
842 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
843 * works, but '/bin/cat' doesn't ) */
844 while (a->name != 0) {
845 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
847 char** argv=newJob.progs[i].argv;
848 for(argc=0;*argv!=NULL; argv++, argc++);
849 exit((*(a->main)) (argc, newJob.progs[i].argv));
855 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
856 fatalError("%s: %s\n", newJob.progs[i].argv[0],
860 /* put our child in the process group whose leader is the
861 first process in this pipe */
862 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
869 /* If there isn't another process, nextin is garbage
870 but it doesn't matter */
874 newJob.pgrp = newJob.progs[0].pid;
876 /* find the ID for the job to use */
878 for (job = jobList->head; job; job = job->next)
879 if (job->jobId >= newJob.jobId)
880 newJob.jobId = job->jobId + 1;
882 /* add the job to the list of running jobs */
883 if (!jobList->head) {
884 job = jobList->head = malloc(sizeof(*job));
886 for (job = jobList->head; job->next; job = job->next);
887 job->next = malloc(sizeof(*job));
893 job->runningProgs = job->numProgs;
894 job->stoppedProgs = 0;
897 /* we don't wait for background jobs to return -- append it
898 to the list of backgrounded jobs and leave it alone */
899 printf("[%d] %d\n", job->jobId,
900 newJob.progs[newJob.numProgs - 1].pid);
904 /* move the new process group into the foreground */
905 /* suppress messages when run from /linuxrc mag@sysgo.de */
906 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
913 static int setupRedirections(struct childProgram *prog)
918 struct redirectionSpecifier *redir = prog->redirections;
920 for (i = 0; i < prog->numRedirections; i++, redir++) {
921 switch (redir->type) {
925 case REDIRECT_OVERWRITE:
926 mode = O_RDWR | O_CREAT | O_TRUNC;
928 case REDIRECT_APPEND:
929 mode = O_RDWR | O_CREAT | O_APPEND;
933 openfd = open(redir->filename, mode, 0666);
935 /* this could get lost if stderr has been redirected, but
936 bash and ash both lose it as well (though zsh doesn't!) */
937 fprintf(stderr, "error opening %s: %s\n", redir->filename,
942 if (openfd != redir->fd) {
943 dup2(openfd, redir->fd);
952 static int busy_loop(FILE * input)
955 char *nextCommand = NULL;
956 struct jobSet jobList = { NULL, NULL };
963 /* save current owner of TTY so we can restore it on exit */
964 parent_pgrp = tcgetpgrp(0);
966 command = (char *) calloc(BUFSIZ, sizeof(char));
968 /* don't pay any attention to this signal; it just confuses
969 things and isn't really meant for shells anyway */
970 signal(SIGTTOU, SIG_IGN);
974 /* no job is in the foreground */
976 /* see if any background processes have exited */
980 if (getCommand(input, command))
982 nextCommand = command;
985 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
987 runCommand(newJob, &jobList, inBg);
990 /* a job is running in the foreground; wait for it */
992 while (!jobList.fg->progs[i].pid ||
993 jobList.fg->progs[i].isStopped) i++;
995 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
997 if (WIFEXITED(status) || WIFSIGNALED(status)) {
998 /* the child exited */
999 jobList.fg->runningProgs--;
1000 jobList.fg->progs[i].pid = 0;
1002 if (!jobList.fg->runningProgs) {
1005 removeJob(&jobList, jobList.fg);
1009 /* the child was stopped */
1010 jobList.fg->stoppedProgs++;
1011 jobList.fg->progs[i].isStopped = 1;
1013 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1014 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1015 "Stopped", jobList.fg->text);
1021 /* move the shell to the foreground */
1022 /* suppress messages when run from /linuxrc mag@sysgo.de */
1023 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1024 perror("tcsetpgrp");
1030 /* return controlling TTY back to parent process group before exiting */
1031 if (tcsetpgrp(0, parent_pgrp))
1032 perror("tcsetpgrp");
1034 /* return exit status if called with "-c" */
1035 if (input == NULL && WIFEXITED(status))
1036 return WEXITSTATUS(status);
1042 int shell_main(int argc, char **argv)
1044 FILE *input = stdin;
1046 /* initialize the cwd */
1047 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1049 fatalError("out of memory\n");
1051 getcwd(cwd, sizeof(char)*BUFSIZ);
1053 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1055 signal(SIGWINCH, win_changed);
1059 //if (argv[0] && argv[0][0] == '-') {
1060 // builtin_source("/etc/profile");
1064 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1065 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1067 if (argv[1][0]=='-' && argv[1][1]=='c') {
1069 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1070 if (local_pending_command == 0) {
1071 fatalError("out of memory\n");
1073 for(i=2; i<argc; i++)
1075 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1076 local_pending_command = realloc(local_pending_command,
1077 strlen(local_pending_command) + strlen(argv[i]));
1078 if (local_pending_command==NULL)
1079 fatalError("commands for -c option too long\n");
1081 strcat(local_pending_command, argv[i]);
1082 if ( (i + 1) < argc)
1083 strcat(local_pending_command, " ");
1088 else if (argv[1][0]=='-') {
1092 input = fopen(argv[1], "r");
1094 fatalError("Couldn't open file '%s': %s\n", argv[1],
1100 return (busy_loop(input));