From 501c88b245fdc63f3f2a044fd7704bb468db3904 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Fri, 28 Jul 2000 15:14:45 +0000 Subject: [PATCH] More sh updates (with related changes to everything else). Switched to using getopt and cleaned up the resulting mess. if-then-else-fi is now basically working (given a bunch of constraints). -Erik --- applets/busybox.c | 6 +- busybox.c | 6 +- cmdedit.c | 25 ++++++- cmdedit.h | 1 + internal.h | 3 +- lash.c | 179 ++++++++++++++++++++++++++++++---------------- sh.c | 179 ++++++++++++++++++++++++++++++---------------- shell/cmdedit.c | 25 ++++++- shell/cmdedit.h | 1 + shell/lash.c | 179 ++++++++++++++++++++++++++++++---------------- utility.c | 10 ++- 11 files changed, 421 insertions(+), 193 deletions(-) diff --git a/applets/busybox.c b/applets/busybox.c index 291d31b19..1ed44ed9b 100644 --- a/applets/busybox.c +++ b/applets/busybox.c @@ -337,7 +337,7 @@ const struct BB_applet applets[] = { {0,NULL,0,NULL} }; -char *applet_name; +const char *applet_name; #ifdef BB_FEATURE_INSTALLER /* @@ -416,7 +416,7 @@ static int install_links(const char *busybox, int use_symbolic_links) int main(int argc, char **argv) { - char *s; + const char *s; const struct BB_applet *a = applets; applet_name = "busybox"; @@ -455,7 +455,7 @@ int main(int argc, char **argv) applet_name = s; } - *argv = applet_name; + *argv = (char*)applet_name; #ifdef BB_SH /* Add in a special case hack -- whenever **argv == '-' diff --git a/busybox.c b/busybox.c index 291d31b19..1ed44ed9b 100644 --- a/busybox.c +++ b/busybox.c @@ -337,7 +337,7 @@ const struct BB_applet applets[] = { {0,NULL,0,NULL} }; -char *applet_name; +const char *applet_name; #ifdef BB_FEATURE_INSTALLER /* @@ -416,7 +416,7 @@ static int install_links(const char *busybox, int use_symbolic_links) int main(int argc, char **argv) { - char *s; + const char *s; const struct BB_applet *a = applets; applet_name = "busybox"; @@ -455,7 +455,7 @@ int main(int argc, char **argv) applet_name = s; } - *argv = applet_name; + *argv = (char*)applet_name; #ifdef BB_SH /* Add in a special case hack -- whenever **argv == '-' diff --git a/cmdedit.c b/cmdedit.c index 0ce64beeb..042064f1e 100644 --- a/cmdedit.c +++ b/cmdedit.c @@ -84,6 +84,7 @@ static int cmdedit_termw = 80; /* actual terminal width */ static int cmdedit_scroll = 27; /* width of EOL scrolling region */ static int history_counter = 0; /* Number of commands in history list */ static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ +static int exithandler_set = 0; /* Set to true when atexit() has been called */ struct history { char *s; @@ -709,10 +710,32 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) extern void cmdedit_init(void) { - atexit(cmdedit_reset_term); + if(exithandler_set == 0) { + atexit(cmdedit_reset_term); /* be sure to do this only once */ + exithandler_set = 1; + } signal(SIGKILL, clean_up_and_die); signal(SIGINT, clean_up_and_die); signal(SIGQUIT, clean_up_and_die); signal(SIGTERM, clean_up_and_die); } + +/* +** Undo the effects of cmdedit_init() as good as we can: +** I am not aware of a way to revoke an atexit() handler, +** but, fortunately, our particular handler can be made +** a no-op by setting reset_term = 0. +*/ +extern void cmdedit_terminate(void) +{ + cmdedit_reset_term(); + reset_term = 0; + signal(SIGKILL, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +} + + + #endif /* BB_FEATURE_SH_COMMAND_EDITING */ diff --git a/cmdedit.h b/cmdedit.h index b621ae8ec..27f7b5500 100644 --- a/cmdedit.h +++ b/cmdedit.h @@ -11,6 +11,7 @@ typedef size_t (*cmdedit_strwidth_proc)(char *); void cmdedit_init(void); +void cmdedit_terminate(void); void cmdedit_read_input(char* promptStr, char* command); /* read a line of input */ void cmdedit_setwidth(int); /* specify width of screen */ void cmdedit_histadd(char *); /* adds entries to hist */ diff --git a/internal.h b/internal.h index 7f0b77475..9de1c9372 100644 --- a/internal.h +++ b/internal.h @@ -317,7 +317,7 @@ extern const char which_usage[]; extern const char whoami_usage[]; extern const char yes_usage[]; -extern char *applet_name; +extern const char *applet_name; extern void usage(const char *usage) __attribute__ ((noreturn)); extern void errorMsg(const char *s, ...) __attribute__ ((format (printf, 1, 2))); @@ -380,6 +380,7 @@ extern void xregcomp(regex_t *preg, const char *regex, int cflags); #ifndef DMALLOC extern void *xmalloc (size_t size); extern void *xrealloc(void *old, size_t size); +extern void *xcalloc(size_t nmemb, size_t size); extern char *xstrdup (const char *s); #endif extern char *xstrndup (const char *s, int n); diff --git a/lash.c b/lash.c index e57567608..836fc9bab 100644 --- a/lash.c +++ b/lash.c @@ -29,6 +29,7 @@ //#define BB_FEATURE_SH_BACKTICKS //#define BB_FEATURE_SH_IF_EXPRESSIONS //#define BB_FEATURE_SH_ENVIRONMENT +//#define DEBUG_SHELL #include "internal.h" @@ -43,6 +44,7 @@ #include #include #include +#include #ifdef BB_FEATURE_SH_COMMAND_EDITING #include "cmdedit.h" #endif @@ -172,8 +174,9 @@ static char **argv; #ifdef BB_FEATURE_SH_ENVIRONMENT static int lastBgPid=-1; static int lastReturnCode=-1; +static int showXtrace=FALSE; #endif - + #ifdef BB_FEATURE_SH_COMMAND_EDITING void win_changed(int junk) @@ -382,14 +385,23 @@ static int builtin_if(struct job *cmd, struct jobSet *jobList) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'if' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'if' now testing '%s'\n", local_pending_command); +#endif status = busy_loop(NULL); /* Frees local_pending_command */ - printf("if test returned "); +#ifdef DEBUG_SHELL + fprintf(stderr, "if test returned "); +#endif if (status == 0) { - printf("TRUE\n"); +#ifdef DEBUG_SHELL + fprintf(stderr, "TRUE\n"); +#endif cmd->jobContext |= IF_TRUE_CONTEXT; } else { - printf("FALSE\n"); +#ifdef DEBUG_SHELL + fprintf(stderr, "FALSE\n"); +#endif cmd->jobContext |= IF_FALSE_CONTEXT; } @@ -407,7 +419,7 @@ static int builtin_then(struct job *cmd, struct jobSet *junk) return FALSE; } /* If the if result was FALSE, skip the 'then' stuff */ - if (cmd->jobContext & IF_TRUE_CONTEXT) { + if (cmd->jobContext & IF_FALSE_CONTEXT) { return TRUE; } @@ -418,7 +430,10 @@ static int builtin_then(struct job *cmd, struct jobSet *junk) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'then' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'then' now running '%s'\n", charptr1); +#endif return( busy_loop(NULL)); } @@ -433,7 +448,7 @@ static int builtin_else(struct job *cmd, struct jobSet *junk) return FALSE; } /* If the if result was TRUE, skip the 'else' stuff */ - if (cmd->jobContext & IF_FALSE_CONTEXT) { + if (cmd->jobContext & IF_TRUE_CONTEXT) { return TRUE; } @@ -444,7 +459,10 @@ static int builtin_else(struct job *cmd, struct jobSet *junk) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'else' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'else' now running '%s'\n", charptr1); +#endif return( busy_loop(NULL)); } @@ -457,7 +475,9 @@ static int builtin_fi(struct job *cmd, struct jobSet *junk) } /* Clear out the if and then context bits */ cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT); - printf("Hit an fi -- jobContext=%d\n", cmd->jobContext); +#ifdef DEBUG_SHELL + fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext); +#endif return TRUE; } #endif @@ -633,12 +653,23 @@ static int getCommand(FILE * source, char *command) if (source == stdin) { #ifdef BB_FEATURE_SH_COMMAND_EDITING int len; + + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_init(); + signal(SIGWINCH, win_changed); len=fprintf(stdout, "%s %s", cwd, prompt); fflush(stdout); promptStr=(char*)xmalloc(sizeof(char)*(len+1)); sprintf(promptStr, "%s %s", cwd, prompt); cmdedit_read_input(promptStr, command); free( promptStr); + cmdedit_terminate(); + signal(SIGWINCH, SIG_DFL); return 0; #else fprintf(stdout, "%s %s", cwd, prompt); @@ -944,6 +975,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi prog->numRedirections = 0; prog->redirections = NULL; prog->freeGlob = 0; + prog->isStopped = 0; argc_l = 0; argvAlloced = 5; @@ -1108,10 +1140,20 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int } } +#ifdef BB_FEATURE_SH_ENVIRONMENT + if (showXtrace==TRUE) { + int j; + fprintf(stderr, "+ "); + for (j = 0; newJob->progs[i].argv[j]; j++) + fprintf(stderr, "%s ", newJob->progs[i].argv[j]); + fprintf(stderr, "\n"); + } +#endif + /* Check if the command matches any non-forking builtins */ for (x = bltins; x->cmd; x++) { if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) { - return (x->function(newJob, jobList)); + return(x->function(newJob, jobList)); } } @@ -1130,6 +1172,7 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int dup2(nextout, 1); dup2(nextout, 2); close(nextout); + close(pipefds[0]); } /* explicit redirections override pipes */ @@ -1138,18 +1181,18 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int /* Check if the command matches any of the other builtins */ for (x = bltins_forking; x->cmd; x++) { if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) { + applet_name=x->cmd; exit (x->function(newJob, jobList)); } } #ifdef BB_FEATURE_SH_STANDALONE_SHELL /* Check if the command matches any busybox internal commands here */ - /* TODO: Add matching when paths are appended (i.e. 'cat' currently - * works, but '/bin/cat' doesn't ) */ while (a->name != 0) { - if (strcmp(newJob->progs[i].argv[0], a->name) == 0) { + if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) { int argc_l; char** argv=newJob->progs[i].argv; for(argc_l=0;*argv!=NULL; argv++, argc_l++); + applet_name=a->name; exit((*(a->main)) (argc_l, newJob->progs[i].argv)); } a++; @@ -1275,15 +1318,18 @@ static int busy_loop(FILE * input) jobList.fg->runningProgs--; jobList.fg->progs[i].pid = 0; +#ifdef BB_FEATURE_SH_ENVIRONMENT + lastReturnCode=WEXITSTATUS(status); +#endif +#if 0 + printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode); +#endif if (!jobList.fg->runningProgs) { /* child exited */ removeJob(&jobList, jobList.fg); jobList.fg = NULL; } -#ifdef BB_FEATURE_SH_ENVIRONMENT - lastReturnCode=WEXITSTATUS(status); -#endif } else { /* the child was stopped */ jobList.fg->stoppedProgs++; @@ -1337,10 +1383,65 @@ void free_memory(void) int shell_main(int argc_l, char **argv_l) { + int opt; FILE *input = stdin; argc = argc_l; argv = argv_l; + + //if (argv[0] && argv[0][0] == '-') { + // builtin_source("/etc/profile"); + //} + + while ((opt = getopt(argc, argv, "cx")) > 0) { + switch (opt) { + case 'c': + input = NULL; + local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); + if (local_pending_command == 0) { + fatalError("sh: out of memory\n"); + } + for(; optind= BUFSIZ) { + local_pending_command = realloc(local_pending_command, + strlen(local_pending_command) + strlen(argv[optind])); + if (local_pending_command==NULL) + fatalError("sh: command too long\n"); + } + strcat(local_pending_command, argv[optind]); + if ( (optind + 1) < argc) + strcat(local_pending_command, " "); + } + break; + case 'x': + showXtrace = TRUE; + break; + default: + usage(shell_usage); + } + } + + + if (optind<1 && input == stdin) { + /* Looks like they want an interactive shell */ + fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); + fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); + } else if (1==(argc-optind)) { + input = fopen(argv[optind], "r"); + if (!input) { + fatalError("%s: %s\n", argv[optind], strerror(errno)); + } + } else { + char *oldpath, *newpath; + oldpath = getenv("PATH"); + newpath=(char*)xmalloc(strlen(oldpath)+12); + snprintf(newpath, strlen(oldpath)+9, "PATH=./:%s", oldpath); + putenv(newpath); + execvp(argv[optind], argv+optind); + fatalError("%s: %s\n", argv[optind], strerror(errno)); + } + /* initialize the cwd -- this is never freed...*/ cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1); getcwd(cwd, sizeof(char)*MAX_LINE); @@ -1350,52 +1451,8 @@ int shell_main(int argc_l, char **argv_l) #endif #ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_init(); - signal(SIGWINCH, win_changed); win_changed(0); #endif - //if (argv[0] && argv[0][0] == '-') { - // builtin_source("/etc/profile"); - //} - - - if (argc < 2) { - fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); - fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); - } else { - if (argv[1][0]=='-' && argv[1][1]=='c') { - int i; - local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); - if (local_pending_command == 0) { - fatalError("out of memory\n"); - } - for(i=2; i= BUFSIZ) { - local_pending_command = realloc(local_pending_command, - strlen(local_pending_command) + strlen(argv[i])); - if (local_pending_command==NULL) - fatalError("commands for -c option too long\n"); - } - strcat(local_pending_command, argv[i]); - if ( (i + 1) < argc) - strcat(local_pending_command, " "); - } - input = NULL; - - } - else if (argv[1][0]=='-') { - usage(shell_usage); - } - else { - input = fopen(argv[1], "r"); - if (!input) { - fatalError("Couldn't open file '%s': %s\n", argv[1], - strerror(errno)); - } - } - } - return (busy_loop(input)); } diff --git a/sh.c b/sh.c index e57567608..836fc9bab 100644 --- a/sh.c +++ b/sh.c @@ -29,6 +29,7 @@ //#define BB_FEATURE_SH_BACKTICKS //#define BB_FEATURE_SH_IF_EXPRESSIONS //#define BB_FEATURE_SH_ENVIRONMENT +//#define DEBUG_SHELL #include "internal.h" @@ -43,6 +44,7 @@ #include #include #include +#include #ifdef BB_FEATURE_SH_COMMAND_EDITING #include "cmdedit.h" #endif @@ -172,8 +174,9 @@ static char **argv; #ifdef BB_FEATURE_SH_ENVIRONMENT static int lastBgPid=-1; static int lastReturnCode=-1; +static int showXtrace=FALSE; #endif - + #ifdef BB_FEATURE_SH_COMMAND_EDITING void win_changed(int junk) @@ -382,14 +385,23 @@ static int builtin_if(struct job *cmd, struct jobSet *jobList) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'if' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'if' now testing '%s'\n", local_pending_command); +#endif status = busy_loop(NULL); /* Frees local_pending_command */ - printf("if test returned "); +#ifdef DEBUG_SHELL + fprintf(stderr, "if test returned "); +#endif if (status == 0) { - printf("TRUE\n"); +#ifdef DEBUG_SHELL + fprintf(stderr, "TRUE\n"); +#endif cmd->jobContext |= IF_TRUE_CONTEXT; } else { - printf("FALSE\n"); +#ifdef DEBUG_SHELL + fprintf(stderr, "FALSE\n"); +#endif cmd->jobContext |= IF_FALSE_CONTEXT; } @@ -407,7 +419,7 @@ static int builtin_then(struct job *cmd, struct jobSet *junk) return FALSE; } /* If the if result was FALSE, skip the 'then' stuff */ - if (cmd->jobContext & IF_TRUE_CONTEXT) { + if (cmd->jobContext & IF_FALSE_CONTEXT) { return TRUE; } @@ -418,7 +430,10 @@ static int builtin_then(struct job *cmd, struct jobSet *junk) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'then' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'then' now running '%s'\n", charptr1); +#endif return( busy_loop(NULL)); } @@ -433,7 +448,7 @@ static int builtin_else(struct job *cmd, struct jobSet *junk) return FALSE; } /* If the if result was TRUE, skip the 'else' stuff */ - if (cmd->jobContext & IF_FALSE_CONTEXT) { + if (cmd->jobContext & IF_TRUE_CONTEXT) { return TRUE; } @@ -444,7 +459,10 @@ static int builtin_else(struct job *cmd, struct jobSet *junk) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'else' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'else' now running '%s'\n", charptr1); +#endif return( busy_loop(NULL)); } @@ -457,7 +475,9 @@ static int builtin_fi(struct job *cmd, struct jobSet *junk) } /* Clear out the if and then context bits */ cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT); - printf("Hit an fi -- jobContext=%d\n", cmd->jobContext); +#ifdef DEBUG_SHELL + fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext); +#endif return TRUE; } #endif @@ -633,12 +653,23 @@ static int getCommand(FILE * source, char *command) if (source == stdin) { #ifdef BB_FEATURE_SH_COMMAND_EDITING int len; + + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_init(); + signal(SIGWINCH, win_changed); len=fprintf(stdout, "%s %s", cwd, prompt); fflush(stdout); promptStr=(char*)xmalloc(sizeof(char)*(len+1)); sprintf(promptStr, "%s %s", cwd, prompt); cmdedit_read_input(promptStr, command); free( promptStr); + cmdedit_terminate(); + signal(SIGWINCH, SIG_DFL); return 0; #else fprintf(stdout, "%s %s", cwd, prompt); @@ -944,6 +975,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi prog->numRedirections = 0; prog->redirections = NULL; prog->freeGlob = 0; + prog->isStopped = 0; argc_l = 0; argvAlloced = 5; @@ -1108,10 +1140,20 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int } } +#ifdef BB_FEATURE_SH_ENVIRONMENT + if (showXtrace==TRUE) { + int j; + fprintf(stderr, "+ "); + for (j = 0; newJob->progs[i].argv[j]; j++) + fprintf(stderr, "%s ", newJob->progs[i].argv[j]); + fprintf(stderr, "\n"); + } +#endif + /* Check if the command matches any non-forking builtins */ for (x = bltins; x->cmd; x++) { if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) { - return (x->function(newJob, jobList)); + return(x->function(newJob, jobList)); } } @@ -1130,6 +1172,7 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int dup2(nextout, 1); dup2(nextout, 2); close(nextout); + close(pipefds[0]); } /* explicit redirections override pipes */ @@ -1138,18 +1181,18 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int /* Check if the command matches any of the other builtins */ for (x = bltins_forking; x->cmd; x++) { if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) { + applet_name=x->cmd; exit (x->function(newJob, jobList)); } } #ifdef BB_FEATURE_SH_STANDALONE_SHELL /* Check if the command matches any busybox internal commands here */ - /* TODO: Add matching when paths are appended (i.e. 'cat' currently - * works, but '/bin/cat' doesn't ) */ while (a->name != 0) { - if (strcmp(newJob->progs[i].argv[0], a->name) == 0) { + if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) { int argc_l; char** argv=newJob->progs[i].argv; for(argc_l=0;*argv!=NULL; argv++, argc_l++); + applet_name=a->name; exit((*(a->main)) (argc_l, newJob->progs[i].argv)); } a++; @@ -1275,15 +1318,18 @@ static int busy_loop(FILE * input) jobList.fg->runningProgs--; jobList.fg->progs[i].pid = 0; +#ifdef BB_FEATURE_SH_ENVIRONMENT + lastReturnCode=WEXITSTATUS(status); +#endif +#if 0 + printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode); +#endif if (!jobList.fg->runningProgs) { /* child exited */ removeJob(&jobList, jobList.fg); jobList.fg = NULL; } -#ifdef BB_FEATURE_SH_ENVIRONMENT - lastReturnCode=WEXITSTATUS(status); -#endif } else { /* the child was stopped */ jobList.fg->stoppedProgs++; @@ -1337,10 +1383,65 @@ void free_memory(void) int shell_main(int argc_l, char **argv_l) { + int opt; FILE *input = stdin; argc = argc_l; argv = argv_l; + + //if (argv[0] && argv[0][0] == '-') { + // builtin_source("/etc/profile"); + //} + + while ((opt = getopt(argc, argv, "cx")) > 0) { + switch (opt) { + case 'c': + input = NULL; + local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); + if (local_pending_command == 0) { + fatalError("sh: out of memory\n"); + } + for(; optind= BUFSIZ) { + local_pending_command = realloc(local_pending_command, + strlen(local_pending_command) + strlen(argv[optind])); + if (local_pending_command==NULL) + fatalError("sh: command too long\n"); + } + strcat(local_pending_command, argv[optind]); + if ( (optind + 1) < argc) + strcat(local_pending_command, " "); + } + break; + case 'x': + showXtrace = TRUE; + break; + default: + usage(shell_usage); + } + } + + + if (optind<1 && input == stdin) { + /* Looks like they want an interactive shell */ + fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); + fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); + } else if (1==(argc-optind)) { + input = fopen(argv[optind], "r"); + if (!input) { + fatalError("%s: %s\n", argv[optind], strerror(errno)); + } + } else { + char *oldpath, *newpath; + oldpath = getenv("PATH"); + newpath=(char*)xmalloc(strlen(oldpath)+12); + snprintf(newpath, strlen(oldpath)+9, "PATH=./:%s", oldpath); + putenv(newpath); + execvp(argv[optind], argv+optind); + fatalError("%s: %s\n", argv[optind], strerror(errno)); + } + /* initialize the cwd -- this is never freed...*/ cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1); getcwd(cwd, sizeof(char)*MAX_LINE); @@ -1350,52 +1451,8 @@ int shell_main(int argc_l, char **argv_l) #endif #ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_init(); - signal(SIGWINCH, win_changed); win_changed(0); #endif - //if (argv[0] && argv[0][0] == '-') { - // builtin_source("/etc/profile"); - //} - - - if (argc < 2) { - fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); - fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); - } else { - if (argv[1][0]=='-' && argv[1][1]=='c') { - int i; - local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); - if (local_pending_command == 0) { - fatalError("out of memory\n"); - } - for(i=2; i= BUFSIZ) { - local_pending_command = realloc(local_pending_command, - strlen(local_pending_command) + strlen(argv[i])); - if (local_pending_command==NULL) - fatalError("commands for -c option too long\n"); - } - strcat(local_pending_command, argv[i]); - if ( (i + 1) < argc) - strcat(local_pending_command, " "); - } - input = NULL; - - } - else if (argv[1][0]=='-') { - usage(shell_usage); - } - else { - input = fopen(argv[1], "r"); - if (!input) { - fatalError("Couldn't open file '%s': %s\n", argv[1], - strerror(errno)); - } - } - } - return (busy_loop(input)); } diff --git a/shell/cmdedit.c b/shell/cmdedit.c index 0ce64beeb..042064f1e 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c @@ -84,6 +84,7 @@ static int cmdedit_termw = 80; /* actual terminal width */ static int cmdedit_scroll = 27; /* width of EOL scrolling region */ static int history_counter = 0; /* Number of commands in history list */ static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ +static int exithandler_set = 0; /* Set to true when atexit() has been called */ struct history { char *s; @@ -709,10 +710,32 @@ extern void cmdedit_read_input(char* prompt, char command[BUFSIZ]) extern void cmdedit_init(void) { - atexit(cmdedit_reset_term); + if(exithandler_set == 0) { + atexit(cmdedit_reset_term); /* be sure to do this only once */ + exithandler_set = 1; + } signal(SIGKILL, clean_up_and_die); signal(SIGINT, clean_up_and_die); signal(SIGQUIT, clean_up_and_die); signal(SIGTERM, clean_up_and_die); } + +/* +** Undo the effects of cmdedit_init() as good as we can: +** I am not aware of a way to revoke an atexit() handler, +** but, fortunately, our particular handler can be made +** a no-op by setting reset_term = 0. +*/ +extern void cmdedit_terminate(void) +{ + cmdedit_reset_term(); + reset_term = 0; + signal(SIGKILL, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +} + + + #endif /* BB_FEATURE_SH_COMMAND_EDITING */ diff --git a/shell/cmdedit.h b/shell/cmdedit.h index b621ae8ec..27f7b5500 100644 --- a/shell/cmdedit.h +++ b/shell/cmdedit.h @@ -11,6 +11,7 @@ typedef size_t (*cmdedit_strwidth_proc)(char *); void cmdedit_init(void); +void cmdedit_terminate(void); void cmdedit_read_input(char* promptStr, char* command); /* read a line of input */ void cmdedit_setwidth(int); /* specify width of screen */ void cmdedit_histadd(char *); /* adds entries to hist */ diff --git a/shell/lash.c b/shell/lash.c index e57567608..836fc9bab 100644 --- a/shell/lash.c +++ b/shell/lash.c @@ -29,6 +29,7 @@ //#define BB_FEATURE_SH_BACKTICKS //#define BB_FEATURE_SH_IF_EXPRESSIONS //#define BB_FEATURE_SH_ENVIRONMENT +//#define DEBUG_SHELL #include "internal.h" @@ -43,6 +44,7 @@ #include #include #include +#include #ifdef BB_FEATURE_SH_COMMAND_EDITING #include "cmdedit.h" #endif @@ -172,8 +174,9 @@ static char **argv; #ifdef BB_FEATURE_SH_ENVIRONMENT static int lastBgPid=-1; static int lastReturnCode=-1; +static int showXtrace=FALSE; #endif - + #ifdef BB_FEATURE_SH_COMMAND_EDITING void win_changed(int junk) @@ -382,14 +385,23 @@ static int builtin_if(struct job *cmd, struct jobSet *jobList) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'if' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'if' now testing '%s'\n", local_pending_command); +#endif status = busy_loop(NULL); /* Frees local_pending_command */ - printf("if test returned "); +#ifdef DEBUG_SHELL + fprintf(stderr, "if test returned "); +#endif if (status == 0) { - printf("TRUE\n"); +#ifdef DEBUG_SHELL + fprintf(stderr, "TRUE\n"); +#endif cmd->jobContext |= IF_TRUE_CONTEXT; } else { - printf("FALSE\n"); +#ifdef DEBUG_SHELL + fprintf(stderr, "FALSE\n"); +#endif cmd->jobContext |= IF_FALSE_CONTEXT; } @@ -407,7 +419,7 @@ static int builtin_then(struct job *cmd, struct jobSet *junk) return FALSE; } /* If the if result was FALSE, skip the 'then' stuff */ - if (cmd->jobContext & IF_TRUE_CONTEXT) { + if (cmd->jobContext & IF_FALSE_CONTEXT) { return TRUE; } @@ -418,7 +430,10 @@ static int builtin_then(struct job *cmd, struct jobSet *junk) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'then' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'then' now running '%s'\n", charptr1); +#endif return( busy_loop(NULL)); } @@ -433,7 +448,7 @@ static int builtin_else(struct job *cmd, struct jobSet *junk) return FALSE; } /* If the if result was TRUE, skip the 'else' stuff */ - if (cmd->jobContext & IF_FALSE_CONTEXT) { + if (cmd->jobContext & IF_TRUE_CONTEXT) { return TRUE; } @@ -444,7 +459,10 @@ static int builtin_else(struct job *cmd, struct jobSet *junk) status=strlen(charptr1); local_pending_command = xmalloc(status+1); strncpy(local_pending_command, charptr1, status); - printf("'else' now running '%s'\n", charptr1); + local_pending_command[status]='\0'; +#ifdef DEBUG_SHELL + fprintf(stderr, "'else' now running '%s'\n", charptr1); +#endif return( busy_loop(NULL)); } @@ -457,7 +475,9 @@ static int builtin_fi(struct job *cmd, struct jobSet *junk) } /* Clear out the if and then context bits */ cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT); - printf("Hit an fi -- jobContext=%d\n", cmd->jobContext); +#ifdef DEBUG_SHELL + fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext); +#endif return TRUE; } #endif @@ -633,12 +653,23 @@ static int getCommand(FILE * source, char *command) if (source == stdin) { #ifdef BB_FEATURE_SH_COMMAND_EDITING int len; + + /* + ** enable command line editing only while a command line + ** is actually being read; otherwise, we'll end up bequeathing + ** atexit() handlers and other unwanted stuff to our + ** child processes (rob@sysgo.de) + */ + cmdedit_init(); + signal(SIGWINCH, win_changed); len=fprintf(stdout, "%s %s", cwd, prompt); fflush(stdout); promptStr=(char*)xmalloc(sizeof(char)*(len+1)); sprintf(promptStr, "%s %s", cwd, prompt); cmdedit_read_input(promptStr, command); free( promptStr); + cmdedit_terminate(); + signal(SIGWINCH, SIG_DFL); return 0; #else fprintf(stdout, "%s %s", cwd, prompt); @@ -944,6 +975,7 @@ static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobLi prog->numRedirections = 0; prog->redirections = NULL; prog->freeGlob = 0; + prog->isStopped = 0; argc_l = 0; argvAlloced = 5; @@ -1108,10 +1140,20 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int } } +#ifdef BB_FEATURE_SH_ENVIRONMENT + if (showXtrace==TRUE) { + int j; + fprintf(stderr, "+ "); + for (j = 0; newJob->progs[i].argv[j]; j++) + fprintf(stderr, "%s ", newJob->progs[i].argv[j]); + fprintf(stderr, "\n"); + } +#endif + /* Check if the command matches any non-forking builtins */ for (x = bltins; x->cmd; x++) { if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) { - return (x->function(newJob, jobList)); + return(x->function(newJob, jobList)); } } @@ -1130,6 +1172,7 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int dup2(nextout, 1); dup2(nextout, 2); close(nextout); + close(pipefds[0]); } /* explicit redirections override pipes */ @@ -1138,18 +1181,18 @@ static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int /* Check if the command matches any of the other builtins */ for (x = bltins_forking; x->cmd; x++) { if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) { + applet_name=x->cmd; exit (x->function(newJob, jobList)); } } #ifdef BB_FEATURE_SH_STANDALONE_SHELL /* Check if the command matches any busybox internal commands here */ - /* TODO: Add matching when paths are appended (i.e. 'cat' currently - * works, but '/bin/cat' doesn't ) */ while (a->name != 0) { - if (strcmp(newJob->progs[i].argv[0], a->name) == 0) { + if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) { int argc_l; char** argv=newJob->progs[i].argv; for(argc_l=0;*argv!=NULL; argv++, argc_l++); + applet_name=a->name; exit((*(a->main)) (argc_l, newJob->progs[i].argv)); } a++; @@ -1275,15 +1318,18 @@ static int busy_loop(FILE * input) jobList.fg->runningProgs--; jobList.fg->progs[i].pid = 0; +#ifdef BB_FEATURE_SH_ENVIRONMENT + lastReturnCode=WEXITSTATUS(status); +#endif +#if 0 + printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode); +#endif if (!jobList.fg->runningProgs) { /* child exited */ removeJob(&jobList, jobList.fg); jobList.fg = NULL; } -#ifdef BB_FEATURE_SH_ENVIRONMENT - lastReturnCode=WEXITSTATUS(status); -#endif } else { /* the child was stopped */ jobList.fg->stoppedProgs++; @@ -1337,10 +1383,65 @@ void free_memory(void) int shell_main(int argc_l, char **argv_l) { + int opt; FILE *input = stdin; argc = argc_l; argv = argv_l; + + //if (argv[0] && argv[0][0] == '-') { + // builtin_source("/etc/profile"); + //} + + while ((opt = getopt(argc, argv, "cx")) > 0) { + switch (opt) { + case 'c': + input = NULL; + local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); + if (local_pending_command == 0) { + fatalError("sh: out of memory\n"); + } + for(; optind= BUFSIZ) { + local_pending_command = realloc(local_pending_command, + strlen(local_pending_command) + strlen(argv[optind])); + if (local_pending_command==NULL) + fatalError("sh: command too long\n"); + } + strcat(local_pending_command, argv[optind]); + if ( (optind + 1) < argc) + strcat(local_pending_command, " "); + } + break; + case 'x': + showXtrace = TRUE; + break; + default: + usage(shell_usage); + } + } + + + if (optind<1 && input == stdin) { + /* Looks like they want an interactive shell */ + fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); + fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); + } else if (1==(argc-optind)) { + input = fopen(argv[optind], "r"); + if (!input) { + fatalError("%s: %s\n", argv[optind], strerror(errno)); + } + } else { + char *oldpath, *newpath; + oldpath = getenv("PATH"); + newpath=(char*)xmalloc(strlen(oldpath)+12); + snprintf(newpath, strlen(oldpath)+9, "PATH=./:%s", oldpath); + putenv(newpath); + execvp(argv[optind], argv+optind); + fatalError("%s: %s\n", argv[optind], strerror(errno)); + } + /* initialize the cwd -- this is never freed...*/ cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1); getcwd(cwd, sizeof(char)*MAX_LINE); @@ -1350,52 +1451,8 @@ int shell_main(int argc_l, char **argv_l) #endif #ifdef BB_FEATURE_SH_COMMAND_EDITING - cmdedit_init(); - signal(SIGWINCH, win_changed); win_changed(0); #endif - //if (argv[0] && argv[0][0] == '-') { - // builtin_source("/etc/profile"); - //} - - - if (argc < 2) { - fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); - fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); - } else { - if (argv[1][0]=='-' && argv[1][1]=='c') { - int i; - local_pending_command = (char *) calloc(BUFSIZ, sizeof(char)); - if (local_pending_command == 0) { - fatalError("out of memory\n"); - } - for(i=2; i= BUFSIZ) { - local_pending_command = realloc(local_pending_command, - strlen(local_pending_command) + strlen(argv[i])); - if (local_pending_command==NULL) - fatalError("commands for -c option too long\n"); - } - strcat(local_pending_command, argv[i]); - if ( (i + 1) < argc) - strcat(local_pending_command, " "); - } - input = NULL; - - } - else if (argv[1][0]=='-') { - usage(shell_usage); - } - else { - input = fopen(argv[1], "r"); - if (!input) { - fatalError("Couldn't open file '%s': %s\n", argv[1], - strerror(errno)); - } - } - } - return (busy_loop(input)); } diff --git a/utility.c b/utility.c index 50f497ff6..565fd735c 100644 --- a/utility.c +++ b/utility.c @@ -1465,13 +1465,21 @@ extern void *xmalloc(size_t size) return ptr; } -void *xrealloc(void *old, size_t size) +extern void *xrealloc(void *old, size_t size) { void *ptr = realloc(old, size); if (!ptr) fatalError(memory_exhausted); return ptr; } + +extern void *xcalloc(size_t nmemb, size_t size) +{ + void *ptr = calloc(nmemb, size); + if (!ptr) + fatalError(memory_exhausted); + return ptr; +} #endif #if defined BB_FEATURE_NFSMOUNT