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 | |
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
-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 | ||