X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fhush.c;h=e83d49a86e8761487fc61d5bd82c7c9684f030e0;hb=032e2cbf20bd54a7b5ded482ed1ba9d3deb574f9;hp=19501386921f396f2b4159f2ed98c9fbab341792;hpb=72f9a4277fe5ad7c6c6b9a9810d469cc7fcc3c71;p=oweals%2Fbusybox.git diff --git a/shell/hush.c b/shell/hush.c index 195013869..e83d49a86 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9,14 +9,16 @@ * * Credits: * The parser routines proper are all original material, first - * written Dec 2000 and Jan 2001 by Larry Doolittle. - * The execution engine, the builtins, and much of the underlying - * support has been adapted from busybox-0.49pre's lash, - * which is Copyright (C) 2000 by Lineo, Inc., and - * written by Erik Andersen , . - * That, in turn, is based in part on ladsh.c, by Michael K. Johnson and - * Erik W. Troan, which they placed in the public domain. I don't know - * how much of the Johnson/Troan code has survived the repeated rewrites. + * written Dec 2000 and Jan 2001 by Larry Doolittle. The + * execution engine, the builtins, and much of the underlying + * support has been adapted from busybox-0.49pre's lash, which is + * Copyright (C) 1999-2004 by Erik Andersen + * written by Erik Andersen . That, in turn, + * is based in part on ladsh.c, by Michael K. Johnson and Erik W. + * Troan, which they placed in the public domain. I don't know + * how much of the Johnson/Troan code has survived the repeated + * rewrites. + * * Other credits: * simple_itoa() was lifted from boa-0.93.15 * b_addchr() derived from similar w_addchar function in glibc-2.2 @@ -106,16 +108,20 @@ /* #include */ /* #define DEBUG_SHELL */ -#ifdef BB_VER +#if 1 #include "busybox.h" #include "cmdedit.h" #else -#define applet_name "hush" +#define bb_applet_name "hush" #include "standalone.h" #define hush_main main #undef CONFIG_FEATURE_SH_FANCY_PROMPT #define BB_BANNER #endif +#define SPECIAL_VAR_SYMBOL 03 +#define FLAG_EXIT_FROM_LOOP 1 +#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ +#define FLAG_REPARSING (1 << 2) /* >=2nd pass */ typedef enum { REDIRECT_INPUT = 1, @@ -127,7 +133,7 @@ typedef enum { /* The descrip member of this structure is only used to make debugging * output pretty */ -struct {int mode; int default_fd; char *descrip;} redir_table[] = { +static const struct {int mode; int default_fd; const char *descrip;} redir_table[] = { { 0, 0, "()" }, { O_RDONLY, 0, "<" }, { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, @@ -157,7 +163,8 @@ typedef enum { RES_DO = 9, RES_DONE = 10, RES_XXXX = 11, - RES_SNTX = 12 + RES_IN = 12, + RES_SNTX = 13 } reserved_style; #define FLAG_END (1<, but protected with __USE_GNU */ - + /* "globals" within this file */ static char *ifs; static char map[256]; @@ -251,8 +262,8 @@ static unsigned int last_jobid; static unsigned int shell_terminal; static char *PS1; static char *PS2; -struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; -struct variables *top_vars = &shell_ver; +static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; +static struct variables *top_vars = &shell_ver; #define B_CHUNK (100) @@ -286,8 +297,8 @@ struct in_str { #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" struct built_in_command { - char *cmd; /* name */ - char *descr; /* description */ + const char *cmd; /* name */ + const char *descr; /* description */ int (*function) (struct child_prog *); /* function ptr */ }; @@ -311,7 +322,7 @@ static inline void debug_printf(const char *format, ...) { } #define final_printf debug_printf static void __syntax(char *file, int line) { - error_msg("syntax error %s:%d", file, line); + bb_error_msg("syntax error %s:%d", file, line); } #define syntax() __syntax(__FILE__, __LINE__) @@ -319,6 +330,7 @@ static void __syntax(char *file, int line) { /* function prototypes for builtins */ static int builtin_cd(struct child_prog *child); static int builtin_env(struct child_prog *child); +static int builtin_eval(struct child_prog *child); static int builtin_exec(struct child_prog *child); static int builtin_exit(struct child_prog *child); static int builtin_export(struct child_prog *child); @@ -349,7 +361,7 @@ static void setup_string_in_str(struct in_str *i, const char *s); /* close_me manipulations: */ static void mark_open(int fd); static void mark_closed(int fd); -static void close_all(); +static void close_all(void); /* "run" the final data structures: */ static char *indenter(int i); static int free_pipe_list(struct pipe *head, int indent); @@ -376,19 +388,22 @@ static int redirect_dup_num(struct in_str *input); static int redirect_opt_num(o_string *o); static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end); static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); -static void lookup_param(o_string *dest, struct p_context *ctx, o_string *src); +static char *lookup_param(char *src); +static char *make_string(char **inp); static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); static int parse_string(o_string *dest, struct p_context *ctx, const char *src); static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger); /* setup: */ -static int parse_stream_outer(struct in_str *inp); -static int parse_string_outer(const char *s); +static int parse_stream_outer(struct in_str *inp, int flag); +static int parse_string_outer(const char *s, int flag); static int parse_file_outer(FILE *f); /* job management: */ static int checkjobs(struct pipe* fg_pipe); static void insert_bg_job(struct pipe *pi); static void remove_bg_job(struct pipe *pi); /* local variable support */ +static char **make_list_in(char **inp, char *name); +static char *insert_var_value(char *inp); static char *get_local_var(const char *var); static void unset_local_var(const char *name); static int set_local_var(const char *s, int flg_export); @@ -399,14 +414,14 @@ static int set_local_var(const char *s, int flg_export); * in the parent shell process. If forked, of course they can not. * For example, 'unset foo | whatever' will parse and run, but foo will * still be set at the end. */ -static struct built_in_command bltins[] = { +static const struct built_in_command bltins[] = { {"bg", "Resume a job in the background", builtin_fg_bg}, {"break", "Exit for, while or until loop", builtin_not_written}, {"cd", "Change working directory", builtin_cd}, {"continue", "Continue for, while or until loop", builtin_not_written}, {"env", "Print all environment variables", builtin_env}, - {"eval", "Construct and run shell command", builtin_not_written}, - {"exec", "Exec command, replacing this shell with the exec'd process", + {"eval", "Construct and run shell command", builtin_eval}, + {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec}, {"exit", "Exit from shell()", builtin_exit}, {"export", "Set environment variable", builtin_export}, @@ -428,14 +443,29 @@ static struct built_in_command bltins[] = { static const char *set_cwd(void) { - if(cwd==unknown) + if(cwd==bb_msg_unknown) cwd = NULL; /* xgetcwd(arg) called free(arg) */ cwd = xgetcwd((char *)cwd); if (!cwd) - cwd = unknown; + cwd = bb_msg_unknown; return cwd; } +/* built-in 'eval' handler */ +static int builtin_eval(struct child_prog *child) +{ + char *str = NULL; + int rcode = EXIT_SUCCESS; + + if (child->argv[1]) { + str = make_string(child->argv + 1); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | + FLAG_PARSE_SEMICOLON); + free(str); + rcode = last_return_code; + } + return rcode; +} /* built-in 'cd ' handler */ static int builtin_cd(struct child_prog *child) @@ -520,7 +550,7 @@ static int builtin_export(struct child_prog *child) } } if (res<0) - perror_msg("export"); + bb_perror_msg("export"); else if(res==0) res = set_local_var(name, 1); else @@ -545,12 +575,12 @@ static int builtin_fg_bg(struct child_prog *child) } } if (!pi) { - error_msg("%s: no current job", child->argv[0]); + bb_error_msg("%s: no current job", child->argv[0]); return EXIT_FAILURE; } } else { if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { - error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); + bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); return EXIT_FAILURE; } for (pi = job_list; pi; pi = pi->next) { @@ -559,7 +589,7 @@ static int builtin_fg_bg(struct child_prog *child) } } if (!pi) { - error_msg("%s: %d: no such job", child->argv[0], jobnum); + bb_error_msg("%s: %d: no such job", child->argv[0], jobnum); return EXIT_FAILURE; } } @@ -577,7 +607,7 @@ static int builtin_fg_bg(struct child_prog *child) if (i == ESRCH) { remove_bg_job(pi); } else { - perror_msg("kill (SIGCONT)"); + bb_perror_msg("kill (SIGCONT)"); } } @@ -588,7 +618,7 @@ static int builtin_fg_bg(struct child_prog *child) /* built-in 'help' handler */ static int builtin_help(struct child_prog *dummy) { - struct built_in_command *x; + const struct built_in_command *x; printf("\nBuilt-in commands:\n"); printf("-------------------\n"); @@ -700,7 +730,7 @@ static int builtin_source(struct child_prog *child) /* XXX search through $PATH is missing */ input = fopen(child->argv[1], "r"); if (!input) { - error_msg("Couldn't open file '%s'", child->argv[1]); + bb_error_msg("Couldn't open file '%s'", child->argv[1]); return EXIT_FAILURE; } @@ -782,7 +812,7 @@ static void b_reset(o_string *o) static void b_free(o_string *o) { b_reset(o); - if (o->data != NULL) free(o->data); + free(o->data); o->data = NULL; o->maxlen = 0; } @@ -801,7 +831,7 @@ static int b_addqchr(o_string *o, int ch, int quote) } /* belongs in utility.c */ -char *simple_itoa(unsigned int i) +static char *simple_itoa(unsigned int i) { /* 21 digits plus null terminator, good for 64-bit or smaller ints */ static char local[22]; @@ -843,7 +873,7 @@ static inline void cmdedit_set_initial_prompt(void) PS1 = getenv("PS1"); if(PS1==0) PS1 = "\\w \\$ "; -#endif +#endif } static inline void setup_prompt_string(int promptmode, char **prompt_str) @@ -852,8 +882,7 @@ static inline void setup_prompt_string(int promptmode, char **prompt_str) #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT /* Set up the prompt */ if (promptmode == 1) { - if (PS1) - free(PS1); + free(PS1); PS1=xmalloc(strlen(cwd)+4); sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); *prompt_str = PS1; @@ -890,7 +919,7 @@ static void get_user_input(struct in_str *i) i->p = the_command; } -/* This is the magic location that prints prompts +/* This is the magic location that prints prompts * and gets data back from the user */ static int file_get(struct in_str *i) { @@ -969,13 +998,13 @@ static void mark_closed(int fd) { struct close_me *tmp; if (close_me_head == NULL || close_me_head->fd != fd) - error_msg_and_die("corrupt close_me"); + bb_error_msg_and_die("corrupt close_me"); tmp = close_me_head; close_me_head = close_me_head->next; free(tmp); } -static void close_all() +static void close_all(void) { struct close_me *c; for (c=close_me_head; c; c=c->next) { @@ -1002,7 +1031,7 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) if (openfd < 0) { /* this could get lost if stderr has been redirected, but bash and ash both lose it as well (though zsh doesn't!) */ - perror_msg("error opening %s", redir->word.gl_pathv[0]); + bb_perror_msg("error opening %s", redir->word.gl_pathv[0]); return 1; } } else { @@ -1046,11 +1075,14 @@ static void restore_redirects(int squirrel[]) static void pseudo_exec(struct child_prog *child) { int i, rcode; - struct built_in_command *x; + char *p; + const struct built_in_command *x; if (child->argv) { for (i=0; is_assignment(child->argv[i]); i++) { debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]); - putenv(strdup(child->argv[i])); + p = insert_var_value(child->argv[i]); + putenv(strdup(p)); + if (p != child->argv[i]) free(p); } child->argv+=i; /* XXX this hack isn't so horrible, since we are about to exit, and therefore don't need to keep data @@ -1078,32 +1110,20 @@ static void pseudo_exec(struct child_prog *child) } /* Check if the command matches any busybox internal commands - * ("applets") here. + * ("applets") here. * FIXME: This feature is not 100% safe, since * BusyBox is not fully reentrant, so we have no guarantee the things * from the .bss are still zeroed, or that things from .data are still * at their defaults. We could exec ourself from /proc/self/exe, but I * really dislike relying on /proc for things. We could exec ourself * from global_argv[0], but if we are in a chroot, we may not be able - * to find ourself... */ + * to find ourself... */ #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL { int argc_l; char** argv_l=child->argv; char *name = child->argv[0]; -#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN - /* Following discussions from November 2000 on the busybox mailing - * list, the default configuration, (without - * get_last_path_component()) lets the user force use of an - * external command by specifying the full (with slashes) filename. - * If you enable CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN, then applets - * _aways_ override external commands, so if you want to run - * /bin/cat, it will use BusyBox cat even if /bin/cat exists on the - * filesystem and is _not_ busybox. Some systems may want this, - * most do not. */ - name = get_last_path_component(name); -#endif /* Count argc for use in a second... */ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); optind = 1; @@ -1113,7 +1133,7 @@ static void pseudo_exec(struct child_prog *child) #endif debug_printf("exec of %s\n",child->argv[0]); execvp(child->argv[0],child->argv); - perror_msg("couldn't exec: %s",child->argv[0]); + bb_perror_msg("couldn't exec: %s",child->argv[0]); _exit(1); } else if (child->group) { debug_printf("runtime nesting to group\n"); @@ -1164,7 +1184,7 @@ static void insert_bg_job(struct pipe *pi) } } - /* we don't wait for background thejobs to return -- append it + /* we don't wait for background thejobs to return -- append it to the list of backgrounded thejobs and leave it alone */ printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); last_bg_pid = thejob->progs[0].pid; @@ -1194,7 +1214,7 @@ static void remove_bg_job(struct pipe *pi) free(pi); } -/* Checks to see if any processes have exited -- if they +/* Checks to see if any processes have exited -- if they have, figure out why and see if a job has completed */ static int checkjobs(struct pipe* fg_pipe) { @@ -1214,7 +1234,7 @@ static int checkjobs(struct pipe* fg_pipe) int i, rcode = 0; for (i=0; i < fg_pipe->num_progs; i++) { if (fg_pipe->progs[i].pid == childpid) { - if (i==fg_pipe->num_progs-1) + if (i==fg_pipe->num_progs-1) rcode=WEXITSTATUS(status); (fg_pipe->num_progs)--; return(rcode); @@ -1257,16 +1277,16 @@ static int checkjobs(struct pipe* fg_pipe) if (pi->stopped_progs == pi->num_progs) { printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text); } -#endif +#endif } } if (childpid == -1 && errno != ECHILD) - perror_msg("waitpid"); + bb_perror_msg("waitpid"); /* move the shell to the foreground */ //if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) - // perror_msg("tcsetpgrp-2"); + // bb_perror_msg("tcsetpgrp-2"); return -1; } @@ -1275,7 +1295,8 @@ static int checkjobs(struct pipe* fg_pipe) * we belong to the foreground process group associated with * that tty. The value of shell_terminal is needed in order to call * tcsetpgrp(shell_terminal, ...); */ -void controlling_tty(int check_pgrp) +#if 0 +static void controlling_tty(int check_pgrp) { pid_t curpgrp; @@ -1293,6 +1314,7 @@ shell_terminal_error: shell_terminal = -1; return; } +#endif /* run_pipe_real() starts all the jobs, but doesn't wait for anything * to finish. See checkjobs(). @@ -1316,7 +1338,8 @@ static int run_pipe_real(struct pipe *pi) int nextin, nextout; int pipefds[2]; /* pipefds[0] is for reading */ struct child_prog *child; - struct built_in_command *x; + const struct built_in_command *x; + char *p; nextin = 0; pi->pgrp = -1; @@ -1345,12 +1368,12 @@ static int run_pipe_real(struct pipe *pi) /* Ok, this case is tricky. We have to decide if this is a * local variable, or an already exported variable. If it is * already exported, we have to export the new value. If it is - * not exported, we need only set this as a local variable. + * not exported, we need only set this as a local variable. * This junk is all to decide whether or not to export this * variable. */ int export_me=0; char *name, *value; - name = xstrdup(child->argv[i]); + name = bb_xstrdup(child->argv[i]); debug_printf("Local environment set: %s\n", name); value = strchr(name, '='); if (value) @@ -1359,10 +1382,28 @@ static int run_pipe_real(struct pipe *pi) export_me=1; } free(name); - set_local_var(child->argv[i], export_me); + p = insert_var_value(child->argv[i]); + set_local_var(p, export_me); + if (p != child->argv[i]) free(p); } return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ } + for (i = 0; is_assignment(child->argv[i]); i++) { + p = insert_var_value(child->argv[i]); + putenv(strdup(p)); + if (p != child->argv[i]) { + child->sp--; + free(p); + } + } + if (child->sp) { + char * str = NULL; + + str = make_string((child->argv + i)); + parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); + free(str); + return last_return_code; + } for (x = bltins; x->cmd; x++) { if (strcmp(child->argv[i], x->cmd) == 0 ) { int squirrel[] = {-1, -1, -1}; @@ -1378,9 +1419,6 @@ static int run_pipe_real(struct pipe *pi) * Is it really safe for inline use? Experimentally, * things seem to work with glibc. */ setup_redirects(child, squirrel); - for (i=0; is_assignment(child->argv[i]); i++) { - putenv(strdup(child->argv[i])); - } child->argv+=i; /* XXX horrible hack */ rcode = x->function(child); child->argv-=i; /* XXX restore hack so free() can work right */ @@ -1395,7 +1433,7 @@ static int run_pipe_real(struct pipe *pi) /* pipes are inserted between pairs of commands */ if ((i + 1) < pi->num_progs) { - if (pipe(pipefds)<0) perror_msg_and_die("pipe"); + if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe"); nextout = pipefds[1]; } else { nextout=1; @@ -1403,10 +1441,10 @@ static int run_pipe_real(struct pipe *pi) } /* XXX test for failed fork()? */ -#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__) +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) if (!(child->pid = fork())) #else - if (!(child->pid = vfork())) + if (!(child->pid = vfork())) #endif { /* Set the handling for job control signals back to the default. */ @@ -1417,7 +1455,7 @@ static int run_pipe_real(struct pipe *pi) signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); signal(SIGCHLD, SIG_DFL); - + close_all(); if (nextin != 0) { @@ -1449,7 +1487,7 @@ static int run_pipe_real(struct pipe *pi) pseudo_exec(child); } - + /* put our child in the process group whose leader is the first process in this pipe */ @@ -1465,7 +1503,7 @@ static int run_pipe_real(struct pipe *pi) if (nextout != 1) close(nextout); - /* If there isn't another process, nextin is garbage + /* If there isn't another process, nextin is garbage but it doesn't matter */ nextin = pipefds[0]; } @@ -1474,19 +1512,97 @@ static int run_pipe_real(struct pipe *pi) static int run_list_real(struct pipe *pi) { - int rcode=0; + char *save_name = NULL; + char **list = NULL; + char **save_list = NULL; + struct pipe *rpipe; + int flag_rep = 0; + int save_num_progs; + int rcode=0, flag_skip=1; + int flag_restore = 0; int if_code=0, next_if_code=0; /* need double-buffer to handle elif */ reserved_style rmode, skip_more_in_this_rmode=RES_XXXX; - for (;pi;pi=pi->next) { + /* check syntax for "for" */ + for (rpipe = pi; rpipe; rpipe = rpipe->next) { + if ((rpipe->r_mode == RES_IN || + rpipe->r_mode == RES_FOR) && + (rpipe->next == NULL)) { + syntax(); + return 1; + } + if ((rpipe->r_mode == RES_IN && + (rpipe->next->r_mode == RES_IN && + rpipe->next->progs->argv != NULL))|| + (rpipe->r_mode == RES_FOR && + rpipe->next->r_mode != RES_IN)) { + syntax(); + return 1; + } + } + for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { + if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL || + pi->r_mode == RES_FOR) { + flag_restore = 0; + if (!rpipe) { + flag_rep = 0; + rpipe = pi; + } + } rmode = pi->r_mode; debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode); - if (rmode == skip_more_in_this_rmode) continue; + if (rmode == skip_more_in_this_rmode && flag_skip) { + if (pi->followup == PIPE_SEQ) flag_skip=0; + continue; + } + flag_skip = 1; skip_more_in_this_rmode = RES_XXXX; if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code; if (rmode == RES_THEN && if_code) continue; if (rmode == RES_ELSE && !if_code) continue; - if (rmode == RES_ELIF && !if_code) continue; + if (rmode == RES_ELIF && !if_code) break; + if (rmode == RES_FOR && pi->num_progs) { + if (!list) { + /* if no variable values after "in" we skip "for" */ + if (!pi->next->progs->argv) continue; + /* create list of variable values */ + list = make_list_in(pi->next->progs->argv, + pi->progs->argv[0]); + save_list = list; + save_name = pi->progs->argv[0]; + pi->progs->argv[0] = NULL; + flag_rep = 1; + } + if (!(*list)) { + free(pi->progs->argv[0]); + free(save_list); + list = NULL; + flag_rep = 0; + pi->progs->argv[0] = save_name; + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; + continue; + } else { + /* insert new value from list for variable */ + if (pi->progs->argv[0]) + free(pi->progs->argv[0]); + pi->progs->argv[0] = *list++; + pi->progs->glob_result.gl_pathv[0] = + pi->progs->argv[0]; + } + } + if (rmode == RES_IN) continue; + if (rmode == RES_DO) { + if (!flag_rep) continue; + } + if ((rmode == RES_DONE)) { + if (flag_rep) { + flag_restore = 1; + } else { + rpipe = NULL; + } + } if (pi->num_progs == 0) continue; + save_num_progs = pi->num_progs; /* save number of programs */ rcode = run_pipe_real(pi); debug_printf("run_pipe_real returned %d\n",rcode); if (rcode!=-1) { @@ -1502,19 +1618,24 @@ static int run_list_real(struct pipe *pi) if (interactive) { /* move the new process group into the foreground */ if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) - perror_msg("tcsetpgrp-3"); + bb_perror_msg("tcsetpgrp-3"); rcode = checkjobs(pi); /* move the shell to the foreground */ if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) - perror_msg("tcsetpgrp-4"); + bb_perror_msg("tcsetpgrp-4"); } else { rcode = checkjobs(pi); } debug_printf("checkjobs returned %d\n",rcode); } last_return_code=rcode; + pi->num_progs = save_num_progs; /* restore number of programs */ if ( rmode == RES_IF || rmode == RES_ELIF ) next_if_code=rcode; /* can be overwritten a number of times */ + if (rmode == RES_WHILE) + flag_rep = !last_return_code; + if (rmode == RES_UNTIL) + flag_rep = last_return_code; if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) || (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) skip_more_in_this_rmode=rmode; @@ -1592,7 +1713,7 @@ static int free_pipe_list(struct pipe *head, int indent) pi->next=NULL; free(pi); } - return rcode; + return rcode; } /* Select which version we will use */ @@ -1601,7 +1722,7 @@ static int run_list(struct pipe *pi) int rcode=0; if (fake_mode==0) { rcode = run_list_real(pi); - } + } /* free_pipe_list has the side effect of clearing memory * In the long run that function can be merged with run_list_real, * but doing that now would hobble the debugging effort. */ @@ -1696,9 +1817,9 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) debug_printf("globhack returned %d\n",gr); } if (gr == GLOB_NOSPACE) - error_msg_and_die("out of memory during glob"); + bb_error_msg_and_die("out of memory during glob"); if (gr != 0) { /* GLOB_ABORTED ? */ - error_msg("glob(3) error %d",gr); + bb_error_msg("glob(3) error %d",gr); } /* globprint(glob_target); */ return gr; @@ -1731,7 +1852,7 @@ static int set_local_var(const char *s, int flg_export) /* Assume when we enter this function that we are already in * NAME=VALUE format. So the first order of business is to - * split 's' on the '=' into 'name' and 'value' */ + * split 's' on the '=' into 'name' and 'value' */ value = strchr(name, '='); if (value==0 && ++value==0) { free(name); @@ -1752,7 +1873,7 @@ static int set_local_var(const char *s, int flg_export) result++; } else { if(cur->flg_read_only) { - error_msg("%s: readonly variable", name); + bb_error_msg("%s: readonly variable", name); result = -1; } else { if(flg_export>0 || cur->flg_export>1) @@ -1806,7 +1927,7 @@ static void unset_local_var(const char *name) if(cur!=0) { struct variables *next = top_vars; if(cur->flg_read_only) { - error_msg("%s: readonly variable", name); + bb_error_msg("%s: readonly variable", name); return; } else { if(cur->flg_export) @@ -1860,7 +1981,7 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); - /* Check for a '2>&1' type redirect */ + /* Check for a '2>&1' type redirect */ redir->dup = redirect_dup_num(input); if (redir->dup == -2) return 1; /* syntax error */ if (redir->dup != -1) { @@ -1879,13 +2000,14 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, return 0; } -struct pipe *new_pipe(void) { +static struct pipe *new_pipe(void) { struct pipe *pi; pi = xmalloc(sizeof(struct pipe)); pi->num_progs = 0; pi->progs = NULL; pi->next = NULL; pi->followup = 0; /* invalid */ + pi->r_mode = RES_NONE; return pi; } @@ -1898,6 +2020,7 @@ static void initialize_context(struct p_context *ctx) ctx->pipe=ctx->list_head; ctx->w=RES_NONE; ctx->stack=NULL; + ctx->old_flag=0; done_command(ctx); /* creates the memory for working child */ } @@ -1906,7 +2029,7 @@ static void initialize_context(struct p_context *ctx) * should handle if, then, elif, else, fi, for, while, until, do, done. * case, function, and select are obnoxious, save those for later. */ -int reserved_word(o_string *dest, struct p_context *ctx) +static int reserved_word(o_string *dest, struct p_context *ctx) { struct reserved_combo { char *literal; @@ -1924,9 +2047,10 @@ int reserved_word(o_string *dest, struct p_context *ctx) { "elif", RES_ELIF, FLAG_THEN }, { "else", RES_ELSE, FLAG_FI }, { "fi", RES_FI, FLAG_END }, - { "for", RES_FOR, FLAG_DO | FLAG_START }, + { "for", RES_FOR, FLAG_IN | FLAG_START }, { "while", RES_WHILE, FLAG_DO | FLAG_START }, { "until", RES_UNTIL, FLAG_DO | FLAG_START }, + { "in", RES_IN, FLAG_DO }, { "do", RES_DO, FLAG_DONE }, { "done", RES_DONE, FLAG_END } }; @@ -1939,13 +2063,20 @@ int reserved_word(o_string *dest, struct p_context *ctx) if (r->flag & FLAG_START) { struct p_context *new = xmalloc(sizeof(struct p_context)); debug_printf("push stack\n"); + if (ctx->w == RES_IN || ctx->w == RES_FOR) { + syntax(); + free(new); + ctx->w = RES_SNTX; + b_reset(dest); + return 1; + } *new = *ctx; /* physical copy */ initialize_context(ctx); ctx->stack=new; } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<code))) { syntax(); ctx->w = RES_SNTX; - b_reset (dest); + b_reset(dest); return 1; } ctx->w=r->code; @@ -1953,6 +2084,7 @@ int reserved_word(o_string *dest, struct p_context *ctx) if (ctx->old_flag & FLAG_END) { struct p_context *old; debug_printf("pop stack\n"); + done_pipe(ctx,PIPE_SEQ); old = ctx->stack; old->child->group = ctx->list_head; old->child->subshell = 0; @@ -1986,7 +2118,7 @@ static int done_word(o_string *dest, struct p_context *ctx) syntax(); return 1; /* syntax error, groups and arglists don't mix */ } - if (!child->argv) { + if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) { debug_printf("checking %s for reserved-ness\n",dest->data); if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX; } @@ -2000,12 +2132,16 @@ static int done_word(o_string *dest, struct p_context *ctx) if (ctx->pending_redirect) { ctx->pending_redirect=NULL; if (glob_target->gl_pathc != 1) { - error_msg("ambiguous redirect"); + bb_error_msg("ambiguous redirect"); return 1; } } else { child->argv = glob_target->gl_pathv; } + if (ctx->w == RES_FOR) { + done_word(dest,ctx); + done_pipe(ctx,PIPE_SEQ); + } return 0; } @@ -2041,8 +2177,10 @@ static int done_command(struct p_context *ctx) prog->group = NULL; prog->glob_result.gl_pathv = NULL; prog->family = pi; + prog->sp = 0; + ctx->child = prog; + prog->type = ctx->type; - ctx->child=prog; /* but ctx->pipe and ctx->list_head remain unchanged */ return 0; } @@ -2086,7 +2224,7 @@ static int redirect_dup_num(struct in_str *input) } if (ok) return d; - error_msg("ambiguous redirect"); + bb_error_msg("ambiguous redirect"); return -2; } @@ -2117,19 +2255,19 @@ static int redirect_opt_num(o_string *o) return num; } -FILE *generate_stream_from_list(struct pipe *head) +static FILE *generate_stream_from_list(struct pipe *head) { FILE *pf; #if 1 int pid, channel[2]; - if (pipe(channel)<0) perror_msg_and_die("pipe"); -#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__) + if (pipe(channel)<0) bb_perror_msg_and_die("pipe"); +#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) pid=fork(); #else pid=vfork(); #endif if (pid<0) { - perror_msg_and_die("fork"); + bb_perror_msg_and_die("fork"); } else if (pid==0) { close(channel[0]); if (channel[1] != 1) { @@ -2227,32 +2365,32 @@ static int parse_group(o_string *dest, struct p_context *ctx, /* basically useful version until someone wants to get fancier, * see the bash man page under "Parameter Expansion" */ -static void lookup_param(o_string *dest, struct p_context *ctx, o_string *src) +static char *lookup_param(char *src) { - const char *p=NULL; - if (src->data) { - p = getenv(src->data); - if (!p) - p = get_local_var(src->data); + char *p=NULL; + if (src) { + p = getenv(src); + if (!p) + p = get_local_var(src); } - if (p) parse_string(dest, ctx, p); /* recursion */ - b_free(src); + return p; } /* return code: 0 for OK, 1 for syntax error */ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) { int i, advance=0; - o_string alt=NULL_O_STRING; char sep[]=" "; int ch = input->peek(input); /* first character after the $ */ debug_printf("handle_dollar: ch=%c\n",ch); if (isalpha(ch)) { + b_addchr(dest, SPECIAL_VAR_SYMBOL); + ctx->child->sp++; while(ch=b_peek(input),isalnum(ch) || ch=='_') { b_getch(input); - b_addchr(&alt,ch); + b_addchr(dest,ch); } - lookup_param(dest, ctx, &alt); + b_addchr(dest, SPECIAL_VAR_SYMBOL); } else if (isdigit(ch)) { i = ch-'0'; /* XXX is $0 special? */ if (ichild->sp++; b_getch(input); /* XXX maybe someone will try to escape the '}' */ while(ch=b_getch(input),ch!=EOF && ch!='}') { - b_addchr(&alt,ch); + b_addchr(dest,ch); } if (ch != '}') { syntax(); return 1; } - lookup_param(dest, ctx, &alt); + b_addchr(dest, SPECIAL_VAR_SYMBOL); break; case '(': b_getch(input); @@ -2303,7 +2443,7 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i case '-': case '_': /* still unhandled, but should be eventually */ - error_msg("unhandled syntax: $%c",ch); + bb_error_msg("unhandled syntax: $%c",ch); return 1; break; default: @@ -2348,7 +2488,9 @@ int parse_stream(o_string *dest, struct p_context *ctx, b_addqchr(dest, ch, dest->quote); } else { if (m==2) { /* unquoted IFS */ - done_word(dest, ctx); + if (done_word(dest, ctx)) { + return 1; + } /* If we aren't performing a substitution, treat a newline as a * command separator. */ if (end_trigger != '\0' && ch=='\n') @@ -2473,7 +2615,7 @@ int parse_stream(o_string *dest, struct p_context *ctx, } /* complain if quote? No, maybe we just finished a command substitution * that was quoted. Example: - * $ echo "`cat foo` plus more" + * $ echo "`cat foo` plus more" * and we just got the EOF generated by the subshell that ran "cat foo" * The only real complaint is if we got an EOF when end_trigger != '\0', * that is, we were really supposed to get end_trigger, and never got @@ -2484,13 +2626,13 @@ int parse_stream(o_string *dest, struct p_context *ctx, return 0; } -void mapset(const unsigned char *set, int code) +static void mapset(const unsigned char *set, int code) { const unsigned char *s; for (s=set; *s; s++) map[*s] = code; } -void update_ifs_map(void) +static void update_ifs_map(void) { /* char *ifs and char map[256] are both globals. */ ifs = getenv("IFS"); @@ -2507,32 +2649,48 @@ void update_ifs_map(void) mapset(ifs, 2); /* also flow through if quoted */ } -/* most recursion does not come through here, the exeception is +/* most recursion does not come through here, the exception is * from builtin_source() */ -int parse_stream_outer(struct in_str *inp) +int parse_stream_outer(struct in_str *inp, int flag) { struct p_context ctx; o_string temp=NULL_O_STRING; int rcode; do { + ctx.type = flag; initialize_context(&ctx); update_ifs_map(); + if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0); inp->promptmode=1; rcode = parse_stream(&temp, &ctx, inp, '\n'); - done_word(&temp, &ctx); - done_pipe(&ctx,PIPE_SEQ); - run_list(ctx.list_head); + if (rcode != 1 && ctx.old_flag != 0) { + syntax(); + } + if (rcode != 1 && ctx.old_flag == 0) { + done_word(&temp, &ctx); + done_pipe(&ctx,PIPE_SEQ); + run_list(ctx.list_head); + } else { + if (ctx.old_flag != 0) { + free(ctx.stack); + b_reset(&temp); + } + temp.nonnull = 0; + temp.quote = 0; + inp->p = NULL; + free_pipe_list(ctx.list_head,0); + } b_free(&temp); - } while (rcode != -1); /* loop on syntax errors, return on EOF */ + } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ return 0; } -static int parse_string_outer(const char *s) +static int parse_string_outer(const char *s, int flag) { struct in_str input; setup_string_in_str(&input, s); - return parse_stream_outer(&input); + return parse_stream_outer(&input, flag); } static int parse_file_outer(FILE *f) @@ -2540,14 +2698,14 @@ static int parse_file_outer(FILE *f) int rcode; struct in_str input; setup_file_in_str(&input, f); - rcode = parse_stream_outer(&input); + rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); return rcode; } /* Make sure we have a controlling tty. If we get started under a job * aware app (like bash for example), make sure we are now in charge so * we don't fight over who gets the foreground */ -static void setup_job_control() +static void setup_job_control(void) { static pid_t shell_pgrp; /* Loop until we are in the foreground. */ @@ -2581,9 +2739,9 @@ int hush_main(int argc, char **argv) /* XXX what should these be while sourcing /etc/profile? */ global_argc = argc; global_argv = argv; - + /* (re?) initialize globals. Sometimes hush_main() ends up calling - * hush_main(), therefore we cannot rely on the BSS to zero out this + * hush_main(), therefore we cannot rely on the BSS to zero out this * stuff. Reset these to 0 every time. */ ifs = NULL; /* map[] is taken care of with call to update_ifs_map() */ @@ -2603,7 +2761,7 @@ int hush_main(int argc, char **argv) #endif PS2 = "> "; - /* initialize our shell local variables with the values + /* initialize our shell local variables with the values * currently living in the environment */ if (e) { for (; *e; e++) @@ -2623,14 +2781,14 @@ int hush_main(int argc, char **argv) } } input=stdin; - + while ((opt = getopt(argc, argv, "c:xif")) > 0) { switch (opt) { case 'c': { global_argv = argv+optind; global_argc = argc-optind; - opt = parse_string_outer(optarg); + opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); goto final_return; } break; @@ -2646,7 +2804,7 @@ int hush_main(int argc, char **argv) " or: sh -c command [args]...\n\n"); exit(EXIT_FAILURE); #else - show_usage(); + bb_show_usage(); #endif } } @@ -2658,20 +2816,20 @@ int hush_main(int argc, char **argv) * standard output is a terminal * Refer to Posix.2, the description of the `sh' utility. */ if (argv[optind]==NULL && input==stdin && - isatty(fileno(stdin)) && isatty(fileno(stdout))) { + isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { interactive++; } debug_printf("\ninteractive=%d\n", interactive); if (interactive) { /* Looks like they want an interactive shell */ -#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET +#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n"); printf( "Enter 'help' for a list of built-in commands.\n\n"); #endif setup_job_control(); } - + if (argv[optind]==NULL) { opt=parse_file_outer(stdin); goto final_return; @@ -2680,12 +2838,12 @@ int hush_main(int argc, char **argv) debug_printf("\nrunning script '%s'\n", argv[optind]); global_argv = argv+optind; global_argc = argc-optind; - input = xfopen(argv[optind], "r"); + input = bb_xfopen(argv[optind], "r"); opt = parse_file_outer(input); #ifdef CONFIG_FEATURE_CLEAN_UP fclose(input); - if (cwd && cwd != unknown) + if (cwd && cwd != bb_msg_unknown) free((char*)cwd); { struct variables *cur, *tmp; @@ -2703,3 +2861,106 @@ int hush_main(int argc, char **argv) final_return: return(opt?opt:last_return_code); } + +static char *insert_var_value(char *inp) +{ + int res_str_len = 0; + int len; + int done = 0; + char *p, *p1, *res_str = NULL; + + while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { + if (p != inp) { + len = p - inp; + res_str = xrealloc(res_str, (res_str_len + len)); + strncpy((res_str + res_str_len), inp, len); + res_str_len += len; + } + inp = ++p; + p = strchr(inp, SPECIAL_VAR_SYMBOL); + *p = '\0'; + if ((p1 = lookup_param(inp))) { + len = res_str_len + strlen(p1); + res_str = xrealloc(res_str, (1 + len)); + strcpy((res_str + res_str_len), p1); + res_str_len = len; + } + *p = SPECIAL_VAR_SYMBOL; + inp = ++p; + done = 1; + } + if (done) { + res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp))); + strcpy((res_str + res_str_len), inp); + while ((p = strchr(res_str, '\n'))) { + *p = ' '; + } + } + return (res_str == NULL) ? inp : res_str; +} + +static char **make_list_in(char **inp, char *name) +{ + int len, i; + int name_len = strlen(name); + int n = 0; + char **list; + char *p1, *p2, *p3; + + /* create list of variable values */ + list = xmalloc(sizeof(*list)); + for (i = 0; inp[i]; i++) { + p3 = insert_var_value(inp[i]); + p1 = p3; + while (*p1) { + if ((*p1 == ' ')) { + p1++; + continue; + } + if ((p2 = strchr(p1, ' '))) { + len = p2 - p1; + } else { + len = strlen(p1); + p2 = p1 + len; + } + /* we use n + 2 in realloc for list,because we add + * new element and then we will add NULL element */ + list = xrealloc(list, sizeof(*list) * (n + 2)); + list[n] = xmalloc(2 + name_len + len); + strcpy(list[n], name); + strcat(list[n], "="); + strncat(list[n], p1, len); + list[n++][name_len + len + 1] = '\0'; + p1 = p2; + } + if (p3 != inp[i]) free(p3); + } + list[n] = NULL; + return list; +} + +/* Make new string for parser */ +static char * make_string(char ** inp) +{ + char *p; + char *str = NULL; + int n; + int len = 2; + + for (n = 0; inp[n]; n++) { + p = insert_var_value(inp[n]); + str = xrealloc(str, (len + strlen(p))); + if (n) { + strcat(str, " "); + } else { + *str = '\0'; + } + strcat(str, p); + len = strlen(str) + 3; + if (p != inp[n]) free(p); + } + len = strlen(str); + *(str + len) = '\n'; + *(str + len + 1) = '\0'; + return str; +}