diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-23 13:01:10 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-23 13:01:10 +0000 |
| commit | 170435c575bb53e1e4d55aa74d471c293c21039e (patch) | |
| tree | 7bde6bd5f00ee9c91a78b5f707657dee4ae40b57 /shell | |
| parent | 1a7358612ff2dfdfc9fa461faa946c577666787f (diff) | |
| download | busybox-w32-170435c575bb53e1e4d55aa74d471c293c21039e.tar.gz busybox-w32-170435c575bb53e1e4d55aa74d471c293c21039e.tar.bz2 busybox-w32-170435c575bb53e1e4d55aa74d471c293c21039e.zip | |
hush: fix job control with eval /bin/external_prog
hush: fix parsing of unterminated "str with no EOL
hush: improved make_string() (smaller, faster, needs less RAM)
hush: renamed several functions
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 185 | ||||
| -rw-r--r-- | shell/hush_test/hush-bugs/noeol3.right | 1 | ||||
| -rwxr-xr-x | shell/hush_test/hush-bugs/noeol3.tests | 2 |
3 files changed, 96 insertions, 92 deletions
diff --git a/shell/hush.c b/shell/hush.c index aab6ff3a3..1545b041f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -117,6 +117,9 @@ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ | |||
| 117 | #define DEBUG_EXPAND 1 | 117 | #define DEBUG_EXPAND 1 |
| 118 | #endif | 118 | #endif |
| 119 | 119 | ||
| 120 | /* Keep unconditionally on for now */ | ||
| 121 | #define ENABLE_HUSH_DEBUG 1 | ||
| 122 | |||
| 120 | #ifndef debug_printf_clean | 123 | #ifndef debug_printf_clean |
| 121 | /* broken, of course, but OK for testing */ | 124 | /* broken, of course, but OK for testing */ |
| 122 | static const char *indenter(int i) | 125 | static const char *indenter(int i) |
| @@ -497,13 +500,12 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, struct in | |||
| 497 | #endif | 500 | #endif |
| 498 | static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); | 501 | static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); |
| 499 | static const char *lookup_param(const char *src); | 502 | static const char *lookup_param(const char *src); |
| 500 | static char *make_string(char **inp); | ||
| 501 | static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); | 503 | static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); |
| 502 | static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger); | 504 | static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger); |
| 503 | /* setup: */ | 505 | /* setup: */ |
| 504 | static int parse_stream_outer(struct in_str *inp, int parse_flag); | 506 | static int parse_and_run_stream(struct in_str *inp, int parse_flag); |
| 505 | static int parse_string_outer(const char *s, int parse_flag); | 507 | static int parse_and_run_string(const char *s, int parse_flag); |
| 506 | static int parse_file_outer(FILE *f); | 508 | static int parse_and_run_file(FILE *f); |
| 507 | /* job management: */ | 509 | /* job management: */ |
| 508 | static int checkjobs(struct pipe* fg_pipe); | 510 | static int checkjobs(struct pipe* fg_pipe); |
| 509 | #if ENABLE_HUSH_JOB | 511 | #if ENABLE_HUSH_JOB |
| @@ -515,9 +517,11 @@ static void delete_finished_bg_job(struct pipe *pi); | |||
| 515 | int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ | 517 | int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ |
| 516 | #endif | 518 | #endif |
| 517 | /* local variable support */ | 519 | /* local variable support */ |
| 518 | static char **expand_variables_to_list(char **argv); | 520 | static char **expand_strvec_to_strvec(char **argv); |
| 521 | /* used for eval */ | ||
| 522 | static char *expand_strvec_to_string(char **argv); | ||
| 519 | /* used for expansion of right hand of assignments */ | 523 | /* used for expansion of right hand of assignments */ |
| 520 | static char *expand_variables_to_string(const char *str); | 524 | static char *expand_string_to_string(const char *str); |
| 521 | static const char *get_local_var(const char *var); | 525 | static const char *get_local_var(const char *var); |
| 522 | static int set_local_var(const char *s, int flg_export); | 526 | static int set_local_var(const char *s, int flg_export); |
| 523 | static void unset_local_var(const char *name); | 527 | static void unset_local_var(const char *name); |
| @@ -716,12 +720,11 @@ static const char *set_cwd(void) | |||
| 716 | /* built-in 'eval' handler */ | 720 | /* built-in 'eval' handler */ |
| 717 | static int builtin_eval(char **argv) | 721 | static int builtin_eval(char **argv) |
| 718 | { | 722 | { |
| 719 | char *str = NULL; | ||
| 720 | int rcode = EXIT_SUCCESS; | 723 | int rcode = EXIT_SUCCESS; |
| 721 | 724 | ||
| 722 | if (argv[1]) { | 725 | if (argv[1]) { |
| 723 | str = make_string(argv + 1); | 726 | char *str = expand_strvec_to_string(argv + 1); |
| 724 | parse_string_outer(str, PARSEFLAG_EXIT_FROM_LOOP | | 727 | parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP | |
| 725 | PARSEFLAG_SEMICOLON); | 728 | PARSEFLAG_SEMICOLON); |
| 726 | free(str); | 729 | free(str); |
| 727 | rcode = last_return_code; | 730 | rcode = last_return_code; |
| @@ -732,9 +735,9 @@ static int builtin_eval(char **argv) | |||
| 732 | /* built-in 'cd <path>' handler */ | 735 | /* built-in 'cd <path>' handler */ |
| 733 | static int builtin_cd(char **argv) | 736 | static int builtin_cd(char **argv) |
| 734 | { | 737 | { |
| 735 | char *newdir; | 738 | const char *newdir; |
| 736 | if (argv[1] == NULL) | 739 | if (argv[1] == NULL) |
| 737 | newdir = getenv("HOME"); | 740 | newdir = getenv("HOME") ? : "/"; |
| 738 | else | 741 | else |
| 739 | newdir = argv[1]; | 742 | newdir = argv[1]; |
| 740 | if (chdir(newdir)) { | 743 | if (chdir(newdir)) { |
| @@ -999,7 +1002,7 @@ static int builtin_source(char **argv) | |||
| 999 | * (pointer only is OK!) on this stack frame, | 1002 | * (pointer only is OK!) on this stack frame, |
| 1000 | * set global_argv=argv+1, recurse, and restore. */ | 1003 | * set global_argv=argv+1, recurse, and restore. */ |
| 1001 | mark_open(fileno(input)); | 1004 | mark_open(fileno(input)); |
| 1002 | status = parse_file_outer(input); | 1005 | status = parse_and_run_file(input); |
| 1003 | mark_closed(fileno(input)); | 1006 | mark_closed(fileno(input)); |
| 1004 | fclose(input); | 1007 | fclose(input); |
| 1005 | return status; | 1008 | return status; |
| @@ -1146,12 +1149,10 @@ static void get_user_input(struct in_str *i) | |||
| 1146 | 1149 | ||
| 1147 | prompt_str = setup_prompt_string(i->promptmode); | 1150 | prompt_str = setup_prompt_string(i->promptmode); |
| 1148 | #if ENABLE_FEATURE_EDITING | 1151 | #if ENABLE_FEATURE_EDITING |
| 1149 | /* | 1152 | /* Enable command line editing only while a command line |
| 1150 | ** enable command line editing only while a command line | 1153 | * is actually being read; otherwise, we'll end up bequeathing |
| 1151 | ** is actually being read; otherwise, we'll end up bequeathing | 1154 | * atexit() handlers and other unwanted stuff to our |
| 1152 | ** atexit() handlers and other unwanted stuff to our | 1155 | * child processes (rob@sysgo.de) */ |
| 1153 | ** child processes (rob@sysgo.de) | ||
| 1154 | */ | ||
| 1155 | r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state); | 1156 | r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state); |
| 1156 | i->eof_flag = (r < 0); | 1157 | i->eof_flag = (r < 0); |
| 1157 | if (i->eof_flag) { /* EOF/error detected */ | 1158 | if (i->eof_flag) { /* EOF/error detected */ |
| @@ -1343,8 +1344,8 @@ static void pseudo_exec_argv(char **argv) | |||
| 1343 | debug_printf_exec("pid %d environment modification: %s\n", | 1344 | debug_printf_exec("pid %d environment modification: %s\n", |
| 1344 | getpid(), argv[i]); | 1345 | getpid(), argv[i]); |
| 1345 | // FIXME: vfork case?? | 1346 | // FIXME: vfork case?? |
| 1346 | p = expand_variables_to_string(argv[i]); | 1347 | p = expand_string_to_string(argv[i]); |
| 1347 | putenv(p == argv[i] ? xstrdup(p) : p); | 1348 | putenv(p); |
| 1348 | } | 1349 | } |
| 1349 | argv += i; | 1350 | argv += i; |
| 1350 | /* If a variable is assigned in a forest, and nobody listens, | 1351 | /* If a variable is assigned in a forest, and nobody listens, |
| @@ -1354,7 +1355,7 @@ static void pseudo_exec_argv(char **argv) | |||
| 1354 | _exit(EXIT_SUCCESS); | 1355 | _exit(EXIT_SUCCESS); |
| 1355 | } | 1356 | } |
| 1356 | 1357 | ||
| 1357 | argv = expand_variables_to_list(argv); | 1358 | argv = expand_strvec_to_strvec(argv); |
| 1358 | 1359 | ||
| 1359 | /* | 1360 | /* |
| 1360 | * Check if the command matches any of the builtins. | 1361 | * Check if the command matches any of the builtins. |
| @@ -1652,8 +1653,8 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
| 1652 | pid_t p; | 1653 | pid_t p; |
| 1653 | int rcode = checkjobs(fg_pipe); | 1654 | int rcode = checkjobs(fg_pipe); |
| 1654 | /* Job finished, move the shell to the foreground */ | 1655 | /* Job finished, move the shell to the foreground */ |
| 1655 | p = getpgid(0); | 1656 | p = getpgid(0); /* pgid of our process */ |
| 1656 | debug_printf("fg'ing ourself: getpgid(0)=%d\n", (int)p); | 1657 | debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); |
| 1657 | if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY) | 1658 | if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY) |
| 1658 | bb_perror_msg("tcsetpgrp-4a"); | 1659 | bb_perror_msg("tcsetpgrp-4a"); |
| 1659 | return rcode; | 1660 | return rcode; |
| @@ -1675,6 +1676,9 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
| 1675 | * subshell, when that is in fact necessary. The subshell process | 1676 | * subshell, when that is in fact necessary. The subshell process |
| 1676 | * now has its stdout directed to the input of the appropriate pipe, | 1677 | * now has its stdout directed to the input of the appropriate pipe, |
| 1677 | * so this routine is noticeably simpler. | 1678 | * so this routine is noticeably simpler. |
| 1679 | * | ||
| 1680 | * Returns -1 only if started some children. IOW: we have to | ||
| 1681 | * mask out retvals of builtins etc with 0xff! | ||
| 1678 | */ | 1682 | */ |
| 1679 | static int run_pipe_real(struct pipe *pi) | 1683 | static int run_pipe_real(struct pipe *pi) |
| 1680 | { | 1684 | { |
| @@ -1710,7 +1714,7 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1710 | rcode = run_list_real(child->group); | 1714 | rcode = run_list_real(child->group); |
| 1711 | restore_redirects(squirrel); | 1715 | restore_redirects(squirrel); |
| 1712 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1716 | debug_printf_exec("run_pipe_real return %d\n", rcode); |
| 1713 | return rcode; | 1717 | return rcode; // do we need to add '... & 0xff' ? |
| 1714 | } | 1718 | } |
| 1715 | 1719 | ||
| 1716 | if (single_fg && child->argv != NULL) { | 1720 | if (single_fg && child->argv != NULL) { |
| @@ -1739,21 +1743,16 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1739 | export_me = 1; | 1743 | export_me = 1; |
| 1740 | } | 1744 | } |
| 1741 | free(name); | 1745 | free(name); |
| 1742 | p = expand_variables_to_string(argv[i]); | 1746 | p = expand_string_to_string(argv[i]); |
| 1743 | set_local_var(p, export_me); | 1747 | set_local_var(p, export_me); |
| 1744 | if (p != argv[i]) | 1748 | free(p); |
| 1745 | free(p); | ||
| 1746 | } | 1749 | } |
| 1747 | return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ | 1750 | return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ |
| 1748 | } | 1751 | } |
| 1749 | for (i = 0; is_assignment(argv[i]); i++) { | 1752 | for (i = 0; is_assignment(argv[i]); i++) { |
| 1750 | p = expand_variables_to_string(argv[i]); | 1753 | p = expand_string_to_string(argv[i]); |
| 1751 | if (p != argv[i]) { | 1754 | //sp: child->sp--; |
| 1752 | //sp: child->sp--; | 1755 | putenv(p); |
| 1753 | putenv(p); | ||
| 1754 | } else { | ||
| 1755 | putenv(xstrdup(p)); | ||
| 1756 | } | ||
| 1757 | } | 1756 | } |
| 1758 | for (x = bltins; x->cmd; x++) { | 1757 | for (x = bltins; x->cmd; x++) { |
| 1759 | if (strcmp(argv[i], x->cmd) == 0) { | 1758 | if (strcmp(argv[i], x->cmd) == 0) { |
| @@ -1770,8 +1769,8 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1770 | setup_redirects(child, squirrel); | 1769 | setup_redirects(child, squirrel); |
| 1771 | debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]); | 1770 | debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]); |
| 1772 | //sp: if (child->sp) /* btw we can do it unconditionally... */ | 1771 | //sp: if (child->sp) /* btw we can do it unconditionally... */ |
| 1773 | argv_expanded = expand_variables_to_list(argv + i); | 1772 | argv_expanded = expand_strvec_to_strvec(argv + i); |
| 1774 | rcode = x->function(argv_expanded); | 1773 | rcode = x->function(argv_expanded) & 0xff; |
| 1775 | free(argv_expanded); | 1774 | free(argv_expanded); |
| 1776 | restore_redirects(squirrel); | 1775 | restore_redirects(squirrel); |
| 1777 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1776 | debug_printf_exec("run_pipe_real return %d\n", rcode); |
| @@ -1786,9 +1785,9 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1786 | save_nofork_data(&nofork_save); | 1785 | save_nofork_data(&nofork_save); |
| 1787 | argv_expanded = argv + i; | 1786 | argv_expanded = argv + i; |
| 1788 | //sp: if (child->sp) | 1787 | //sp: if (child->sp) |
| 1789 | argv_expanded = expand_variables_to_list(argv + i); | 1788 | argv_expanded = expand_strvec_to_strvec(argv + i); |
| 1790 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); | 1789 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); |
| 1791 | rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded); | 1790 | rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff; |
| 1792 | free(argv_expanded); | 1791 | free(argv_expanded); |
| 1793 | restore_redirects(squirrel); | 1792 | restore_redirects(squirrel); |
| 1794 | debug_printf_exec("run_pipe_real return %d\n", rcode); | 1793 | debug_printf_exec("run_pipe_real return %d\n", rcode); |
| @@ -1832,7 +1831,7 @@ static int run_pipe_real(struct pipe *pi) | |||
| 1832 | /* Every child adds itself to new process group | 1831 | /* Every child adds itself to new process group |
| 1833 | * with pgid == pid of first child in pipe */ | 1832 | * with pgid == pid of first child in pipe */ |
| 1834 | #if ENABLE_HUSH_JOB | 1833 | #if ENABLE_HUSH_JOB |
| 1835 | if (interactive_fd) { | 1834 | if (run_list_level == 1 && interactive_fd) { |
| 1836 | /* Don't do pgrp restore anymore on fatal signals */ | 1835 | /* Don't do pgrp restore anymore on fatal signals */ |
| 1837 | set_fatal_sighandler(SIG_DFL); | 1836 | set_fatal_sighandler(SIG_DFL); |
| 1838 | if (pi->pgrp < 0) /* true for 1st process only */ | 1837 | if (pi->pgrp < 0) /* true for 1st process only */ |
| @@ -2078,7 +2077,7 @@ static int run_list_real(struct pipe *pi) | |||
| 2078 | if (!pi->next->progs->argv) | 2077 | if (!pi->next->progs->argv) |
| 2079 | continue; | 2078 | continue; |
| 2080 | /* create list of variable values */ | 2079 | /* create list of variable values */ |
| 2081 | for_list = expand_variables_to_list(pi->next->progs->argv); | 2080 | for_list = expand_strvec_to_strvec(pi->next->progs->argv); |
| 2082 | for_lcur = for_list; | 2081 | for_lcur = for_list; |
| 2083 | for_varname = pi->progs->argv[0]; | 2082 | for_varname = pi->progs->argv[0]; |
| 2084 | pi->progs->argv[0] = NULL; | 2083 | pi->progs->argv[0] = NULL; |
| @@ -2122,24 +2121,24 @@ static int run_list_real(struct pipe *pi) | |||
| 2122 | * of run_pipe_real(), and we don't need to wait for anything. */ | 2121 | * of run_pipe_real(), and we don't need to wait for anything. */ |
| 2123 | } else if (pi->followup == PIPE_BG) { | 2122 | } else if (pi->followup == PIPE_BG) { |
| 2124 | /* What does bash do with attempts to background builtins? */ | 2123 | /* What does bash do with attempts to background builtins? */ |
| 2125 | |||
| 2126 | /* Even bash 3.2 doesn't do that well with nested bg: | 2124 | /* Even bash 3.2 doesn't do that well with nested bg: |
| 2127 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". | 2125 | * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
| 2128 | * I'm considering NOT treating inner bgs as jobs - | 2126 | * I'm NOT treating inner &'s as jobs */ |
| 2129 | * thus maybe "if (run_list_level == 1 && pi->followup == PIPE_BG)" | ||
| 2130 | * above? */ | ||
| 2131 | #if ENABLE_HUSH_JOB | 2127 | #if ENABLE_HUSH_JOB |
| 2132 | insert_bg_job(pi); | 2128 | if (run_list_level == 1) |
| 2129 | insert_bg_job(pi); | ||
| 2133 | #endif | 2130 | #endif |
| 2134 | rcode = EXIT_SUCCESS; | 2131 | rcode = EXIT_SUCCESS; |
| 2135 | } else { | 2132 | } else { |
| 2136 | #if ENABLE_HUSH_JOB | 2133 | #if ENABLE_HUSH_JOB |
| 2137 | /* Paranoia, just "interactive_fd" should be enough */ | 2134 | /* Paranoia, just "interactive_fd" should be enough? */ |
| 2138 | if (run_list_level == 1 && interactive_fd) { | 2135 | if (run_list_level == 1 && interactive_fd) { |
| 2136 | /* waits for completion, then fg's main shell */ | ||
| 2139 | rcode = checkjobs_and_fg_shell(pi); | 2137 | rcode = checkjobs_and_fg_shell(pi); |
| 2140 | } else | 2138 | } else |
| 2141 | #endif | 2139 | #endif |
| 2142 | { | 2140 | { |
| 2141 | /* this one just waits for completion */ | ||
| 2143 | rcode = checkjobs(pi); | 2142 | rcode = checkjobs(pi); |
| 2144 | } | 2143 | } |
| 2145 | debug_printf_exec(": checkjobs returned %d\n", rcode); | 2144 | debug_printf_exec(": checkjobs returned %d\n", rcode); |
| @@ -2343,7 +2342,7 @@ static int xglob(o_string *dest, int flags, glob_t *pglob) | |||
| 2343 | return gr; | 2342 | return gr; |
| 2344 | } | 2343 | } |
| 2345 | 2344 | ||
| 2346 | /* expand_variables_to_list() takes a list of strings, expands | 2345 | /* expand_strvec_to_strvec() takes a list of strings, expands |
| 2347 | * all variable references within and returns a pointer to | 2346 | * all variable references within and returns a pointer to |
| 2348 | * a list of expanded strings, possibly with larger number | 2347 | * a list of expanded strings, possibly with larger number |
| 2349 | * of strings. (Think VAR="a b"; echo $VAR). | 2348 | * of strings. (Think VAR="a b"; echo $VAR). |
| @@ -2629,29 +2628,51 @@ static char **expand_variables(char **argv, char or_mask) | |||
| 2629 | debug_printf_expand("used_space=%d\n", pos - (char*)list); | 2628 | debug_printf_expand("used_space=%d\n", pos - (char*)list); |
| 2630 | } | 2629 | } |
| 2631 | #endif | 2630 | #endif |
| 2632 | /* To be removed / made conditional later. */ | 2631 | if (ENABLE_HUSH_DEBUG) |
| 2633 | if (pos - (char*)list > len) | 2632 | if (pos - (char*)list > len) |
| 2634 | bb_error_msg_and_die("BUG in varexp"); | 2633 | bb_error_msg_and_die("BUG in varexp"); |
| 2635 | return list; | 2634 | return list; |
| 2636 | } | 2635 | } |
| 2637 | 2636 | ||
| 2638 | static char **expand_variables_to_list(char **argv) | 2637 | static char **expand_strvec_to_strvec(char **argv) |
| 2639 | { | 2638 | { |
| 2640 | return expand_variables(argv, 0); | 2639 | return expand_variables(argv, 0); |
| 2641 | } | 2640 | } |
| 2642 | 2641 | ||
| 2643 | static char *expand_variables_to_string(const char *str) | 2642 | static char *expand_string_to_string(const char *str) |
| 2644 | { | 2643 | { |
| 2645 | char *argv[2], **list; | 2644 | char *argv[2], **list; |
| 2646 | 2645 | ||
| 2647 | argv[0] = (char*)str; | 2646 | argv[0] = (char*)str; |
| 2648 | argv[1] = NULL; | 2647 | argv[1] = NULL; |
| 2649 | list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ | 2648 | list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ |
| 2650 | /* To be removed / made conditional later. */ | 2649 | if (ENABLE_HUSH_DEBUG) |
| 2651 | if (!list[0] || list[1]) | 2650 | if (!list[0] || list[1]) |
| 2652 | bb_error_msg_and_die("BUG in varexp"); | 2651 | bb_error_msg_and_die("BUG in varexp2"); |
| 2653 | /* actually, just move string 2*sizeof(char*) bytes back */ | 2652 | /* actually, just move string 2*sizeof(char*) bytes back */ |
| 2654 | strcpy((char*)list, list[0]); | 2653 | strcpy((char*)list, list[0]); |
| 2654 | debug_printf_expand("string_to_string='%s'\n", (char*)list); | ||
| 2655 | return (char*)list; | ||
| 2656 | } | ||
| 2657 | |||
| 2658 | static char* expand_strvec_to_string(char **argv) | ||
| 2659 | { | ||
| 2660 | char **list; | ||
| 2661 | |||
| 2662 | list = expand_variables(argv, 0x80); | ||
| 2663 | /* Convert all NULs to spaces */ | ||
| 2664 | if (list[0]) { | ||
| 2665 | int n = 1; | ||
| 2666 | while (list[n]) { | ||
| 2667 | if (ENABLE_HUSH_DEBUG) | ||
| 2668 | if (list[n-1] + strlen(list[n-1]) + 1 != list[n]) | ||
| 2669 | bb_error_msg_and_die("BUG in varexp3"); | ||
| 2670 | list[n][-1] = ' '; /* TODO: or to ifs[0]? */ | ||
| 2671 | n++; | ||
| 2672 | } | ||
| 2673 | } | ||
| 2674 | strcpy((char*)list, list[0]); | ||
| 2675 | debug_printf_expand("strvec_to_string='%s'\n", (char*)list); | ||
| 2655 | return (char*)list; | 2676 | return (char*)list; |
| 2656 | } | 2677 | } |
| 2657 | 2678 | ||
| @@ -2713,9 +2734,9 @@ static int set_local_var(const char *s, int flg_export) | |||
| 2713 | } | 2734 | } |
| 2714 | 2735 | ||
| 2715 | cur = xzalloc(sizeof(*cur)); | 2736 | cur = xzalloc(sizeof(*cur)); |
| 2737 | /*cur->next = 0;*/ | ||
| 2716 | cur->name = xstrdup(name); | 2738 | cur->name = xstrdup(name); |
| 2717 | cur->value = xstrdup(value); | 2739 | cur->value = xstrdup(value); |
| 2718 | /*cur->next = 0;*/ | ||
| 2719 | cur->flg_export = flg_export; | 2740 | cur->flg_export = flg_export; |
| 2720 | /*cur->flg_read_only = 0;*/ | 2741 | /*cur->flg_read_only = 0;*/ |
| 2721 | { | 2742 | { |
| @@ -3242,31 +3263,6 @@ static const char *lookup_param(const char *src) | |||
| 3242 | return p; | 3263 | return p; |
| 3243 | } | 3264 | } |
| 3244 | 3265 | ||
| 3245 | /* Make new string for parser */ | ||
| 3246 | static char* make_string(char **inp) | ||
| 3247 | { | ||
| 3248 | char *p; | ||
| 3249 | char *str = NULL; | ||
| 3250 | int n; | ||
| 3251 | int val_len; | ||
| 3252 | int len = 0; | ||
| 3253 | |||
| 3254 | for (n = 0; inp[n]; n++) { | ||
| 3255 | p = expand_variables_to_string(inp[n]); | ||
| 3256 | val_len = strlen(p); | ||
| 3257 | str = xrealloc(str, len + val_len + 3); /* +3: space, '\n', <nul>*/ | ||
| 3258 | str[len++] = ' '; | ||
| 3259 | strcpy(str + len, p); | ||
| 3260 | len += val_len; | ||
| 3261 | if (p != inp[n]) free(p); | ||
| 3262 | } | ||
| 3263 | /* We do not check for case where loop had no iterations at all | ||
| 3264 | * - cannot happen? */ | ||
| 3265 | str[len] = '\n'; | ||
| 3266 | str[len+1] = '\0'; | ||
| 3267 | return str; | ||
| 3268 | } | ||
| 3269 | |||
| 3270 | /* return code: 0 for OK, 1 for syntax error */ | 3266 | /* return code: 0 for OK, 1 for syntax error */ |
| 3271 | static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) | 3267 | static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) |
| 3272 | { | 3268 | { |
| @@ -3358,9 +3354,9 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
| 3358 | debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger); | 3354 | debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger); |
| 3359 | 3355 | ||
| 3360 | while (1) { | 3356 | while (1) { |
| 3361 | ch = b_getch(input); | ||
| 3362 | m = CHAR_IFS; | 3357 | m = CHAR_IFS; |
| 3363 | next = '\0'; | 3358 | next = '\0'; |
| 3359 | ch = b_getch(input); | ||
| 3364 | if (ch != EOF) { | 3360 | if (ch != EOF) { |
| 3365 | m = charmap[ch]; | 3361 | m = charmap[ch]; |
| 3366 | if (ch != '\n') | 3362 | if (ch != '\n') |
| @@ -3371,6 +3367,11 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
| 3371 | if (m == CHAR_ORDINARY | 3367 | if (m == CHAR_ORDINARY |
| 3372 | || (m != CHAR_SPECIAL && dest->quote) | 3368 | || (m != CHAR_SPECIAL && dest->quote) |
| 3373 | ) { | 3369 | ) { |
| 3370 | if (ch == EOF) { | ||
| 3371 | syntax(); | ||
| 3372 | debug_printf_parse("parse_stream return 1: unterminated \"\n"); | ||
| 3373 | return 1; | ||
| 3374 | } | ||
| 3374 | b_addqchr(dest, ch, dest->quote); | 3375 | b_addqchr(dest, ch, dest->quote); |
| 3375 | continue; | 3376 | continue; |
| 3376 | } | 3377 | } |
| @@ -3564,8 +3565,8 @@ static void update_charmap(void) | |||
| 3564 | } | 3565 | } |
| 3565 | 3566 | ||
| 3566 | /* most recursion does not come through here, the exception is | 3567 | /* most recursion does not come through here, the exception is |
| 3567 | * from builtin_source() */ | 3568 | * from builtin_source() and builtin_eval() */ |
| 3568 | static int parse_stream_outer(struct in_str *inp, int parse_flag) | 3569 | static int parse_and_run_stream(struct in_str *inp, int parse_flag) |
| 3569 | { | 3570 | { |
| 3570 | struct p_context ctx; | 3571 | struct p_context ctx; |
| 3571 | o_string temp = NULL_O_STRING; | 3572 | o_string temp = NULL_O_STRING; |
| @@ -3607,19 +3608,19 @@ static int parse_stream_outer(struct in_str *inp, int parse_flag) | |||
| 3607 | return 0; | 3608 | return 0; |
| 3608 | } | 3609 | } |
| 3609 | 3610 | ||
| 3610 | static int parse_string_outer(const char *s, int parse_flag) | 3611 | static int parse_and_run_string(const char *s, int parse_flag) |
| 3611 | { | 3612 | { |
| 3612 | struct in_str input; | 3613 | struct in_str input; |
| 3613 | setup_string_in_str(&input, s); | 3614 | setup_string_in_str(&input, s); |
| 3614 | return parse_stream_outer(&input, parse_flag); | 3615 | return parse_and_run_stream(&input, parse_flag); |
| 3615 | } | 3616 | } |
| 3616 | 3617 | ||
| 3617 | static int parse_file_outer(FILE *f) | 3618 | static int parse_and_run_file(FILE *f) |
| 3618 | { | 3619 | { |
| 3619 | int rcode; | 3620 | int rcode; |
| 3620 | struct in_str input; | 3621 | struct in_str input; |
| 3621 | setup_file_in_str(&input, f); | 3622 | setup_file_in_str(&input, f); |
| 3622 | rcode = parse_stream_outer(&input, PARSEFLAG_SEMICOLON); | 3623 | rcode = parse_and_run_stream(&input, PARSEFLAG_SEMICOLON); |
| 3623 | return rcode; | 3624 | return rcode; |
| 3624 | } | 3625 | } |
| 3625 | 3626 | ||
| @@ -3698,7 +3699,7 @@ int hush_main(int argc, char **argv) | |||
| 3698 | input = fopen("/etc/profile", "r"); | 3699 | input = fopen("/etc/profile", "r"); |
| 3699 | if (input != NULL) { | 3700 | if (input != NULL) { |
| 3700 | mark_open(fileno(input)); | 3701 | mark_open(fileno(input)); |
| 3701 | parse_file_outer(input); | 3702 | parse_and_run_file(input); |
| 3702 | mark_closed(fileno(input)); | 3703 | mark_closed(fileno(input)); |
| 3703 | fclose(input); | 3704 | fclose(input); |
| 3704 | } | 3705 | } |
| @@ -3710,7 +3711,7 @@ int hush_main(int argc, char **argv) | |||
| 3710 | case 'c': | 3711 | case 'c': |
| 3711 | global_argv = argv + optind; | 3712 | global_argv = argv + optind; |
| 3712 | global_argc = argc - optind; | 3713 | global_argc = argc - optind; |
| 3713 | opt = parse_string_outer(optarg, PARSEFLAG_SEMICOLON); | 3714 | opt = parse_and_run_string(optarg, PARSEFLAG_SEMICOLON); |
| 3714 | goto final_return; | 3715 | goto final_return; |
| 3715 | case 'i': | 3716 | case 'i': |
| 3716 | /* Well, we cannot just declare interactiveness, | 3717 | /* Well, we cannot just declare interactiveness, |
| @@ -3791,7 +3792,7 @@ int hush_main(int argc, char **argv) | |||
| 3791 | #endif | 3792 | #endif |
| 3792 | 3793 | ||
| 3793 | if (argv[optind] == NULL) { | 3794 | if (argv[optind] == NULL) { |
| 3794 | opt = parse_file_outer(stdin); | 3795 | opt = parse_and_run_file(stdin); |
| 3795 | goto final_return; | 3796 | goto final_return; |
| 3796 | } | 3797 | } |
| 3797 | 3798 | ||
| @@ -3799,7 +3800,7 @@ int hush_main(int argc, char **argv) | |||
| 3799 | global_argv = argv + optind; | 3800 | global_argv = argv + optind; |
| 3800 | global_argc = argc - optind; | 3801 | global_argc = argc - optind; |
| 3801 | input = xfopen(argv[optind], "r"); | 3802 | input = xfopen(argv[optind], "r"); |
| 3802 | opt = parse_file_outer(input); | 3803 | opt = parse_and_run_file(input); |
| 3803 | 3804 | ||
| 3804 | #if ENABLE_FEATURE_CLEAN_UP | 3805 | #if ENABLE_FEATURE_CLEAN_UP |
| 3805 | fclose(input); | 3806 | fclose(input); |
diff --git a/shell/hush_test/hush-bugs/noeol3.right b/shell/hush_test/hush-bugs/noeol3.right new file mode 100644 index 000000000..a0ce47f30 --- /dev/null +++ b/shell/hush_test/hush-bugs/noeol3.right | |||
| @@ -0,0 +1 @@ | |||
| hush: syntax error hush.c:3370 | |||
diff --git a/shell/hush_test/hush-bugs/noeol3.tests b/shell/hush_test/hush-bugs/noeol3.tests new file mode 100755 index 000000000..ec958ed7a --- /dev/null +++ b/shell/hush_test/hush-bugs/noeol3.tests | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | # last line has no EOL! | ||
| 2 | echo "unterminated \ No newline at end of file | ||
