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
30 //#define BB_FEATURE_SH_IF_EXPRESSIONS
31 #define BB_FEATURE_SH_ENVIRONMENT
44 #include <sys/ioctl.h>
48 #ifdef BB_FEATURE_SH_COMMAND_EDITING
52 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
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,
61 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
62 static const unsigned int IF_TRUE_CONTEXT=0x2;
63 static const unsigned int IF_FALSE_CONTEXT=0x4;
64 static const unsigned int THEN_EXP_CONTEXT=0x8;
65 static const unsigned int ELSE_EXP_CONTEXT=0x10;
69 struct job *head; /* head of list of running jobs */
70 struct job *fg; /* current foreground job */
73 struct redirectionSpecifier {
74 enum redirectionType type; /* type of redirection */
75 int fd; /* file descriptor being redirected */
76 char *filename; /* file to redirect fd to */
80 pid_t pid; /* 0 if exited */
81 char **argv; /* program name and arguments */
82 int numRedirections; /* elements in redirection array */
83 struct redirectionSpecifier *redirections; /* I/O redirections */
84 glob_t globResult; /* result of parameter globbing */
85 int freeGlob; /* should we globfree(&globResult)? */
86 int isStopped; /* is the program currently running? */
90 int jobId; /* job number */
91 int numProgs; /* total number of programs in job */
92 int runningProgs; /* number of programs running */
93 char *text; /* name of job */
94 char *cmdBuf; /* buffer various argv's point into */
95 pid_t pgrp; /* process group ID for the job */
96 struct childProgram *progs; /* array of programs in job */
97 struct job *next; /* to track background commands */
98 int stoppedProgs; /* number of programs alive, but stopped */
99 int jobContext; /* bitmask defining current context */
102 struct builtInCommand {
103 char *cmd; /* name */
104 char *descr; /* description */
105 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
108 /* function prototypes for builtins */
109 static int builtin_cd(struct job *cmd, struct jobSet *junk);
110 static int builtin_env(struct job *dummy, struct jobSet *junk);
111 static int builtin_exec(struct job *cmd, struct jobSet *junk);
112 static int builtin_exit(struct job *cmd, struct jobSet *junk);
113 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
114 static int builtin_help(struct job *cmd, struct jobSet *junk);
115 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
116 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
117 static int builtin_export(struct job *cmd, struct jobSet *junk);
118 static int builtin_source(struct job *cmd, struct jobSet *jobList);
119 static int builtin_unset(struct job *cmd, struct jobSet *junk);
120 static int builtin_read(struct job *cmd, struct jobSet *junk);
121 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
122 static int builtin_if(struct job *cmd, struct jobSet *junk);
123 static int builtin_then(struct job *cmd, struct jobSet *junk);
124 static int builtin_else(struct job *cmd, struct jobSet *junk);
125 static int builtin_fi(struct job *cmd, struct jobSet *junk);
129 /* function prototypes for shell stuff */
130 static void checkJobs(struct jobSet *jobList);
131 static int getCommand(FILE * source, char *command);
132 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
133 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
134 static int busy_loop(FILE * input);
137 /* Table of built-in functions (these are non-forking builtins, meaning they
138 * can change global variables in the parent shell process but they will not
139 * work with pipes and redirects; 'unset foo | whatever' will not work) */
140 static struct builtInCommand bltins[] = {
141 {"bg", "Resume a job in the background", builtin_fg_bg},
142 {"cd", "Change working directory", builtin_cd},
143 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
144 {"exit", "Exit from shell()", builtin_exit},
145 {"fg", "Bring job into the foreground", builtin_fg_bg},
146 {"jobs", "Lists the active jobs", builtin_jobs},
147 {"export", "Set environment variable", builtin_export},
148 {"unset", "Unset environment variable", builtin_unset},
149 {"read", "Input environment variable", builtin_read},
150 {".", "Source-in and run commands in a file", builtin_source},
151 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
152 {"if", NULL, builtin_if},
153 {"then", NULL, builtin_then},
154 {"else", NULL, builtin_else},
155 {"fi", NULL, builtin_fi},
160 /* Table of forking built-in functions (things that fork cannot change global
161 * variables in the parent process, such as the current working directory) */
162 static struct builtInCommand bltins_forking[] = {
163 {"env", "Print all environment variables", builtin_env},
164 {"pwd", "Print current directory", builtin_pwd},
165 {"help", "List shell built-in commands", builtin_help},
169 static char *prompt = "# ";
171 static char *local_pending_command = NULL;
172 static char *promptStr = NULL;
173 static struct jobSet jobList = { NULL, NULL };
176 #ifdef BB_FEATURE_SH_ENVIRONMENT
177 static int lastBgPid=-1;
178 static int lastReturnCode=-1;
179 static int showXtrace=FALSE;
183 #ifdef BB_FEATURE_SH_COMMAND_EDITING
184 void win_changed(int junk)
186 struct winsize win = { 0, 0, 0, 0 };
187 ioctl(0, TIOCGWINSZ, &win);
188 if (win.ws_col > 0) {
189 cmdedit_setwidth( win.ws_col - 1);
195 /* built-in 'cd <path>' handler */
196 static int builtin_cd(struct job *cmd, struct jobSet *junk)
200 if (!cmd->progs[0].argv[1] == 1)
201 newdir = getenv("HOME");
203 newdir = cmd->progs[0].argv[1];
205 printf("cd: %s: %s\n", newdir, strerror(errno));
208 getcwd(cwd, sizeof(char)*MAX_LINE);
213 /* built-in 'env' handler */
214 static int builtin_env(struct job *dummy, struct jobSet *junk)
218 for (e = environ; *e; e++) {
219 fprintf(stdout, "%s\n", *e);
224 /* built-in 'exec' handler */
225 static int builtin_exec(struct job *cmd, struct jobSet *junk)
227 if (cmd->progs[0].argv[1])
229 cmd->progs[0].argv++;
230 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
231 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
237 /* built-in 'exit' handler */
238 static int builtin_exit(struct job *cmd, struct jobSet *junk)
240 if (!cmd->progs[0].argv[1] == 1)
243 exit (atoi(cmd->progs[0].argv[1]));
246 /* built-in 'fg' and 'bg' handler */
247 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
250 struct job *job=NULL;
252 if (!jobList->head) {
253 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
254 errorMsg("%s: exactly one argument is expected\n",
255 cmd->progs[0].argv[0]);
258 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
259 errorMsg("%s: bad argument '%s'\n",
260 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
262 for (job = jobList->head; job; job = job->next) {
263 if (job->jobId == jobNum) {
273 errorMsg("%s: unknown job %d\n",
274 cmd->progs[0].argv[0], jobNum);
278 if (*cmd->progs[0].argv[0] == 'f') {
279 /* Make this job the foreground job */
280 /* suppress messages when run from /linuxrc mag@sysgo.de */
281 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
286 /* Restart the processes in the job */
287 for (i = 0; i < job->numProgs; i++)
288 job->progs[i].isStopped = 0;
290 kill(-job->pgrp, SIGCONT);
292 job->stoppedProgs = 0;
297 /* built-in 'help' handler */
298 static int builtin_help(struct job *dummy, struct jobSet *junk)
300 struct builtInCommand *x;
302 fprintf(stdout, "\nBuilt-in commands:\n");
303 fprintf(stdout, "-------------------\n");
304 for (x = bltins; x->cmd; x++) {
307 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
309 for (x = bltins_forking; x->cmd; x++) {
312 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
314 fprintf(stdout, "\n\n");
318 /* built-in 'jobs' handler */
319 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
324 for (job = jobList->head; job; job = job->next) {
325 if (job->runningProgs == job->stoppedProgs)
326 statusString = "Stopped";
328 statusString = "Running";
330 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
336 /* built-in 'pwd' handler */
337 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
339 getcwd(cwd, sizeof(char)*MAX_LINE);
340 fprintf(stdout, "%s\n", cwd);
344 /* built-in 'export VAR=value' handler */
345 static int builtin_export(struct job *cmd, struct jobSet *junk)
349 if (!cmd->progs[0].argv[1] == 1) {
350 return (builtin_env(cmd, junk));
352 res = putenv(cmd->progs[0].argv[1]);
354 fprintf(stdout, "export: %s\n", strerror(errno));
358 /* built-in 'read VAR' handler */
359 static int builtin_read(struct job *cmd, struct jobSet *junk)
361 int res = 0, len, newlen;
363 char string[MAX_READ];
365 if (cmd->progs[0].argv[1]) {
366 /* argument (VAR) given: put "VAR=" into buffer */
367 strcpy(string, cmd->progs[0].argv[1]);
368 len = strlen(string);
371 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
372 newlen = strlen(string);
374 string[--newlen] = '\0'; /* chomp trailing newline */
376 ** string should now contain "VAR=<value>"
377 ** copy it (putenv() won't do that, so we must make sure
378 ** the string resides in a static buffer!)
381 if((s = strdup(string)))
384 fprintf(stdout, "read: %s\n", strerror(errno));
387 fgets(string, sizeof(string), stdin);
392 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
393 /* Built-in handler for 'if' commands */
394 static int builtin_if(struct job *cmd, struct jobSet *jobList)
397 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
399 /* Now run the 'if' command */
400 status=strlen(charptr1);
401 local_pending_command = xmalloc(status+1);
402 strncpy(local_pending_command, charptr1, status);
403 local_pending_command[status]='\0';
405 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
407 status = busy_loop(NULL); /* Frees local_pending_command */
409 fprintf(stderr, "if test returned ");
413 fprintf(stderr, "TRUE\n");
415 cmd->jobContext |= IF_TRUE_CONTEXT;
418 fprintf(stderr, "FALSE\n");
420 cmd->jobContext |= IF_FALSE_CONTEXT;
426 /* Built-in handler for 'then' (part of the 'if' command) */
427 static int builtin_then(struct job *cmd, struct jobSet *junk)
430 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
432 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
433 errorMsg("unexpected token `then'\n");
436 /* If the if result was FALSE, skip the 'then' stuff */
437 if (cmd->jobContext & IF_FALSE_CONTEXT) {
441 cmd->jobContext |= THEN_EXP_CONTEXT;
442 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
444 /* Now run the 'then' command */
445 status=strlen(charptr1);
446 local_pending_command = xmalloc(status+1);
447 strncpy(local_pending_command, charptr1, status);
448 local_pending_command[status]='\0';
450 fprintf(stderr, "'then' now running '%s'\n", charptr1);
452 return( busy_loop(NULL));
455 /* Built-in handler for 'else' (part of the 'if' command) */
456 static int builtin_else(struct job *cmd, struct jobSet *junk)
459 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
461 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
462 errorMsg("unexpected token `else'\n");
465 /* If the if result was TRUE, skip the 'else' stuff */
466 if (cmd->jobContext & IF_TRUE_CONTEXT) {
470 cmd->jobContext |= ELSE_EXP_CONTEXT;
471 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
473 /* Now run the 'else' command */
474 status=strlen(charptr1);
475 local_pending_command = xmalloc(status+1);
476 strncpy(local_pending_command, charptr1, status);
477 local_pending_command[status]='\0';
479 fprintf(stderr, "'else' now running '%s'\n", charptr1);
481 return( busy_loop(NULL));
484 /* Built-in handler for 'fi' (part of the 'if' command) */
485 static int builtin_fi(struct job *cmd, struct jobSet *junk)
487 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
488 errorMsg("unexpected token `fi'\n");
491 /* Clear out the if and then context bits */
492 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
494 fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
500 /* Built-in '.' handler (read-in and execute commands from file) */
501 static int builtin_source(struct job *cmd, struct jobSet *junk)
506 if (!cmd->progs[0].argv[1] == 1)
509 input = fopen(cmd->progs[0].argv[1], "r");
511 fprintf(stdout, "Couldn't open file '%s'\n",
512 cmd->progs[0].argv[1]);
516 /* Now run the file */
517 status = busy_loop(input);
522 /* built-in 'unset VAR' handler */
523 static int builtin_unset(struct job *cmd, struct jobSet *junk)
525 if (!cmd->progs[0].argv[1] == 1) {
526 fprintf(stdout, "unset: parameter required.\n");
529 unsetenv(cmd->progs[0].argv[1]);
533 /* free up all memory from a job */
534 static void freeJob(struct job *cmd)
538 for (i = 0; i < cmd->numProgs; i++) {
539 free(cmd->progs[i].argv);
540 if (cmd->progs[i].redirections)
541 free(cmd->progs[i].redirections);
542 if (cmd->progs[i].freeGlob)
543 globfree(&cmd->progs[i].globResult);
549 memset(cmd, 0, sizeof(struct job));
552 /* remove a job from the jobList */
553 static void removeJob(struct jobSet *jobList, struct job *job)
558 if (job == jobList->head) {
559 jobList->head = job->next;
561 prevJob = jobList->head;
562 while (prevJob->next != job)
563 prevJob = prevJob->next;
564 prevJob->next = job->next;
570 /* Checks to see if any background processes have exited -- if they
571 have, figure out why and see if a job has completed */
572 static void checkJobs(struct jobSet *jobList)
579 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
580 for (job = jobList->head; job; job = job->next) {
582 while (progNum < job->numProgs &&
583 job->progs[progNum].pid != childpid) progNum++;
584 if (progNum < job->numProgs)
588 /* This happens on backticked commands */
592 if (WIFEXITED(status) || WIFSIGNALED(status)) {
595 job->progs[progNum].pid = 0;
597 if (!job->runningProgs) {
598 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
599 removeJob(jobList, job);
604 job->progs[progNum].isStopped = 1;
606 if (job->stoppedProgs == job->numProgs) {
607 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
613 if (childpid == -1 && errno != ECHILD)
617 static int setupRedirections(struct childProgram *prog)
622 struct redirectionSpecifier *redir = prog->redirections;
624 for (i = 0; i < prog->numRedirections; i++, redir++) {
625 switch (redir->type) {
629 case REDIRECT_OVERWRITE:
630 mode = O_WRONLY | O_CREAT | O_TRUNC;
632 case REDIRECT_APPEND:
633 mode = O_WRONLY | O_CREAT | O_APPEND;
637 openfd = open(redir->filename, mode, 0666);
639 /* this could get lost if stderr has been redirected, but
640 bash and ash both lose it as well (though zsh doesn't!) */
641 errorMsg("error opening %s: %s\n", redir->filename,
646 if (openfd != redir->fd) {
647 dup2(openfd, redir->fd);
656 static int getCommand(FILE * source, char *command)
658 if (source == NULL) {
659 if (local_pending_command) {
660 /* a command specified (-c option): return it & mark it done */
661 strcpy(command, local_pending_command);
662 free(local_pending_command);
663 local_pending_command = NULL;
669 if (source == stdin) {
670 #ifdef BB_FEATURE_SH_COMMAND_EDITING
674 ** enable command line editing only while a command line
675 ** is actually being read; otherwise, we'll end up bequeathing
676 ** atexit() handlers and other unwanted stuff to our
677 ** child processes (rob@sysgo.de)
680 signal(SIGWINCH, win_changed);
681 len=fprintf(stdout, "%s %s", cwd, prompt);
683 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
684 sprintf(promptStr, "%s %s", cwd, prompt);
685 cmdedit_read_input(promptStr, command);
688 signal(SIGWINCH, SIG_DFL);
691 fprintf(stdout, "%s %s", cwd, prompt);
696 if (!fgets(command, BUFSIZ - 2, source)) {
702 /* remove trailing newline */
703 command[strlen(command) - 1] = '\0';
708 #ifdef BB_FEATURE_SH_ENVIRONMENT
709 #define __MAX_INT_CHARS 7
710 static char* itoa(register int i)
712 static char a[__MAX_INT_CHARS];
713 register char *b = a + sizeof(a) - 1;
721 *--b = '0' + (i % 10);
731 static void globLastArgument(struct childProgram *prog, int *argcPtr,
734 int argc_l = *argcPtr;
735 int argcAlloced = *argcAllocedPtr;
739 char *src, *dst, *var;
741 if (argc_l > 1) { /* cmd->globResult is already initialized */
743 i = prog->globResult.gl_pathc;
749 /* do shell variable substitution */
750 if(*prog->argv[argc_l - 1] == '$') {
751 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
752 prog->argv[argc_l - 1] = var;
754 #ifdef BB_FEATURE_SH_ENVIRONMENT
756 switch(*(prog->argv[argc_l - 1] + 1)) {
758 prog->argv[argc_l - 1] = itoa(lastReturnCode);
761 prog->argv[argc_l - 1] = itoa(getpid());
764 prog->argv[argc_l - 1] = itoa(argc-1);
768 *(prog->argv[argc_l - 1])='\0';
770 prog->argv[argc_l - 1] = itoa(lastBgPid);
772 case '0':case '1':case '2':case '3':case '4':
773 case '5':case '6':case '7':case '8':case '9':
775 int index=*(prog->argv[argc_l - 1] + 1)-48;
777 *(prog->argv[argc_l - 1])='\0';
779 prog->argv[argc_l - 1] = argv[index];
788 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
789 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
790 if (rc == GLOB_NOSPACE) {
791 errorMsg("out of space during glob operation\n");
793 } else if (rc == GLOB_NOMATCH ||
794 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
795 strcmp(prog->argv[argc_l - 1],
796 prog->globResult.gl_pathv[i]) == 0)) {
797 /* we need to remove whatever \ quoting is still present */
798 src = dst = prog->argv[argc_l - 1];
802 *dst++ = process_escape_sequence(&src);
810 argcAlloced += (prog->globResult.gl_pathc - i);
811 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
812 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
813 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
814 argc_l += (prog->globResult.gl_pathc - i - 1);
817 src = dst = prog->argv[argc_l - 1];
821 *dst++ = process_escape_sequence(&src);
829 prog->globResult.gl_pathc=0;
831 prog->globResult.gl_pathv=NULL;
833 *argcAllocedPtr = argcAlloced;
837 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
838 line). If a valid command is found, commandPtr is set to point to
839 the beginning of the next command (if the original command had more
840 then one job associated with it) or NULL if no more commands are
842 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
845 char *returnCommand = NULL;
846 char *src, *buf, *chptr;
853 struct childProgram *prog;
855 /* skip leading white space */
856 while (**commandPtr && isspace(**commandPtr))
859 /* this handles empty lines or leading '#' characters */
860 if (!**commandPtr || (**commandPtr == '#')) {
867 job->progs = xmalloc(sizeof(*job->progs));
869 /* We set the argv elements to point inside of this string. The
870 memory is freed by freeJob(). Allocate twice the original
871 length in case we need to quote every single character.
873 Getting clean memory relieves us of the task of NULL
874 terminating things and makes the rest of this look a bit
875 cleaner (though it is, admittedly, a tad less efficient) */
876 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
880 prog->numRedirections = 0;
881 prog->redirections = NULL;
886 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
887 prog->argv[0] = job->cmdBuf;
891 while (*src && !done) {
898 errorMsg("character expected after \\\n");
903 /* in shell, "\'" should yield \' */
906 } else if (*src == '*' || *src == '?' || *src == '[' ||
907 *src == ']') *buf++ = '\\';
909 } else if (isspace(*src)) {
910 if (*prog->argv[argc_l]) {
912 /* +1 here leaves room for the NULL which ends argv */
913 if ((argc_l + 1) == argvAlloced) {
915 prog->argv = xrealloc(prog->argv,
916 sizeof(*prog->argv) *
919 globLastArgument(prog, &argc_l, &argvAlloced);
920 prog->argv[argc_l] = buf;
929 case '#': /* comment */
936 case '>': /* redirections */
938 i = prog->numRedirections++;
939 prog->redirections = xrealloc(prog->redirections,
940 sizeof(*prog->redirections) *
943 prog->redirections[i].fd = -1;
944 if (buf != prog->argv[argc_l]) {
945 /* the stuff before this character may be the file number
947 prog->redirections[i].fd =
948 strtol(prog->argv[argc_l], &chptr, 10);
950 if (*chptr && *prog->argv[argc_l]) {
952 globLastArgument(prog, &argc_l, &argvAlloced);
953 prog->argv[argc_l] = buf;
957 if (prog->redirections[i].fd == -1) {
959 prog->redirections[i].fd = 1;
961 prog->redirections[i].fd = 0;
966 prog->redirections[i].type =
967 REDIRECT_APPEND, src++;
969 prog->redirections[i].type = REDIRECT_OVERWRITE;
971 prog->redirections[i].type = REDIRECT_INPUT;
974 /* This isn't POSIX sh compliant. Oh well. */
976 while (isspace(*chptr))
980 errorMsg("file name expected after %c\n", *src);
986 prog->redirections[i].filename = buf;
987 while (*chptr && !isspace(*chptr))
990 src = chptr - 1; /* we src++ later */
991 prog->argv[argc_l] = ++buf;
995 /* finish this command */
996 if (*prog->argv[argc_l])
999 errorMsg("empty command in pipe\n");
1004 prog->argv[argc_l] = NULL;
1006 /* and start the next */
1008 job->progs = xrealloc(job->progs,
1009 sizeof(*job->progs) * job->numProgs);
1010 prog = job->progs + (job->numProgs - 1);
1011 prog->numRedirections = 0;
1012 prog->redirections = NULL;
1014 prog->isStopped = 0;
1018 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1019 prog->argv[0] = ++buf;
1022 while (*src && isspace(*src))
1026 errorMsg("empty command in pipe\n");
1031 src--; /* we'll ++ it at the end of the loop */
1035 case '&': /* background */
1037 case ';': /* multiple commands */
1039 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1042 #ifdef BB_FEATURE_SH_BACKTICKS
1044 /* Exec a backtick-ed command */
1046 char* charptr1=NULL, *charptr2;
1049 struct jobSet njobList = { NULL, NULL };
1053 ptr=strchr(++src, '`');
1055 fprintf(stderr, "Unmatched '`' in command\n");
1060 /* Make some space to hold just the backticked command */
1061 charptr1 = charptr2 = xmalloc(1+ptr-src);
1062 memcpy(charptr1, src, ptr-src);
1063 charptr1[ptr-src] = '\0';
1064 newJob = xmalloc(sizeof(struct job));
1065 /* Now parse and run the backticked command */
1066 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1067 && newJob->numProgs) {
1069 runCommand(newJob, &njobList, 0, pipefd);
1075 /* Make a copy of any stuff left over in the command
1076 * line after the second backtick */
1077 charptr2 = xmalloc(strlen(ptr)+1);
1078 memcpy(charptr2, ptr+1, strlen(ptr));
1081 /* Copy the output from the backtick-ed command into the
1082 * command line, making extra room as needed */
1084 charptr1 = xmalloc(BUFSIZ);
1085 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1086 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1087 if (newSize > BUFSIZ) {
1088 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1089 size + 1 + strlen(charptr2));
1091 memcpy(src, charptr1, size);
1099 /* Now paste into the *commandPtr all the stuff
1100 * leftover after the second backtick */
1101 memcpy(src, charptr2, strlen(charptr2)+1);
1104 /* Now recursively call parseCommand to deal with the new
1105 * and improved version of the command line with the backtick
1106 * results expanded in place... */
1108 return(parseCommand(commandPtr, job, jobList, inBg));
1111 #endif // BB_FEATURE_SH_BACKTICKS
1116 errorMsg("character expected after \\\n");
1120 if (*src == '*' || *src == '[' || *src == ']'
1121 || *src == '?') *buf++ = '\\';
1130 if (*prog->argv[argc_l]) {
1132 globLastArgument(prog, &argc_l, &argvAlloced);
1138 prog->argv[argc_l] = NULL;
1140 if (!returnCommand) {
1141 job->text = xmalloc(strlen(*commandPtr) + 1);
1142 strcpy(job->text, *commandPtr);
1144 /* This leaves any trailing spaces, which is a bit sloppy */
1145 count = returnCommand - *commandPtr;
1146 job->text = xmalloc(count + 1);
1147 strncpy(job->text, *commandPtr, count);
1148 job->text[count] = '\0';
1151 *commandPtr = returnCommand;
1156 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1160 int nextin, nextout;
1161 int pipefds[2]; /* pipefd[0] is for reading */
1162 struct builtInCommand *x;
1163 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1164 struct BB_applet search_applet, *applet;
1167 nextin = 0, nextout = 1;
1168 for (i = 0; i < newJob->numProgs; i++) {
1169 if ((i + 1) < newJob->numProgs) {
1171 nextout = pipefds[1];
1173 if (outPipe[1]!=-1) {
1174 nextout = outPipe[1];
1180 #ifdef BB_FEATURE_SH_ENVIRONMENT
1181 if (showXtrace==TRUE) {
1183 fprintf(stderr, "+ ");
1184 for (j = 0; newJob->progs[i].argv[j]; j++)
1185 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1186 fprintf(stderr, "\n");
1190 /* Check if the command matches any non-forking builtins */
1191 for (x = bltins; x->cmd; x++) {
1192 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1193 return(x->function(newJob, jobList));
1197 if (!(newJob->progs[i].pid = fork())) {
1198 signal(SIGTTOU, SIG_DFL);
1200 if (outPipe[1]!=-1) {
1215 /* explicit redirections override pipes */
1216 setupRedirections(newJob->progs + i);
1218 /* Check if the command matches any of the other builtins */
1219 for (x = bltins_forking; x->cmd; x++) {
1220 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1222 exit (x->function(newJob, jobList));
1225 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1226 /* Check if the command matches any busybox internal
1227 * commands ("applets") here. Following discussions from
1228 * November 2000 on busybox@opensource.lineo.com, don't use
1229 * get_last_path_component(). This way explicit (with
1230 * slashes) filenames will never be interpreted as an
1231 * applet, just like with builtins. This way the user can
1232 * override an applet with an explicit filename reference.
1233 * The only downside to this change is that an explicit
1234 * /bin/foo invocation fill fork and exec /bin/foo, even if
1235 * /bin/foo is a symlink to busybox.
1237 search_applet.name = newJob->progs[i].argv[0];
1239 #ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
1240 /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
1241 * if you run /bin/cat, it will use BusyBox cat even if
1242 * /bin/cat exists on the filesystem and is _not_ busybox.
1243 * Some systems want this, others do not. Choose wisely. :-)
1245 search_applet.name = get_last_path_component(search_applet.name);
1248 /* Do a binary search to find the applet entry given the name. */
1249 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1250 sizeof(struct BB_applet), applet_name_compare);
1251 if (applet != NULL) {
1253 char** argv=newJob->progs[i].argv;
1254 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1255 applet_name=applet->name;
1257 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1261 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1262 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1265 if (outPipe[1]!=-1) {
1269 /* put our child in the process group whose leader is the
1270 first process in this pipe */
1271 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1277 /* If there isn't another process, nextin is garbage
1278 but it doesn't matter */
1279 nextin = pipefds[0];
1282 newJob->pgrp = newJob->progs[0].pid;
1284 /* find the ID for the theJob to use */
1286 for (theJob = jobList->head; theJob; theJob = theJob->next)
1287 if (theJob->jobId >= newJob->jobId)
1288 newJob->jobId = theJob->jobId + 1;
1290 /* add the theJob to the list of running jobs */
1291 if (!jobList->head) {
1292 theJob = jobList->head = xmalloc(sizeof(*theJob));
1294 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1295 theJob->next = xmalloc(sizeof(*theJob));
1296 theJob = theJob->next;
1300 theJob->next = NULL;
1301 theJob->runningProgs = theJob->numProgs;
1302 theJob->stoppedProgs = 0;
1305 /* we don't wait for background theJobs to return -- append it
1306 to the list of backgrounded theJobs and leave it alone */
1307 printf("[%d] %d\n", theJob->jobId,
1308 newJob->progs[newJob->numProgs - 1].pid);
1309 #ifdef BB_FEATURE_SH_ENVIRONMENT
1310 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1313 jobList->fg = theJob;
1315 /* move the new process group into the foreground */
1316 /* suppress messages when run from /linuxrc mag@sysgo.de */
1317 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1318 perror("tcsetpgrp");
1324 static int busy_loop(FILE * input)
1327 char *nextCommand = NULL;
1333 newJob.jobContext = REGULAR_JOB_CONTEXT;
1335 /* save current owner of TTY so we can restore it on exit */
1336 parent_pgrp = tcgetpgrp(0);
1338 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1340 /* don't pay any attention to this signal; it just confuses
1341 things and isn't really meant for shells anyway */
1342 signal(SIGTTOU, SIG_IGN);
1346 /* no job is in the foreground */
1348 /* see if any background processes have exited */
1349 checkJobs(&jobList);
1352 if (getCommand(input, command))
1354 nextCommand = command;
1357 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1359 int pipefds[2] = {-1,-1};
1360 runCommand(&newJob, &jobList, inBg, pipefds);
1364 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1368 /* a job is running in the foreground; wait for it */
1370 while (!jobList.fg->progs[i].pid ||
1371 jobList.fg->progs[i].isStopped == 1) i++;
1373 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1375 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1376 /* the child exited */
1377 jobList.fg->runningProgs--;
1378 jobList.fg->progs[i].pid = 0;
1380 #ifdef BB_FEATURE_SH_ENVIRONMENT
1381 lastReturnCode=WEXITSTATUS(status);
1384 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1386 if (!jobList.fg->runningProgs) {
1389 removeJob(&jobList, jobList.fg);
1393 /* the child was stopped */
1394 jobList.fg->stoppedProgs++;
1395 jobList.fg->progs[i].isStopped = 1;
1397 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1398 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1399 "Stopped", jobList.fg->text);
1405 /* move the shell to the foreground */
1406 /* suppress messages when run from /linuxrc mag@sysgo.de */
1407 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1408 perror("tcsetpgrp");
1414 /* return controlling TTY back to parent process group before exiting */
1415 if (tcsetpgrp(0, parent_pgrp))
1416 perror("tcsetpgrp");
1418 /* return exit status if called with "-c" */
1419 if (input == NULL && WIFEXITED(status))
1420 return WEXITSTATUS(status);
1426 #ifdef BB_FEATURE_CLEAN_UP
1427 void free_memory(void)
1433 if (local_pending_command)
1434 free(local_pending_command);
1436 if (jobList.fg && !jobList.fg->runningProgs) {
1437 removeJob(&jobList, jobList.fg);
1443 int shell_main(int argc_l, char **argv_l)
1445 int opt, interactive=FALSE;
1446 FILE *input = stdin;
1451 //if (argv[0] && argv[0][0] == '-') {
1452 // builtin_source("/etc/profile");
1455 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1459 if (local_pending_command != 0)
1460 fatalError("multiple -c arguments\n");
1461 local_pending_command = xstrdup(argv[optind]);
1465 #ifdef BB_FEATURE_SH_ENVIRONMENT
1477 /* A shell is interactive if the `-i' flag was given, or if all of
1478 * the following conditions are met:
1480 * no arguments remaining or the -s flag given
1481 * standard input is a terminal
1482 * standard output is a terminal
1483 * Refer to Posix.2, the description of the `sh' utility. */
1484 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1485 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1486 /* Looks like they want an interactive shell */
1487 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1488 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1489 } else if (local_pending_command==NULL) {
1490 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1491 input = xfopen(argv[optind], "r");
1494 /* initialize the cwd -- this is never freed...*/
1495 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1496 getcwd(cwd, sizeof(char)*MAX_LINE);
1498 #ifdef BB_FEATURE_CLEAN_UP
1499 atexit(free_memory);
1502 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1506 return (busy_loop(input));