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"
55 extern size_t NUM_APPLETS;
58 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
62 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
63 static const unsigned int IF_TRUE_CONTEXT=0x2;
64 static const unsigned int IF_FALSE_CONTEXT=0x4;
65 static const unsigned int THEN_EXP_CONTEXT=0x8;
66 static const unsigned int ELSE_EXP_CONTEXT=0x10;
70 struct job *head; /* head of list of running jobs */
71 struct job *fg; /* current foreground job */
74 struct redirectionSpecifier {
75 enum redirectionType type; /* type of redirection */
76 int fd; /* file descriptor being redirected */
77 char *filename; /* file to redirect fd to */
81 pid_t pid; /* 0 if exited */
82 char **argv; /* program name and arguments */
83 int numRedirections; /* elements in redirection array */
84 struct redirectionSpecifier *redirections; /* I/O redirections */
85 glob_t globResult; /* result of parameter globbing */
86 int freeGlob; /* should we globfree(&globResult)? */
87 int isStopped; /* is the program currently running? */
91 int jobId; /* job number */
92 int numProgs; /* total number of programs in job */
93 int runningProgs; /* number of programs running */
94 char *text; /* name of job */
95 char *cmdBuf; /* buffer various argv's point into */
96 pid_t pgrp; /* process group ID for the job */
97 struct childProgram *progs; /* array of programs in job */
98 struct job *next; /* to track background commands */
99 int stoppedProgs; /* number of programs alive, but stopped */
100 int jobContext; /* bitmask defining current context */
103 struct builtInCommand {
104 char *cmd; /* name */
105 char *descr; /* description */
106 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
109 /* function prototypes for builtins */
110 static int builtin_cd(struct job *cmd, struct jobSet *junk);
111 static int builtin_env(struct job *dummy, struct jobSet *junk);
112 static int builtin_exec(struct job *cmd, struct jobSet *junk);
113 static int builtin_exit(struct job *cmd, struct jobSet *junk);
114 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
115 static int builtin_help(struct job *cmd, struct jobSet *junk);
116 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
117 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
118 static int builtin_export(struct job *cmd, struct jobSet *junk);
119 static int builtin_source(struct job *cmd, struct jobSet *jobList);
120 static int builtin_unset(struct job *cmd, struct jobSet *junk);
121 static int builtin_read(struct job *cmd, struct jobSet *junk);
122 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
123 static int builtin_if(struct job *cmd, struct jobSet *junk);
124 static int builtin_then(struct job *cmd, struct jobSet *junk);
125 static int builtin_else(struct job *cmd, struct jobSet *junk);
126 static int builtin_fi(struct job *cmd, struct jobSet *junk);
130 /* function prototypes for shell stuff */
131 static void checkJobs(struct jobSet *jobList);
132 static int getCommand(FILE * source, char *command);
133 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
134 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
135 static int busy_loop(FILE * input);
138 /* Table of built-in functions (these are non-forking builtins, meaning they
139 * can change global variables in the parent shell process but they will not
140 * work with pipes and redirects; 'unset foo | whatever' will not work) */
141 static struct builtInCommand bltins[] = {
142 {"bg", "Resume a job in the background", builtin_fg_bg},
143 {"cd", "Change working directory", builtin_cd},
144 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
145 {"exit", "Exit from shell()", builtin_exit},
146 {"fg", "Bring job into the foreground", builtin_fg_bg},
147 {"jobs", "Lists the active jobs", builtin_jobs},
148 {"export", "Set environment variable", builtin_export},
149 {"unset", "Unset environment variable", builtin_unset},
150 {"read", "Input environment variable", builtin_read},
151 {".", "Source-in and run commands in a file", builtin_source},
152 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
153 {"if", NULL, builtin_if},
154 {"then", NULL, builtin_then},
155 {"else", NULL, builtin_else},
156 {"fi", NULL, builtin_fi},
161 /* Table of forking built-in functions (things that fork cannot change global
162 * variables in the parent process, such as the current working directory) */
163 static struct builtInCommand bltins_forking[] = {
164 {"env", "Print all environment variables", builtin_env},
165 {"pwd", "Print current directory", builtin_pwd},
166 {"help", "List shell built-in commands", builtin_help},
170 static char *prompt = "# ";
172 static char *local_pending_command = NULL;
173 static char *promptStr = NULL;
174 static struct jobSet jobList = { NULL, NULL };
177 #ifdef BB_FEATURE_SH_ENVIRONMENT
178 static int lastBgPid=-1;
179 static int lastReturnCode=-1;
180 static int showXtrace=FALSE;
184 #ifdef BB_FEATURE_SH_COMMAND_EDITING
185 void win_changed(int junk)
187 struct winsize win = { 0, 0, 0, 0 };
188 ioctl(0, TIOCGWINSZ, &win);
189 if (win.ws_col > 0) {
190 cmdedit_setwidth( win.ws_col - 1);
196 /* built-in 'cd <path>' handler */
197 static int builtin_cd(struct job *cmd, struct jobSet *junk)
201 if (!cmd->progs[0].argv[1] == 1)
202 newdir = getenv("HOME");
204 newdir = cmd->progs[0].argv[1];
206 printf("cd: %s: %s\n", newdir, strerror(errno));
209 getcwd(cwd, sizeof(char)*MAX_LINE);
214 /* built-in 'env' handler */
215 static int builtin_env(struct job *dummy, struct jobSet *junk)
219 for (e = environ; *e; e++) {
220 fprintf(stdout, "%s\n", *e);
225 /* built-in 'exec' handler */
226 static int builtin_exec(struct job *cmd, struct jobSet *junk)
228 if (cmd->progs[0].argv[1])
230 cmd->progs[0].argv++;
231 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
232 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
238 /* built-in 'exit' handler */
239 static int builtin_exit(struct job *cmd, struct jobSet *junk)
241 if (!cmd->progs[0].argv[1] == 1)
244 exit (atoi(cmd->progs[0].argv[1]));
247 /* built-in 'fg' and 'bg' handler */
248 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
251 struct job *job=NULL;
253 if (!jobList->head) {
254 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
255 errorMsg("%s: exactly one argument is expected\n",
256 cmd->progs[0].argv[0]);
259 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
260 errorMsg("%s: bad argument '%s'\n",
261 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
263 for (job = jobList->head; job; job = job->next) {
264 if (job->jobId == jobNum) {
274 errorMsg("%s: unknown job %d\n",
275 cmd->progs[0].argv[0], jobNum);
279 if (*cmd->progs[0].argv[0] == 'f') {
280 /* Make this job the foreground job */
281 /* suppress messages when run from /linuxrc mag@sysgo.de */
282 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
287 /* Restart the processes in the job */
288 for (i = 0; i < job->numProgs; i++)
289 job->progs[i].isStopped = 0;
291 kill(-job->pgrp, SIGCONT);
293 job->stoppedProgs = 0;
298 /* built-in 'help' handler */
299 static int builtin_help(struct job *dummy, struct jobSet *junk)
301 struct builtInCommand *x;
303 fprintf(stdout, "\nBuilt-in commands:\n");
304 fprintf(stdout, "-------------------\n");
305 for (x = bltins; x->cmd; x++) {
308 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
310 for (x = bltins_forking; x->cmd; x++) {
313 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
315 fprintf(stdout, "\n\n");
319 /* built-in 'jobs' handler */
320 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
325 for (job = jobList->head; job; job = job->next) {
326 if (job->runningProgs == job->stoppedProgs)
327 statusString = "Stopped";
329 statusString = "Running";
331 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
337 /* built-in 'pwd' handler */
338 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
340 getcwd(cwd, sizeof(char)*MAX_LINE);
341 fprintf(stdout, "%s\n", cwd);
345 /* built-in 'export VAR=value' handler */
346 static int builtin_export(struct job *cmd, struct jobSet *junk)
350 if (!cmd->progs[0].argv[1] == 1) {
351 return (builtin_env(cmd, junk));
353 res = putenv(cmd->progs[0].argv[1]);
355 fprintf(stdout, "export: %s\n", strerror(errno));
359 /* built-in 'read VAR' handler */
360 static int builtin_read(struct job *cmd, struct jobSet *junk)
362 int res = 0, len, newlen;
364 char string[MAX_READ];
366 if (cmd->progs[0].argv[1]) {
367 /* argument (VAR) given: put "VAR=" into buffer */
368 strcpy(string, cmd->progs[0].argv[1]);
369 len = strlen(string);
372 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
373 newlen = strlen(string);
375 string[--newlen] = '\0'; /* chomp trailing newline */
377 ** string should now contain "VAR=<value>"
378 ** copy it (putenv() won't do that, so we must make sure
379 ** the string resides in a static buffer!)
382 if((s = strdup(string)))
385 fprintf(stdout, "read: %s\n", strerror(errno));
388 fgets(string, sizeof(string), stdin);
393 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
394 /* Built-in handler for 'if' commands */
395 static int builtin_if(struct job *cmd, struct jobSet *jobList)
398 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
400 /* Now run the 'if' command */
401 status=strlen(charptr1);
402 local_pending_command = xmalloc(status+1);
403 strncpy(local_pending_command, charptr1, status);
404 local_pending_command[status]='\0';
406 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
408 status = busy_loop(NULL); /* Frees local_pending_command */
410 fprintf(stderr, "if test returned ");
414 fprintf(stderr, "TRUE\n");
416 cmd->jobContext |= IF_TRUE_CONTEXT;
419 fprintf(stderr, "FALSE\n");
421 cmd->jobContext |= IF_FALSE_CONTEXT;
427 /* Built-in handler for 'then' (part of the 'if' command) */
428 static int builtin_then(struct job *cmd, struct jobSet *junk)
431 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
433 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
434 errorMsg("unexpected token `then'\n");
437 /* If the if result was FALSE, skip the 'then' stuff */
438 if (cmd->jobContext & IF_FALSE_CONTEXT) {
442 cmd->jobContext |= THEN_EXP_CONTEXT;
443 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
445 /* Now run the 'then' command */
446 status=strlen(charptr1);
447 local_pending_command = xmalloc(status+1);
448 strncpy(local_pending_command, charptr1, status);
449 local_pending_command[status]='\0';
451 fprintf(stderr, "'then' now running '%s'\n", charptr1);
453 return( busy_loop(NULL));
456 /* Built-in handler for 'else' (part of the 'if' command) */
457 static int builtin_else(struct job *cmd, struct jobSet *junk)
460 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
462 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
463 errorMsg("unexpected token `else'\n");
466 /* If the if result was TRUE, skip the 'else' stuff */
467 if (cmd->jobContext & IF_TRUE_CONTEXT) {
471 cmd->jobContext |= ELSE_EXP_CONTEXT;
472 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
474 /* Now run the 'else' command */
475 status=strlen(charptr1);
476 local_pending_command = xmalloc(status+1);
477 strncpy(local_pending_command, charptr1, status);
478 local_pending_command[status]='\0';
480 fprintf(stderr, "'else' now running '%s'\n", charptr1);
482 return( busy_loop(NULL));
485 /* Built-in handler for 'fi' (part of the 'if' command) */
486 static int builtin_fi(struct job *cmd, struct jobSet *junk)
488 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
489 errorMsg("unexpected token `fi'\n");
492 /* Clear out the if and then context bits */
493 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
495 fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
501 /* Built-in '.' handler (read-in and execute commands from file) */
502 static int builtin_source(struct job *cmd, struct jobSet *junk)
507 if (!cmd->progs[0].argv[1] == 1)
510 input = fopen(cmd->progs[0].argv[1], "r");
512 fprintf(stdout, "Couldn't open file '%s'\n",
513 cmd->progs[0].argv[1]);
517 /* Now run the file */
518 status = busy_loop(input);
523 /* built-in 'unset VAR' handler */
524 static int builtin_unset(struct job *cmd, struct jobSet *junk)
526 if (!cmd->progs[0].argv[1] == 1) {
527 fprintf(stdout, "unset: parameter required.\n");
530 unsetenv(cmd->progs[0].argv[1]);
534 /* free up all memory from a job */
535 static void freeJob(struct job *cmd)
539 for (i = 0; i < cmd->numProgs; i++) {
540 free(cmd->progs[i].argv);
541 if (cmd->progs[i].redirections)
542 free(cmd->progs[i].redirections);
543 if (cmd->progs[i].freeGlob)
544 globfree(&cmd->progs[i].globResult);
550 memset(cmd, 0, sizeof(struct job));
553 /* remove a job from the jobList */
554 static void removeJob(struct jobSet *jobList, struct job *job)
559 if (job == jobList->head) {
560 jobList->head = job->next;
562 prevJob = jobList->head;
563 while (prevJob->next != job)
564 prevJob = prevJob->next;
565 prevJob->next = job->next;
571 /* Checks to see if any background processes have exited -- if they
572 have, figure out why and see if a job has completed */
573 static void checkJobs(struct jobSet *jobList)
580 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
581 for (job = jobList->head; job; job = job->next) {
583 while (progNum < job->numProgs &&
584 job->progs[progNum].pid != childpid) progNum++;
585 if (progNum < job->numProgs)
589 /* This happens on backticked commands */
593 if (WIFEXITED(status) || WIFSIGNALED(status)) {
596 job->progs[progNum].pid = 0;
598 if (!job->runningProgs) {
599 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
600 removeJob(jobList, job);
605 job->progs[progNum].isStopped = 1;
607 if (job->stoppedProgs == job->numProgs) {
608 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
614 if (childpid == -1 && errno != ECHILD)
618 static int setupRedirections(struct childProgram *prog)
623 struct redirectionSpecifier *redir = prog->redirections;
625 for (i = 0; i < prog->numRedirections; i++, redir++) {
626 switch (redir->type) {
630 case REDIRECT_OVERWRITE:
631 mode = O_WRONLY | O_CREAT | O_TRUNC;
633 case REDIRECT_APPEND:
634 mode = O_WRONLY | O_CREAT | O_APPEND;
638 openfd = open(redir->filename, mode, 0666);
640 /* this could get lost if stderr has been redirected, but
641 bash and ash both lose it as well (though zsh doesn't!) */
642 errorMsg("error opening %s: %s\n", redir->filename,
647 if (openfd != redir->fd) {
648 dup2(openfd, redir->fd);
657 static int getCommand(FILE * source, char *command)
659 if (source == NULL) {
660 if (local_pending_command) {
661 /* a command specified (-c option): return it & mark it done */
662 strcpy(command, local_pending_command);
663 free(local_pending_command);
664 local_pending_command = NULL;
670 if (source == stdin) {
671 #ifdef BB_FEATURE_SH_COMMAND_EDITING
675 ** enable command line editing only while a command line
676 ** is actually being read; otherwise, we'll end up bequeathing
677 ** atexit() handlers and other unwanted stuff to our
678 ** child processes (rob@sysgo.de)
681 signal(SIGWINCH, win_changed);
682 len=fprintf(stdout, "%s %s", cwd, prompt);
684 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
685 sprintf(promptStr, "%s %s", cwd, prompt);
686 cmdedit_read_input(promptStr, command);
689 signal(SIGWINCH, SIG_DFL);
692 fprintf(stdout, "%s %s", cwd, prompt);
697 if (!fgets(command, BUFSIZ - 2, source)) {
703 /* remove trailing newline */
704 command[strlen(command) - 1] = '\0';
709 #ifdef BB_FEATURE_SH_ENVIRONMENT
710 #define __MAX_INT_CHARS 7
711 static char* itoa(register int i)
713 static char a[__MAX_INT_CHARS];
714 register char *b = a + sizeof(a) - 1;
722 *--b = '0' + (i % 10);
732 static void globLastArgument(struct childProgram *prog, int *argcPtr,
735 int argc_l = *argcPtr;
736 int argcAlloced = *argcAllocedPtr;
740 char *src, *dst, *var;
742 if (argc_l > 1) { /* cmd->globResult is already initialized */
744 i = prog->globResult.gl_pathc;
750 /* do shell variable substitution */
751 if(*prog->argv[argc_l - 1] == '$') {
752 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
753 prog->argv[argc_l - 1] = var;
755 #ifdef BB_FEATURE_SH_ENVIRONMENT
757 switch(*(prog->argv[argc_l - 1] + 1)) {
759 prog->argv[argc_l - 1] = itoa(lastReturnCode);
762 prog->argv[argc_l - 1] = itoa(getpid());
765 prog->argv[argc_l - 1] = itoa(argc-1);
769 *(prog->argv[argc_l - 1])='\0';
771 prog->argv[argc_l - 1] = itoa(lastBgPid);
773 case '0':case '1':case '2':case '3':case '4':
774 case '5':case '6':case '7':case '8':case '9':
776 int index=*(prog->argv[argc_l - 1] + 1)-48;
778 *(prog->argv[argc_l - 1])='\0';
780 prog->argv[argc_l - 1] = argv[index];
789 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
790 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
791 if (rc == GLOB_NOSPACE) {
792 errorMsg("out of space during glob operation\n");
794 } else if (rc == GLOB_NOMATCH ||
795 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
796 strcmp(prog->argv[argc_l - 1],
797 prog->globResult.gl_pathv[i]) == 0)) {
798 /* we need to remove whatever \ quoting is still present */
799 src = dst = prog->argv[argc_l - 1];
803 *dst++ = process_escape_sequence(&src);
811 argcAlloced += (prog->globResult.gl_pathc - i);
812 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
813 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
814 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
815 argc_l += (prog->globResult.gl_pathc - i - 1);
818 src = dst = prog->argv[argc_l - 1];
822 *dst++ = process_escape_sequence(&src);
830 prog->globResult.gl_pathc=0;
832 prog->globResult.gl_pathv=NULL;
834 *argcAllocedPtr = argcAlloced;
838 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
839 line). If a valid command is found, commandPtr is set to point to
840 the beginning of the next command (if the original command had more
841 then one job associated with it) or NULL if no more commands are
843 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
846 char *returnCommand = NULL;
847 char *src, *buf, *chptr;
854 struct childProgram *prog;
856 /* skip leading white space */
857 while (**commandPtr && isspace(**commandPtr))
860 /* this handles empty lines or leading '#' characters */
861 if (!**commandPtr || (**commandPtr == '#')) {
868 job->progs = xmalloc(sizeof(*job->progs));
870 /* We set the argv elements to point inside of this string. The
871 memory is freed by freeJob(). Allocate twice the original
872 length in case we need to quote every single character.
874 Getting clean memory relieves us of the task of NULL
875 terminating things and makes the rest of this look a bit
876 cleaner (though it is, admittedly, a tad less efficient) */
877 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
881 prog->numRedirections = 0;
882 prog->redirections = NULL;
887 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
888 prog->argv[0] = job->cmdBuf;
892 while (*src && !done) {
899 errorMsg("character expected after \\\n");
904 /* in shell, "\'" should yield \' */
907 } else if (*src == '*' || *src == '?' || *src == '[' ||
908 *src == ']') *buf++ = '\\';
910 } else if (isspace(*src)) {
911 if (*prog->argv[argc_l]) {
913 /* +1 here leaves room for the NULL which ends argv */
914 if ((argc_l + 1) == argvAlloced) {
916 prog->argv = xrealloc(prog->argv,
917 sizeof(*prog->argv) *
920 globLastArgument(prog, &argc_l, &argvAlloced);
921 prog->argv[argc_l] = buf;
930 case '#': /* comment */
937 case '>': /* redirections */
939 i = prog->numRedirections++;
940 prog->redirections = xrealloc(prog->redirections,
941 sizeof(*prog->redirections) *
944 prog->redirections[i].fd = -1;
945 if (buf != prog->argv[argc_l]) {
946 /* the stuff before this character may be the file number
948 prog->redirections[i].fd =
949 strtol(prog->argv[argc_l], &chptr, 10);
951 if (*chptr && *prog->argv[argc_l]) {
953 globLastArgument(prog, &argc_l, &argvAlloced);
954 prog->argv[argc_l] = buf;
958 if (prog->redirections[i].fd == -1) {
960 prog->redirections[i].fd = 1;
962 prog->redirections[i].fd = 0;
967 prog->redirections[i].type =
968 REDIRECT_APPEND, src++;
970 prog->redirections[i].type = REDIRECT_OVERWRITE;
972 prog->redirections[i].type = REDIRECT_INPUT;
975 /* This isn't POSIX sh compliant. Oh well. */
977 while (isspace(*chptr))
981 errorMsg("file name expected after %c\n", *src);
987 prog->redirections[i].filename = buf;
988 while (*chptr && !isspace(*chptr))
991 src = chptr - 1; /* we src++ later */
992 prog->argv[argc_l] = ++buf;
996 /* finish this command */
997 if (*prog->argv[argc_l])
1000 errorMsg("empty command in pipe\n");
1005 prog->argv[argc_l] = NULL;
1007 /* and start the next */
1009 job->progs = xrealloc(job->progs,
1010 sizeof(*job->progs) * job->numProgs);
1011 prog = job->progs + (job->numProgs - 1);
1012 prog->numRedirections = 0;
1013 prog->redirections = NULL;
1015 prog->isStopped = 0;
1019 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1020 prog->argv[0] = ++buf;
1023 while (*src && isspace(*src))
1027 errorMsg("empty command in pipe\n");
1032 src--; /* we'll ++ it at the end of the loop */
1036 case '&': /* background */
1038 case ';': /* multiple commands */
1040 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1043 #ifdef BB_FEATURE_SH_BACKTICKS
1045 /* Exec a backtick-ed command */
1047 char* charptr1=NULL, *charptr2;
1050 struct jobSet njobList = { NULL, NULL };
1054 ptr=strchr(++src, '`');
1056 fprintf(stderr, "Unmatched '`' in command\n");
1061 /* Make some space to hold just the backticked command */
1062 charptr1 = charptr2 = xmalloc(1+ptr-src);
1063 memcpy(charptr1, src, ptr-src);
1064 charptr1[ptr-src] = '\0';
1065 newJob = xmalloc(sizeof(struct job));
1066 /* Now parse and run the backticked command */
1067 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1068 && newJob->numProgs) {
1070 runCommand(newJob, &njobList, 0, pipefd);
1076 /* Make a copy of any stuff left over in the command
1077 * line after the second backtick */
1078 charptr2 = xmalloc(strlen(ptr)+1);
1079 memcpy(charptr2, ptr+1, strlen(ptr));
1082 /* Copy the output from the backtick-ed command into the
1083 * command line, making extra room as needed */
1085 charptr1 = xmalloc(BUFSIZ);
1086 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1087 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1088 if (newSize > BUFSIZ) {
1089 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1090 size + 1 + strlen(charptr2));
1092 memcpy(src, charptr1, size);
1100 /* Now paste into the *commandPtr all the stuff
1101 * leftover after the second backtick */
1102 memcpy(src, charptr2, strlen(charptr2)+1);
1105 /* Now recursively call parseCommand to deal with the new
1106 * and improved version of the command line with the backtick
1107 * results expanded in place... */
1109 return(parseCommand(commandPtr, job, jobList, inBg));
1112 #endif // BB_FEATURE_SH_BACKTICKS
1117 errorMsg("character expected after \\\n");
1121 if (*src == '*' || *src == '[' || *src == ']'
1122 || *src == '?') *buf++ = '\\';
1131 if (*prog->argv[argc_l]) {
1133 globLastArgument(prog, &argc_l, &argvAlloced);
1139 prog->argv[argc_l] = NULL;
1141 if (!returnCommand) {
1142 job->text = xmalloc(strlen(*commandPtr) + 1);
1143 strcpy(job->text, *commandPtr);
1145 /* This leaves any trailing spaces, which is a bit sloppy */
1146 count = returnCommand - *commandPtr;
1147 job->text = xmalloc(count + 1);
1148 strncpy(job->text, *commandPtr, count);
1149 job->text[count] = '\0';
1152 *commandPtr = returnCommand;
1157 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1161 int nextin, nextout;
1162 int pipefds[2]; /* pipefd[0] is for reading */
1163 struct builtInCommand *x;
1164 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1165 struct BB_applet search_applet, *applet;
1168 nextin = 0, nextout = 1;
1169 for (i = 0; i < newJob->numProgs; i++) {
1170 if ((i + 1) < newJob->numProgs) {
1172 nextout = pipefds[1];
1174 if (outPipe[1]!=-1) {
1175 nextout = outPipe[1];
1181 #ifdef BB_FEATURE_SH_ENVIRONMENT
1182 if (showXtrace==TRUE) {
1184 fprintf(stderr, "+ ");
1185 for (j = 0; newJob->progs[i].argv[j]; j++)
1186 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1187 fprintf(stderr, "\n");
1191 /* Check if the command matches any non-forking builtins */
1192 for (x = bltins; x->cmd; x++) {
1193 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1194 return(x->function(newJob, jobList));
1198 if (!(newJob->progs[i].pid = fork())) {
1199 signal(SIGTTOU, SIG_DFL);
1201 if (outPipe[1]!=-1) {
1216 /* explicit redirections override pipes */
1217 setupRedirections(newJob->progs + i);
1219 /* Check if the command matches any of the other builtins */
1220 for (x = bltins_forking; x->cmd; x++) {
1221 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1223 exit (x->function(newJob, jobList));
1226 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1227 /* Check if the command matches any busybox internal
1228 * commands ("applets") here. Following discussions from
1229 * November 2000 on busybox@opensource.lineo.com, don't use
1230 * get_last_path_component(). This way explicit (with
1231 * slashes) filenames will never be interpreted as an
1232 * applet, just like with builtins. This way the user can
1233 * override an applet with an explicit filename reference.
1234 * The only downside to this change is that an explicit
1235 * /bin/foo invocation fill fork and exec /bin/foo, even if
1236 * /bin/foo is a symlink to busybox.
1238 search_applet.name = newJob->progs[i].argv[0];
1240 #ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
1241 /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
1242 * if you run /bin/cat, it will use BusyBox cat even if
1243 * /bin/cat exists on the filesystem and is _not_ busybox.
1244 * Some systems want this, others do not. Choose wisely. :-)
1246 search_applet.name = get_last_path_component(search_applet.name);
1249 /* Do a binary search to find the applet entry given the name. */
1250 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1251 sizeof(struct BB_applet), applet_name_compare);
1252 if (applet != NULL) {
1254 char** argv=newJob->progs[i].argv;
1255 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1256 applet_name=applet->name;
1258 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1262 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1263 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1266 if (outPipe[1]!=-1) {
1270 /* put our child in the process group whose leader is the
1271 first process in this pipe */
1272 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1278 /* If there isn't another process, nextin is garbage
1279 but it doesn't matter */
1280 nextin = pipefds[0];
1283 newJob->pgrp = newJob->progs[0].pid;
1285 /* find the ID for the theJob to use */
1287 for (theJob = jobList->head; theJob; theJob = theJob->next)
1288 if (theJob->jobId >= newJob->jobId)
1289 newJob->jobId = theJob->jobId + 1;
1291 /* add the theJob to the list of running jobs */
1292 if (!jobList->head) {
1293 theJob = jobList->head = xmalloc(sizeof(*theJob));
1295 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1296 theJob->next = xmalloc(sizeof(*theJob));
1297 theJob = theJob->next;
1301 theJob->next = NULL;
1302 theJob->runningProgs = theJob->numProgs;
1303 theJob->stoppedProgs = 0;
1306 /* we don't wait for background theJobs to return -- append it
1307 to the list of backgrounded theJobs and leave it alone */
1308 printf("[%d] %d\n", theJob->jobId,
1309 newJob->progs[newJob->numProgs - 1].pid);
1310 #ifdef BB_FEATURE_SH_ENVIRONMENT
1311 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1314 jobList->fg = theJob;
1316 /* move the new process group into the foreground */
1317 /* suppress messages when run from /linuxrc mag@sysgo.de */
1318 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1319 perror("tcsetpgrp");
1325 static int busy_loop(FILE * input)
1328 char *nextCommand = NULL;
1334 newJob.jobContext = REGULAR_JOB_CONTEXT;
1336 /* save current owner of TTY so we can restore it on exit */
1337 parent_pgrp = tcgetpgrp(0);
1339 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1341 /* don't pay any attention to this signal; it just confuses
1342 things and isn't really meant for shells anyway */
1343 signal(SIGTTOU, SIG_IGN);
1347 /* no job is in the foreground */
1349 /* see if any background processes have exited */
1350 checkJobs(&jobList);
1353 if (getCommand(input, command))
1355 nextCommand = command;
1358 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1360 int pipefds[2] = {-1,-1};
1361 runCommand(&newJob, &jobList, inBg, pipefds);
1365 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1369 /* a job is running in the foreground; wait for it */
1371 while (!jobList.fg->progs[i].pid ||
1372 jobList.fg->progs[i].isStopped == 1) i++;
1374 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1376 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1377 /* the child exited */
1378 jobList.fg->runningProgs--;
1379 jobList.fg->progs[i].pid = 0;
1381 #ifdef BB_FEATURE_SH_ENVIRONMENT
1382 lastReturnCode=WEXITSTATUS(status);
1385 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1387 if (!jobList.fg->runningProgs) {
1390 removeJob(&jobList, jobList.fg);
1394 /* the child was stopped */
1395 jobList.fg->stoppedProgs++;
1396 jobList.fg->progs[i].isStopped = 1;
1398 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1399 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1400 "Stopped", jobList.fg->text);
1406 /* move the shell to the foreground */
1407 /* suppress messages when run from /linuxrc mag@sysgo.de */
1408 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1409 perror("tcsetpgrp");
1415 /* return controlling TTY back to parent process group before exiting */
1416 if (tcsetpgrp(0, parent_pgrp))
1417 perror("tcsetpgrp");
1419 /* return exit status if called with "-c" */
1420 if (input == NULL && WIFEXITED(status))
1421 return WEXITSTATUS(status);
1427 #ifdef BB_FEATURE_CLEAN_UP
1428 void free_memory(void)
1434 if (local_pending_command)
1435 free(local_pending_command);
1437 if (jobList.fg && !jobList.fg->runningProgs) {
1438 removeJob(&jobList, jobList.fg);
1444 int shell_main(int argc_l, char **argv_l)
1446 int opt, interactive=FALSE;
1447 FILE *input = stdin;
1452 //if (argv[0] && argv[0][0] == '-') {
1453 // builtin_source("/etc/profile");
1456 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1460 if (local_pending_command != 0)
1461 fatalError("multiple -c arguments\n");
1462 local_pending_command = xstrdup(argv[optind]);
1466 #ifdef BB_FEATURE_SH_ENVIRONMENT
1478 /* A shell is interactive if the `-i' flag was given, or if all of
1479 * the following conditions are met:
1481 * no arguments remaining or the -s flag given
1482 * standard input is a terminal
1483 * standard output is a terminal
1484 * Refer to Posix.2, the description of the `sh' utility. */
1485 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1486 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1487 /* Looks like they want an interactive shell */
1488 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1489 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1490 } else if (local_pending_command==NULL) {
1491 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1492 input = xfopen(argv[optind], "r");
1495 /* initialize the cwd -- this is never freed...*/
1496 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1497 getcwd(cwd, sizeof(char)*MAX_LINE);
1499 #ifdef BB_FEATURE_CLEAN_UP
1500 atexit(free_memory);
1503 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1507 return (busy_loop(input));