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
29 #define BB_FEATURE_SH_BACKTICKS
42 #include <sys/ioctl.h>
45 #ifdef BB_FEATURE_SH_COMMAND_EDITING
49 #define bb_need_full_version
50 #define BB_DECLARE_EXTERN
53 #define MAX_READ 128 /* size of input buffer for `read' builtin */
54 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
57 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
62 struct job *head; /* head of list of running jobs */
63 struct job *fg; /* current foreground job */
66 struct redirectionSpecifier {
67 enum redirectionType type; /* type of redirection */
68 int fd; /* file descriptor being redirected */
69 char *filename; /* file to redirect fd to */
73 pid_t pid; /* 0 if exited */
74 char **argv; /* program name and arguments */
75 int numRedirections; /* elements in redirection array */
76 struct redirectionSpecifier *redirections; /* I/O redirections */
77 glob_t globResult; /* result of parameter globbing */
78 int freeGlob; /* should we globfree(&globResult)? */
79 int isStopped; /* is the program currently running? */
83 int jobId; /* job number */
84 int numProgs; /* total number of programs in job */
85 int runningProgs; /* number of programs running */
86 char *text; /* name of job */
87 char *cmdBuf; /* buffer various argv's point into */
88 pid_t pgrp; /* process group ID for the job */
89 struct childProgram *progs; /* array of programs in job */
90 struct job *next; /* to track background commands */
91 int stoppedProgs; /* number of programs alive, but stopped */
94 struct builtInCommand {
96 char *descr; /* description */
97 char *usage; /* usage */
98 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
101 /* function prototypes for builtins */
102 static int builtin_cd(struct job *cmd, struct jobSet *junk);
103 static int builtin_env(struct job *dummy, struct jobSet *junk);
104 static int builtin_exit(struct job *cmd, struct jobSet *junk);
105 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
106 static int builtin_help(struct job *cmd, struct jobSet *junk);
107 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
108 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
109 static int builtin_export(struct job *cmd, struct jobSet *junk);
110 static int builtin_source(struct job *cmd, struct jobSet *jobList);
111 static int builtin_unset(struct job *cmd, struct jobSet *junk);
112 static int builtin_read(struct job *cmd, struct jobSet *junk);
115 /* function prototypes for shell stuff */
116 static void checkJobs(struct jobSet *jobList);
117 static int getCommand(FILE * source, char *command);
118 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
119 static int setupRedirections(struct childProgram *prog);
120 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
121 static int busy_loop(FILE * input);
124 /* Table of built-in functions (these are non-forking builtins, meaning they
125 * can change global variables in the parent shell process but they will not
126 * work with pipes and redirects; 'unset foo | whatever' will not work) */
127 static struct builtInCommand bltins[] = {
128 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
129 {"cd", "Change working directory", "cd [dir]", builtin_cd},
130 {"exit", "Exit from shell()", "exit", builtin_exit},
131 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
132 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
133 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
134 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
135 {"read", "Input environment variable", "read [VAR]", builtin_read},
136 {NULL, NULL, NULL, NULL}
139 /* Table of forking built-in functions (things that fork cannot change global
140 * variables in the parent process, such as the current working directory) */
141 static struct builtInCommand bltins_forking[] = {
142 {"env", "Print all environment variables", "env", builtin_env},
143 {"pwd", "Print current directory", "pwd", builtin_pwd},
144 {".", "Source-in and run commands in a file", ". filename", builtin_source},
145 {"help", "List shell built-in commands", "help", builtin_help},
146 {NULL, NULL, NULL, NULL}
149 static char *prompt = "# ";
150 static char *cwd = NULL;
151 static char *local_pending_command = NULL;
153 #ifdef BB_FEATURE_SH_COMMAND_EDITING
154 void win_changed(int junk)
156 struct winsize win = { 0, 0, 0, 0 };
157 ioctl(0, TIOCGWINSZ, &win);
158 if (win.ws_col > 0) {
159 cmdedit_setwidth( win.ws_col - 1);
165 /* built-in 'cd <path>' handler */
166 static int builtin_cd(struct job *cmd, struct jobSet *junk)
170 if (!cmd->progs[0].argv[1] == 1)
171 newdir = getenv("HOME");
173 newdir = cmd->progs[0].argv[1];
175 printf("cd: %s: %s\n", newdir, strerror(errno));
178 getcwd(cwd, sizeof(cwd));
183 /* built-in 'env' handler */
184 static int builtin_env(struct job *dummy, struct jobSet *junk)
188 for (e = environ; *e; e++) {
189 fprintf(stdout, "%s\n", *e);
194 /* built-in 'exit' handler */
195 static int builtin_exit(struct job *cmd, struct jobSet *junk)
197 if (!cmd->progs[0].argv[1] == 1)
200 return(atoi(cmd->progs[0].argv[1]));
203 /* built-in 'fg' and 'bg' handler */
204 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
207 struct job *job=NULL;
209 if (!jobList->head) {
210 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
211 errorMsg("%s: exactly one argument is expected\n",
212 cmd->progs[0].argv[0]);
215 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
216 errorMsg("%s: bad argument '%s'\n",
217 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
219 for (job = jobList->head; job; job = job->next) {
220 if (job->jobId == jobNum) {
230 errorMsg("%s: unknown job %d\n",
231 cmd->progs[0].argv[0], jobNum);
235 if (*cmd->progs[0].argv[0] == 'f') {
236 /* Make this job the foreground job */
237 /* suppress messages when run from /linuxrc mag@sysgo.de */
238 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
243 /* Restart the processes in the job */
244 for (i = 0; i < job->numProgs; i++)
245 job->progs[i].isStopped = 0;
247 kill(-job->pgrp, SIGCONT);
249 job->stoppedProgs = 0;
254 /* built-in 'help' handler */
255 static int builtin_help(struct job *dummy, struct jobSet *junk)
257 struct builtInCommand *x;
259 fprintf(stdout, "\nBuilt-in commands:\n");
260 fprintf(stdout, "-------------------\n");
261 for (x = bltins; x->cmd; x++) {
262 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
264 for (x = bltins_forking; x->cmd; x++) {
265 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
267 fprintf(stdout, "\n\n");
271 /* built-in 'jobs' handler */
272 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
277 for (job = jobList->head; job; job = job->next) {
278 if (job->runningProgs == job->stoppedProgs)
279 statusString = "Stopped";
281 statusString = "Running";
283 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
289 /* built-in 'pwd' handler */
290 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
292 getcwd(cwd, sizeof(cwd));
293 fprintf(stdout, "%s\n", cwd);
297 /* built-in 'export VAR=value' handler */
298 static int builtin_export(struct job *cmd, struct jobSet *junk)
302 if (!cmd->progs[0].argv[1] == 1) {
303 return (builtin_env(cmd, junk));
305 res = putenv(cmd->progs[0].argv[1]);
307 fprintf(stdout, "export: %s\n", strerror(errno));
311 /* built-in 'read VAR' handler */
312 static int builtin_read(struct job *cmd, struct jobSet *junk)
314 int res = 0, len, newlen;
316 char string[MAX_READ];
318 if (cmd->progs[0].argv[1]) {
319 /* argument (VAR) given: put "VAR=" into buffer */
320 strcpy(string, cmd->progs[0].argv[1]);
321 len = strlen(string);
324 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
325 newlen = strlen(string);
327 string[--newlen] = '\0'; /* chomp trailing newline */
329 ** string should now contain "VAR=<value>"
330 ** copy it (putenv() won't do that, so we must make sure
331 ** the string resides in a static buffer!)
334 if((s = strdup(string)))
337 fprintf(stdout, "read: %s\n", strerror(errno));
340 fgets(string, sizeof(string), stdin);
345 /* Built-in '.' handler (read-in and execute commands from file) */
346 static int builtin_source(struct job *cmd, struct jobSet *junk)
351 if (!cmd->progs[0].argv[1] == 1)
354 input = fopen(cmd->progs[0].argv[1], "r");
356 fprintf(stdout, "Couldn't open file '%s'\n",
357 cmd->progs[0].argv[1]);
361 /* Now run the file */
362 status = busy_loop(input);
366 /* built-in 'unset VAR' handler */
367 static int builtin_unset(struct job *cmd, struct jobSet *junk)
369 if (!cmd->progs[0].argv[1] == 1) {
370 fprintf(stdout, "unset: parameter required.\n");
373 unsetenv(cmd->progs[0].argv[1]);
377 /* free up all memory from a job */
378 static void freeJob(struct job *cmd)
382 for (i = 0; i < cmd->numProgs; i++) {
383 free(cmd->progs[i].argv);
384 if (cmd->progs[i].redirections)
385 free(cmd->progs[i].redirections);
386 if (cmd->progs[i].freeGlob)
387 globfree(&cmd->progs[i].globResult);
393 memset(cmd, 0, sizeof(struct job));
396 /* remove a job from the jobList */
397 static void removeJob(struct jobSet *jobList, struct job *job)
402 if (job == jobList->head) {
403 jobList->head = job->next;
405 prevJob = jobList->head;
406 while (prevJob->next != job)
407 prevJob = prevJob->next;
408 prevJob->next = job->next;
414 /* Checks to see if any background processes have exited -- if they
415 have, figure out why and see if a job has completed */
416 static void checkJobs(struct jobSet *jobList)
423 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
424 for (job = jobList->head; job; job = job->next) {
426 while (progNum < job->numProgs &&
427 job->progs[progNum].pid != childpid) progNum++;
428 if (progNum < job->numProgs)
432 /* This happens on backticked commands */
436 if (WIFEXITED(status) || WIFSIGNALED(status)) {
439 job->progs[progNum].pid = 0;
441 if (!job->runningProgs) {
442 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
443 removeJob(jobList, job);
448 job->progs[progNum].isStopped = 1;
450 if (job->stoppedProgs == job->numProgs) {
451 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
457 if (childpid == -1 && errno != ECHILD)
461 static int getCommand(FILE * source, char *command)
463 if (source == NULL) {
464 if (local_pending_command) {
465 /* a command specified (-c option): return it & mark it done */
466 strcpy(command, local_pending_command);
467 free(local_pending_command);
468 local_pending_command = NULL;
474 if (source == stdin) {
475 #ifdef BB_FEATURE_SH_COMMAND_EDITING
478 len=fprintf(stdout, "%s %s", cwd, prompt);
480 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
481 sprintf(promptStr, "%s %s", cwd, prompt);
482 cmdedit_read_input(promptStr, command);
486 fprintf(stdout, "%s %s", cwd, prompt);
491 if (!fgets(command, BUFSIZ - 2, source)) {
497 /* remove trailing newline */
498 command[strlen(command) - 1] = '\0';
503 static void globLastArgument(struct childProgram *prog, int *argcPtr,
507 int argcAlloced = *argcAllocedPtr;
511 char *src, *dst, *var;
513 if (argc > 1) { /* cmd->globResult is already initialized */
515 i = prog->globResult.gl_pathc;
521 /* do shell variable substitution */
522 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
523 prog->argv[argc - 1] = var;
525 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
526 if (rc == GLOB_NOSPACE) {
527 errorMsg("out of space during glob operation\n");
529 } else if (rc == GLOB_NOMATCH ||
530 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
531 !strcmp(prog->argv[argc - 1],
532 prog->globResult.gl_pathv[i]))) {
533 /* we need to remove whatever \ quoting is still present */
534 src = dst = prog->argv[argc - 1];
542 argcAlloced += (prog->globResult.gl_pathc - i);
544 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
545 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
546 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
547 argc += (prog->globResult.gl_pathc - i - 1);
550 *argcAllocedPtr = argcAlloced;
554 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
555 line). If a valid command is found, commandPtr is set to point to
556 the beginning of the next command (if the original command had more
557 then one job associated with it) or NULL if no more commands are
559 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
562 char *returnCommand = NULL;
563 char *src, *buf, *chptr;
570 struct childProgram *prog;
572 /* skip leading white space */
573 while (**commandPtr && isspace(**commandPtr))
576 /* this handles empty lines or leading '#' characters */
577 if (!**commandPtr || (**commandPtr == '#')) {
584 job->progs = xmalloc(sizeof(*job->progs));
586 /* We set the argv elements to point inside of this string. The
587 memory is freed by freeJob(). Allocate twice the original
588 length in case we need to quote every single character.
590 Getting clean memory relieves us of the task of NULL
591 terminating things and makes the rest of this look a bit
592 cleaner (though it is, admittedly, a tad less efficient) */
593 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
597 prog->numRedirections = 0;
598 prog->redirections = NULL;
603 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
604 prog->argv[0] = job->cmdBuf;
608 while (*src && !done) {
615 errorMsg("character expected after \\\n");
620 /* in shell, "\'" should yield \' */
623 } else if (*src == '*' || *src == '?' || *src == '[' ||
624 *src == ']') *buf++ = '\\';
626 } else if (isspace(*src)) {
627 if (*prog->argv[argc]) {
629 /* +1 here leaves room for the NULL which ends argv */
630 if ((argc + 1) == argvAlloced) {
632 prog->argv = realloc(prog->argv,
633 sizeof(*prog->argv) *
636 globLastArgument(prog, &argc, &argvAlloced);
637 prog->argv[argc] = buf;
646 case '#': /* comment */
650 case '>': /* redirections */
652 i = prog->numRedirections++;
653 prog->redirections = realloc(prog->redirections,
654 sizeof(*prog->redirections) *
657 prog->redirections[i].fd = -1;
658 if (buf != prog->argv[argc]) {
659 /* the stuff before this character may be the file number
661 prog->redirections[i].fd =
662 strtol(prog->argv[argc], &chptr, 10);
664 if (*chptr && *prog->argv[argc]) {
666 globLastArgument(prog, &argc, &argvAlloced);
667 prog->argv[argc] = buf;
671 if (prog->redirections[i].fd == -1) {
673 prog->redirections[i].fd = 1;
675 prog->redirections[i].fd = 0;
680 prog->redirections[i].type =
681 REDIRECT_APPEND, src++;
683 prog->redirections[i].type = REDIRECT_OVERWRITE;
685 prog->redirections[i].type = REDIRECT_INPUT;
688 /* This isn't POSIX sh compliant. Oh well. */
690 while (isspace(*chptr))
694 errorMsg("file name expected after %c\n", *src);
700 prog->redirections[i].filename = buf;
701 while (*chptr && !isspace(*chptr))
704 src = chptr - 1; /* we src++ later */
705 prog->argv[argc] = ++buf;
709 /* finish this command */
710 if (*prog->argv[argc])
713 errorMsg("empty command in pipe\n");
718 prog->argv[argc] = NULL;
720 /* and start the next */
722 job->progs = realloc(job->progs,
723 sizeof(*job->progs) * job->numProgs);
724 prog = job->progs + (job->numProgs - 1);
725 prog->numRedirections = 0;
726 prog->redirections = NULL;
731 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
732 prog->argv[0] = ++buf;
735 while (*src && isspace(*src))
739 errorMsg("empty command in pipe\n");
744 src--; /* we'll ++ it at the end of the loop */
748 case '&': /* background */
750 case ';': /* multiple commands */
752 returnCommand = *commandPtr + (src - *commandPtr) + 1;
758 errorMsg("character expected after \\\n");
762 if (*src == '*' || *src == '[' || *src == ']'
763 || *src == '?') *buf++ = '\\';
765 #ifdef BB_FEATURE_SH_BACKTICKS
767 /* Exec a backtick-ed command */
769 char* charptr1=NULL, *charptr2;
772 struct jobSet njobList = { NULL, NULL };
776 ptr=strchr(++src, '`');
778 fprintf(stderr, "Unmatched '`' in command\n");
783 /* Make a copy of any stuff left over in the command
784 * line after the second backtick */
785 charptr2 = xmalloc(strlen(ptr)+1);
786 memcpy(charptr2, ptr+1, strlen(ptr));
788 /* Make some space to hold just the backticked command */
789 charptr1 = xmalloc(1+ptr-src);
790 snprintf(charptr1, 1+ptr-src, src);
791 newJob = xmalloc(sizeof(struct job));
792 /* Now parse and run the backticked command */
793 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
794 && newJob->numProgs) {
796 runCommand(newJob, &njobList, 0, pipefd);
801 /* Copy the output from the backtick-ed command into the
802 * command line, making extra room as needed */
804 charptr1 = xmalloc(BUFSIZ);
805 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
806 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
807 if (newSize > BUFSIZ) {
808 *commandPtr=realloc(*commandPtr, src - *commandPtr +
809 size + 1 + strlen(charptr2));
811 memcpy(src, charptr1, size);
819 /* Now paste into the *commandPtr all the stuff
820 * leftover after the second backtick */
821 memcpy(src, charptr2, strlen(charptr2));
822 fprintf(stderr,"*commandPtr='%s'\n", *commandPtr);
826 /* Now recursively call parseCommand to deal with the new
827 * and improved version of the command line with the backtick
828 * results expanded in place... */
829 return(parseCommand(commandPtr, job, jobList, isBg));
832 #endif // BB_FEATURE_SH_BACKTICKS
840 if (*prog->argv[argc]) {
842 globLastArgument(prog, &argc, &argvAlloced);
848 prog->argv[argc] = NULL;
850 if (!returnCommand) {
851 job->text = xmalloc(strlen(*commandPtr) + 1);
852 strcpy(job->text, *commandPtr);
854 /* This leaves any trailing spaces, which is a bit sloppy */
855 count = returnCommand - *commandPtr;
856 job->text = xmalloc(count + 1);
857 strncpy(job->text, *commandPtr, count);
858 job->text[count] = '\0';
861 *commandPtr = returnCommand;
867 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
870 int nextin=0, nextout, stdoutfd=fileno(stdout);
872 int pipefds[2]; /* pipefd[0] is for reading */
873 struct builtInCommand *x;
874 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
875 const struct BB_applet *a = applets;
878 for (i = 0; i < newJob->numProgs; i++) {
879 if ((i + 1) < newJob->numProgs) {
881 nextout = pipefds[1];
886 /* Check if the command matches any non-forking builtins */
887 for (x = bltins; x->cmd; x++) {
888 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
889 return (x->function(newJob, jobList));
893 if (!(newJob->progs[i].pid = fork())) {
894 signal(SIGTTOU, SIG_DFL);
896 if (outPipe[1]!=-1) {
898 nextout = stdoutfd = outPipe[1];
907 /* explicit redirections override pipes */
908 setupRedirections(newJob->progs + i);
910 /* Check if the command matches any of the other builtins */
911 for (x = bltins_forking; x->cmd; x++) {
912 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
913 exit (x->function(newJob, jobList));
916 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
917 /* Check if the command matches any busybox internal commands here */
918 /* TODO: Add matching on commands with paths appended (i.e. 'cat'
919 * currently works, but '/bin/cat' doesn't ) */
920 while (a->name != 0) {
921 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
923 char** argv=newJob->progs[i].argv;
924 for(argc=0;*argv!=NULL; argv++, argc++);
925 exit((*(a->main)) (argc, newJob->progs[i].argv));
931 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
932 fatalError("%s: %s\n", newJob->progs[i].argv[0],
935 if (outPipe[1]!=-1) {
939 /* put our child in the process group whose leader is the
940 first process in this pipe */
941 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
945 if (nextout != stdoutfd)
948 /* If there isn't another process, nextin is garbage
949 but it doesn't matter */
953 newJob->pgrp = newJob->progs[0].pid;
955 /* find the ID for the job to use */
957 for (job = jobList->head; job; job = job->next)
958 if (job->jobId >= newJob->jobId)
959 newJob->jobId = job->jobId + 1;
961 /* add the job to the list of running jobs */
962 if (!jobList->head) {
963 job = jobList->head = xmalloc(sizeof(*job));
965 for (job = jobList->head; job->next; job = job->next);
966 job->next = xmalloc(sizeof(*job));
972 job->runningProgs = job->numProgs;
973 job->stoppedProgs = 0;
976 /* we don't wait for background jobs to return -- append it
977 to the list of backgrounded jobs and leave it alone */
978 printf("[%d] %d\n", job->jobId,
979 newJob->progs[newJob->numProgs - 1].pid);
983 /* move the new process group into the foreground */
984 /* suppress messages when run from /linuxrc mag@sysgo.de */
985 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
992 static int setupRedirections(struct childProgram *prog)
997 struct redirectionSpecifier *redir = prog->redirections;
999 for (i = 0; i < prog->numRedirections; i++, redir++) {
1000 switch (redir->type) {
1001 case REDIRECT_INPUT:
1004 case REDIRECT_OVERWRITE:
1005 mode = O_RDWR | O_CREAT | O_TRUNC;
1007 case REDIRECT_APPEND:
1008 mode = O_RDWR | O_CREAT | O_APPEND;
1012 openfd = open(redir->filename, mode, 0666);
1014 /* this could get lost if stderr has been redirected, but
1015 bash and ash both lose it as well (though zsh doesn't!) */
1016 errorMsg("error opening %s: %s\n", redir->filename,
1021 if (openfd != redir->fd) {
1022 dup2(openfd, redir->fd);
1031 static int busy_loop(FILE * input)
1034 char *nextCommand = NULL;
1035 struct jobSet jobList = { NULL, NULL };
1042 /* save current owner of TTY so we can restore it on exit */
1043 parent_pgrp = tcgetpgrp(0);
1045 command = (char *) calloc(BUFSIZ, sizeof(char));
1047 /* don't pay any attention to this signal; it just confuses
1048 things and isn't really meant for shells anyway */
1049 signal(SIGTTOU, SIG_IGN);
1053 /* no job is in the foreground */
1055 /* see if any background processes have exited */
1056 checkJobs(&jobList);
1059 if (getCommand(input, command))
1061 nextCommand = command;
1064 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1066 int pipefds[2] = {-1,-1};
1067 runCommand(&newJob, &jobList, inBg, pipefds);
1071 command = (char *) calloc(BUFSIZ, sizeof(char));
1074 /* a job is running in the foreground; wait for it */
1076 while (!jobList.fg->progs[i].pid ||
1077 jobList.fg->progs[i].isStopped) i++;
1079 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1081 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1082 /* the child exited */
1083 jobList.fg->runningProgs--;
1084 jobList.fg->progs[i].pid = 0;
1086 if (!jobList.fg->runningProgs) {
1089 removeJob(&jobList, jobList.fg);
1093 /* the child was stopped */
1094 jobList.fg->stoppedProgs++;
1095 jobList.fg->progs[i].isStopped = 1;
1097 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1098 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1099 "Stopped", jobList.fg->text);
1105 /* move the shell to the foreground */
1106 /* suppress messages when run from /linuxrc mag@sysgo.de */
1107 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1108 perror("tcsetpgrp");
1114 /* return controlling TTY back to parent process group before exiting */
1115 if (tcsetpgrp(0, parent_pgrp))
1116 perror("tcsetpgrp");
1118 /* return exit status if called with "-c" */
1119 if (input == NULL && WIFEXITED(status))
1120 return WEXITSTATUS(status);
1126 int shell_main(int argc, char **argv)
1128 FILE *input = stdin;
1130 /* initialize the cwd */
1131 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1133 fatalError("out of memory\n");
1135 getcwd(cwd, sizeof(char)*BUFSIZ);
1137 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1139 signal(SIGWINCH, win_changed);
1143 //if (argv[0] && argv[0][0] == '-') {
1144 // builtin_source("/etc/profile");
1148 fprintf(stdout, "\n\n%s Built-in shell\n", full_version);
1149 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1151 if (argv[1][0]=='-' && argv[1][1]=='c') {
1153 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1154 if (local_pending_command == 0) {
1155 fatalError("out of memory\n");
1157 for(i=2; i<argc; i++)
1159 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1160 local_pending_command = realloc(local_pending_command,
1161 strlen(local_pending_command) + strlen(argv[i]));
1162 if (local_pending_command==NULL)
1163 fatalError("commands for -c option too long\n");
1165 strcat(local_pending_command, argv[i]);
1166 if ( (i + 1) < argc)
1167 strcat(local_pending_command, " ");
1172 else if (argv[1][0]=='-') {
1176 input = fopen(argv[1], "r");
1178 fatalError("Couldn't open file '%s': %s\n", argv[1],
1184 return (busy_loop(input));