aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-09 19:16:15 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-09 19:16:15 +0000
commit05d3b7cc0de2283f149b07196f1ca0557c062323 (patch)
tree4bd8a4d447086c3aebc675400d3925806adf2520
parent1943aec2ec390d9fda159aa0412362780ec83f09 (diff)
downloadbusybox-w32-05d3b7cc0de2283f149b07196f1ca0557c062323.tar.gz
busybox-w32-05d3b7cc0de2283f149b07196f1ca0557c062323.tar.bz2
busybox-w32-05d3b7cc0de2283f149b07196f1ca0557c062323.zip
hush: deal with some easier TODOs
function old new delta is_well_formed_var_name - 87 +87 builtin_read 49 86 +37 die_if_script - 31 +31 syntax_error_unterminated - 28 +28 syntax_error 26 51 +25 done_word 768 788 +20 syntax_error_at - 12 +12 parse_stream_dquoted 320 328 +8 expand_variables 2064 2063 -1 run_list 1225 1220 -5 add_till_closing_paren 308 303 -5 add_till_backquote 111 106 -5 handle_dollar 812 803 -9 parse_stream 2378 2356 -22 parse_redirect 408 372 -36 maybe_die 44 - -44 is_assignment 215 134 -81 ------------------------------------------------------------------------------ (add/remove: 4/1 grow/shrink: 4/8 up/down: 248/-208) Total: 40 bytes
-rw-r--r--shell/hush.c289
-rw-r--r--shell/hush_test/hush-arith/arith.right54
-rw-r--r--shell/hush_test/hush-vars/param_expand_assign.right8
-rw-r--r--shell/hush_test/hush-vars/param_expand_indicate_error.right10
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_indicate_error.tests10
5 files changed, 214 insertions, 157 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 4c2716ddd..41d65ff69 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -65,8 +65,6 @@
65 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 65 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
66 */ 66 */
67#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ 67#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
68//TODO: pull in some .h and find out whether we have SINGLE_APPLET_MAIN?
69//#include "applet_tables.h" doesn't work
70#include <glob.h> 68#include <glob.h>
71/* #include <dmalloc.h> */ 69/* #include <dmalloc.h> */
72#if ENABLE_HUSH_CASE 70#if ENABLE_HUSH_CASE
@@ -80,11 +78,18 @@
80 78
81 79
82/* Debug build knobs */ 80/* Debug build knobs */
83//#define LEAK_HUNTING 1 81#define LEAK_HUNTING 0
84//#define WANT_TO_TEST_NOMMU 1 82#define BUILD_AS_NOMMU 0
83/* Enable/disable sanity checks. Ok to enable in production,
84 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
85 * Keeping 1 for now even in released versions.
86 */
87#define HUSH_DEBUG 1
88/* In progress... */
89#define ENABLE_HUSH_FUNCTIONS 0
85 90
86 91
87#ifdef WANT_TO_TEST_NOMMU 92#if BUILD_AS_NOMMU
88# undef BB_MMU 93# undef BB_MMU
89# undef USE_FOR_NOMMU 94# undef USE_FOR_NOMMU
90# undef USE_FOR_MMU 95# undef USE_FOR_MMU
@@ -122,15 +127,6 @@
122#define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__ 127#define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
123#endif 128#endif
124 129
125/* Enable/disable sanity checks. Ok to enable in production,
126 * only adds a bit of bloat.
127 * Keeping unconditionally on for now.
128 */
129#define HUSH_DEBUG 1
130/* In progress... */
131#define ENABLE_HUSH_FUNCTIONS 0
132
133
134/* If you comment out one of these below, it will be #defined later 130/* If you comment out one of these below, it will be #defined later
135 * to perform debug printfs to stderr: */ 131 * to perform debug printfs to stderr: */
136#define debug_printf(...) do {} while (0) 132#define debug_printf(...) do {} while (0)
@@ -216,40 +212,6 @@ static void debug_print_strings(const char *prefix, char **vv)
216#define debug_print_strings(prefix, vv) ((void)0) 212#define debug_print_strings(prefix, vv) ((void)0)
217#endif 213#endif
218 214
219/*
220 * Leak hunting. Use hush_leaktool.sh for post-processing.
221 */
222#ifdef LEAK_HUNTING
223static void *xxmalloc(int lineno, size_t size)
224{
225 void *ptr = xmalloc((size + 0xff) & ~0xff);
226 fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
227 return ptr;
228}
229static void *xxrealloc(int lineno, void *ptr, size_t size)
230{
231 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
232 fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
233 return ptr;
234}
235static char *xxstrdup(int lineno, const char *str)
236{
237 char *ptr = xstrdup(str);
238 fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
239 return ptr;
240}
241static void xxfree(void *ptr)
242{
243 fdprintf(2, "free %p\n", ptr);
244 free(ptr);
245}
246#define xmalloc(s) xxmalloc(__LINE__, s)
247#define xrealloc(p, s) xxrealloc(__LINE__, p, s)
248#define xstrdup(s) xxstrdup(__LINE__, s)
249#define free(p) xxfree(p)
250#endif
251
252
253#define ERR_PTR ((void*)(long)1) 215#define ERR_PTR ((void*)(long)1)
254 216
255#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" 217#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
@@ -662,28 +624,100 @@ static const struct built_in_command bltins[] = {
662}; 624};
663 625
664 626
665static void maybe_die(const char *notice, const char *msg) 627/* Leak hunting. Use hush_leaktool.sh for post-processing.
628 */
629#if LEAK_HUNTING
630static void *xxmalloc(int lineno, size_t size)
631{
632 void *ptr = xmalloc((size + 0xff) & ~0xff);
633 fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
634 return ptr;
635}
636static void *xxrealloc(int lineno, void *ptr, size_t size)
637{
638 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
639 fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
640 return ptr;
641}
642static char *xxstrdup(int lineno, const char *str)
643{
644 char *ptr = xstrdup(str);
645 fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
646 return ptr;
647}
648static void xxfree(void *ptr)
649{
650 fdprintf(2, "free %p\n", ptr);
651 free(ptr);
652}
653#define xmalloc(s) xxmalloc(__LINE__, s)
654#define xrealloc(p, s) xxrealloc(__LINE__, p, s)
655#define xstrdup(s) xxstrdup(__LINE__, s)
656#define free(p) xxfree(p)
657#endif
658
659
660/* Syntax and runtime errors. They always abort scripts.
661 * In interactive use they usually discard unparsed and/or unexecuted commands
662 * and return to the prompt.
663 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
664 */
665#if HUSH_DEBUG < 2
666# define die_if_script(lineno, fmt, msg) die_if_script(fmt, msg)
667# define syntax_error(lineno, msg) syntax_error(msg)
668# define syntax_error_at(lineno, msg) syntax_error_at(msg)
669# define syntax_error_unterminated(lineno, ch) syntax_error_unterminated(ch)
670#endif
671
672static void die_if_script(unsigned lineno, const char *fmt, const char *msg)
666{ 673{
667 /* Was using fancy stuff:
668 * (G_interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...)
669 * but it SEGVs. ?! Oh well... explicit temp ptr works around that */
670 void FAST_FUNC (*fp)(const char *s, ...) = bb_error_msg_and_die; 674 void FAST_FUNC (*fp)(const char *s, ...) = bb_error_msg_and_die;
671#if ENABLE_HUSH_INTERACTIVE 675#if ENABLE_HUSH_INTERACTIVE
672 if (G_interactive_fd) 676 if (G_interactive_fd)
673 fp = bb_error_msg; 677 fp = bb_error_msg;
674#endif 678#endif
675 fp(msg ? "%s: %s" : notice, notice, msg); 679#if HUSH_DEBUG >= 2
680 bb_error_msg("hush.c:%u", lineno);
681#endif
682 fp(fmt, msg);
683}
684
685static void syntax_error(unsigned lineno, const char *msg)
686{
687 if (msg)
688 die_if_script(lineno, "syntax error: %s", msg);
689 else
690 die_if_script(lineno, "syntax error", NULL);
691}
692
693static void syntax_error_at(unsigned lineno, const char *msg)
694{
695 die_if_script(lineno, "syntax error at '%s'", msg);
696}
697
698static void syntax_error_unterminated(unsigned lineno, char ch)
699{
700 char msg[2];
701 msg[0] = ch;
702 msg[1] = '\0';
703 die_if_script(lineno, "syntax error: unterminated %s", msg);
676} 704}
677#if 1 705
678#define syntax(msg) maybe_die("syntax error", msg); 706#if HUSH_DEBUG < 2
707# undef die_if_script
708# undef syntax_error
709# undef syntax_error_at
710# undef syntax_error_unterminated
679#else 711#else
680/* Debug -- trick gcc to expand __LINE__ and convert to string */ 712# define die_if_script(fmt, msg) die_if_script(__LINE__, fmt, msg)
681#define __syntax(msg, line) maybe_die("syntax error hush.c:" # line, msg) 713# define syntax_error(msg) syntax_error(__LINE__, msg)
682#define _syntax(msg, line) __syntax(msg, line) 714# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
683#define syntax(msg) _syntax(msg, __LINE__) 715# define syntax_error_unterminated(ch) syntax_error_unterminated(__LINE__, ch)
684#endif 716#endif
685 717
686 718
719/* Utility functions
720 */
687static int glob_needed(const char *s) 721static int glob_needed(const char *s)
688{ 722{
689 while (*s) { 723 while (*s) {
@@ -696,14 +730,14 @@ static int glob_needed(const char *s)
696 return 0; 730 return 0;
697} 731}
698 732
699static int is_assignment(const char *s) 733static int is_well_formed_var_name(const char *s, char terminator)
700{ 734{
701 if (!s || !(isalpha(*s) || *s == '_')) 735 if (!s || !(isalpha(*s) || *s == '_'))
702 return 0; 736 return 0;
703 s++; 737 s++;
704 while (isalnum(*s) || *s == '_') 738 while (isalnum(*s) || *s == '_')
705 s++; 739 s++;
706 return *s == '='; 740 return *s == terminator;
707} 741}
708 742
709/* Replace each \x with x in place, return ptr past NUL. */ 743/* Replace each \x with x in place, return ptr past NUL. */
@@ -747,7 +781,7 @@ static char **add_strings_to_strings(char **strings, char **add, int need_to_dup
747 v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]); 781 v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
748 return v; 782 return v;
749} 783}
750#ifdef LEAK_HUNTING 784#if LEAK_HUNTING
751static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup) 785static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
752{ 786{
753 char **ptr = add_strings_to_strings(strings, add, need_to_dup); 787 char **ptr = add_strings_to_strings(strings, add, need_to_dup);
@@ -765,7 +799,7 @@ static char **add_string_to_strings(char **strings, char *add)
765 v[1] = NULL; 799 v[1] = NULL;
766 return add_strings_to_strings(strings, v, /*dup:*/ 0); 800 return add_strings_to_strings(strings, v, /*dup:*/ 0);
767} 801}
768#ifdef LEAK_HUNTING 802#if LEAK_HUNTING
769static char **xx_add_string_to_strings(int lineno, char **strings, char *add) 803static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
770{ 804{
771 char **ptr = add_string_to_strings(strings, add); 805 char **ptr = add_string_to_strings(strings, add);
@@ -1911,12 +1945,19 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1911 free(exp_str); 1945 free(exp_str);
1912 1946
1913 if (errcode < 0) { 1947 if (errcode < 0) {
1948 const char *msg = "error in arithmetic";
1914 switch (errcode) { 1949 switch (errcode) {
1915 case -3: maybe_die("arith", "exponent less than 0"); break; 1950 case -3:
1916 case -2: maybe_die("arith", "divide by zero"); break; 1951 msg = "exponent less than 0";
1917 case -5: maybe_die("arith", "expression recursion loop detected"); break; 1952 break;
1918 default: maybe_die("arith", "syntax error"); break; 1953 case -2:
1954 msg = "divide by 0";
1955 break;
1956 case -5:
1957 msg = "expression recursion loop detected";
1958 break;
1919 } 1959 }
1960 die_if_script(msg, NULL);
1920 } 1961 }
1921 debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res); 1962 debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
1922 sprintf(arith_buf, arith_t_fmt, res); 1963 sprintf(arith_buf, arith_t_fmt, res);
@@ -1951,7 +1992,8 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1951 exp_save = var[exp_off]; 1992 exp_save = var[exp_off];
1952 exp_null = exp_save == ':'; 1993 exp_null = exp_save == ':';
1953 exp_word = var + exp_off; 1994 exp_word = var + exp_off;
1954 if (exp_null) ++exp_word; 1995 if (exp_null)
1996 ++exp_word;
1955 exp_op = *exp_word++; 1997 exp_op = *exp_word++;
1956 var[exp_off] = '\0'; 1998 var[exp_off] = '\0';
1957 } 1999 }
@@ -1995,17 +2037,28 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1995 exp_null ? "true" : "false", exp_test); 2037 exp_null ? "true" : "false", exp_test);
1996 if (exp_test) { 2038 if (exp_test) {
1997 if (exp_op == '?') 2039 if (exp_op == '?')
1998 maybe_die(var, *exp_word ? exp_word : "parameter null or not set"); 2040//TODO: what does interactive bash
2041 /* ${var?[error_msg_if_unset]} */
2042 /* ${var:?[error_msg_if_unset_or_null]} */
2043 /* mimic bash message */
2044 if (*exp_word) {
2045 char *msg = xasprintf("%s: %s", var, exp_word);
2046 die_if_script("%s", msg);
2047 free(msg);
2048 } else {
2049 die_if_script("%s: parameter null or not set", var);
2050 }
1999 else 2051 else
2000 val = exp_word; 2052 val = exp_word;
2001 2053
2002 if (exp_op == '=') { 2054 if (exp_op == '=') {
2055 /* ${var=[word]} or ${var:=[word]} */
2003 if (isdigit(var[0]) || var[0] == '#') { 2056 if (isdigit(var[0]) || var[0] == '#') {
2004 maybe_die(var, "special vars cannot assign in this way"); 2057 /* mimic bash message */
2058 die_if_script("$%s: cannot assign in this way", var);
2005 val = NULL; 2059 val = NULL;
2006 } else { 2060 } else {
2007 char *new_var = xmalloc(strlen(var) + strlen(val) + 2); 2061 char *new_var = xasprintf("%s=%s", var, val);
2008 sprintf(new_var, "%s=%s", var, val);
2009 set_local_var(new_var, -1, 0); 2062 set_local_var(new_var, -1, 0);
2010 } 2063 }
2011 } 2064 }
@@ -3280,7 +3333,7 @@ static int run_list(struct pipe *pi)
3280 continue; 3333 continue;
3281 /* current word is FOR or IN (BOLD in comments below) */ 3334 /* current word is FOR or IN (BOLD in comments below) */
3282 if (cpipe->next == NULL) { 3335 if (cpipe->next == NULL) {
3283 syntax("malformed for"); 3336 syntax_error("malformed for");
3284 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); 3337 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
3285 return 1; 3338 return 1;
3286 } 3339 }
@@ -3291,7 +3344,7 @@ static int run_list(struct pipe *pi)
3291 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ 3344 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
3292 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ 3345 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
3293 ) { 3346 ) {
3294 syntax("malformed for"); 3347 syntax_error("malformed for");
3295 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); 3348 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
3296 return 1; 3349 return 1;
3297 } 3350 }
@@ -3443,7 +3496,7 @@ static int run_list(struct pipe *pi)
3443 break; 3496 break;
3444 } 3497 }
3445 /* Insert next value from for_lcur */ 3498 /* Insert next value from for_lcur */
3446//TODO: does it need escaping? 3499 /* note: *for_lcur already has quotes removed, $var expanded, etc */
3447 pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); 3500 pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
3448 pi->cmds[0].assignment_cnt = 1; 3501 pi->cmds[0].assignment_cnt = 1;
3449 } 3502 }
@@ -3846,7 +3899,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
3846#endif 3899#endif
3847 if (r->flag == 0) { /* '!' */ 3900 if (r->flag == 0) { /* '!' */
3848 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ 3901 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
3849 syntax("! ! command"); 3902 syntax_error("! ! command");
3850 IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) 3903 IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;)
3851 } 3904 }
3852 ctx->ctx_inverted = 1; 3905 ctx->ctx_inverted = 1;
@@ -3860,7 +3913,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
3860 initialize_context(ctx); 3913 initialize_context(ctx);
3861 ctx->stack = old; 3914 ctx->stack = old;
3862 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { 3915 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
3863 syntax(word->data); 3916 syntax_error_at(word->data);
3864 ctx->ctx_res_w = RES_SNTX; 3917 ctx->ctx_res_w = RES_SNTX;
3865 return 1; 3918 return 1;
3866 } 3919 }
@@ -3943,7 +3996,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
3943 * while if false; then false; fi; do; break; done 3996 * while if false; then false; fi; do; break; done
3944 * TODO? */ 3997 * TODO? */
3945 if (command->group) { 3998 if (command->group) {
3946 syntax(word->data); 3999 syntax_error_at(word->data);
3947 debug_printf_parse("done_word return 1: syntax error, " 4000 debug_printf_parse("done_word return 1: syntax error, "
3948 "groups and arglists don't mix\n"); 4001 "groups and arglists don't mix\n");
3949 return 1; 4002 return 1;
@@ -4008,7 +4061,10 @@ static int done_word(o_string *word, struct parse_context *ctx)
4008 * as it is "for v; in ...". FOR and IN become two pipe structs 4061 * as it is "for v; in ...". FOR and IN become two pipe structs
4009 * in parse tree. */ 4062 * in parse tree. */
4010 if (ctx->ctx_res_w == RES_FOR) { 4063 if (ctx->ctx_res_w == RES_FOR) {
4011//TODO: check that command->argv[0] is a valid variable name! 4064 if (!is_well_formed_var_name(command->argv[0], '\0')) {
4065 syntax_error("malformed variable name in for");
4066 return 1;
4067 }
4012 done_pipe(ctx, PIPE_SEQ); 4068 done_pipe(ctx, PIPE_SEQ);
4013 } 4069 }
4014#endif 4070#endif
@@ -4091,12 +4147,6 @@ static int parse_redirect(struct parse_context *ctx,
4091 nommu_addchr(&ctx->as_string, ch); 4147 nommu_addchr(&ctx->as_string, ch);
4092 ch = i_peek(input); 4148 ch = i_peek(input);
4093 } 4149 }
4094 /* <<[-] word is the same as <<[-]word */
4095 while (ch == ' ' || ch == '\t') {
4096 ch = i_getch(input);
4097 nommu_addchr(&ctx->as_string, ch);
4098 ch = i_peek(input);
4099 }
4100 } 4150 }
4101 4151
4102 if (style == REDIRECT_OVERWRITE && dup_num == -1) { 4152 if (style == REDIRECT_OVERWRITE && dup_num == -1) {
@@ -4222,7 +4272,7 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
4222{ 4272{
4223 struct pipe *pi = ctx->list_head; 4273 struct pipe *pi = ctx->list_head;
4224 4274
4225 while (pi) { 4275 while (pi && heredoc_cnt) {
4226 int i; 4276 int i;
4227 struct command *cmd = pi->cmds; 4277 struct command *cmd = pi->cmds;
4228 4278
@@ -4238,16 +4288,12 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
4238 if (redir->rd_type == REDIRECT_HEREDOC) { 4288 if (redir->rd_type == REDIRECT_HEREDOC) {
4239 char *p; 4289 char *p;
4240 4290
4241 if (heredoc_cnt <= 0) {
4242 syntax("heredoc BUG 1");
4243 return 1; /* error */
4244 }
4245 redir->rd_type = REDIRECT_HEREDOC2; 4291 redir->rd_type = REDIRECT_HEREDOC2;
4246 /* redir->dup is (ab)used to indicate <<- */ 4292 /* redir->dup is (ab)used to indicate <<- */
4247 p = fetch_till_str(&ctx->as_string, input, 4293 p = fetch_till_str(&ctx->as_string, input,
4248 redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS); 4294 redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS);
4249 if (!p) { 4295 if (!p) {
4250 syntax("unexpected EOF in here document"); 4296 syntax_error("unexpected EOF in here document");
4251 return 1; 4297 return 1;
4252 } 4298 }
4253 free(redir->rd_filename); 4299 free(redir->rd_filename);
@@ -4260,10 +4306,12 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
4260 } 4306 }
4261 pi = pi->next; 4307 pi = pi->next;
4262 } 4308 }
4309#if 0
4263 /* Should be 0. If it isn't, it's a parse error */ 4310 /* Should be 0. If it isn't, it's a parse error */
4264 if (heredoc_cnt) 4311 if (heredoc_cnt)
4265 syntax("heredoc BUG 2"); 4312 bb_error_msg_and_die("heredoc BUG 2");
4266 return heredoc_cnt; 4313#endif
4314 return 0;
4267} 4315}
4268 4316
4269 4317
@@ -4388,7 +4436,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4388 || dest->length /* word(... */ 4436 || dest->length /* word(... */
4389 || dest->o_quoted /* ""(... */ 4437 || dest->o_quoted /* ""(... */
4390 ) { 4438 ) {
4391 syntax(NULL); 4439 syntax_error(NULL);
4392 debug_printf_parse("parse_group return 1: " 4440 debug_printf_parse("parse_group return 1: "
4393 "syntax error, groups and arglists don't mix\n"); 4441 "syntax error, groups and arglists don't mix\n");
4394 return 1; 4442 return 1;
@@ -4412,7 +4460,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4412#if !BB_MMU 4460#if !BB_MMU
4413 free(as_string); 4461 free(as_string);
4414#endif 4462#endif
4415 syntax(NULL); 4463 syntax_error(NULL);
4416 debug_printf_parse("parse_group return 1: " 4464 debug_printf_parse("parse_group return 1: "
4417 "parse_stream returned %p\n", pipe_list); 4465 "parse_stream returned %p\n", pipe_list);
4418 return 1; 4466 return 1;
@@ -4439,7 +4487,7 @@ static int add_till_single_quote(o_string *dest, struct in_str *input)
4439 while (1) { 4487 while (1) {
4440 int ch = i_getch(input); 4488 int ch = i_getch(input);
4441 if (ch == EOF) { 4489 if (ch == EOF) {
4442 syntax("unterminated '"); 4490 syntax_error_unterminated('\'');
4443 return 1; 4491 return 1;
4444 } 4492 }
4445 if (ch == '\'') 4493 if (ch == '\'')
@@ -4453,7 +4501,7 @@ static int add_till_double_quote(o_string *dest, struct in_str *input)
4453 while (1) { 4501 while (1) {
4454 int ch = i_getch(input); 4502 int ch = i_getch(input);
4455 if (ch == EOF) { 4503 if (ch == EOF) {
4456 syntax("unterminated \""); 4504 syntax_error_unterminated('"');
4457 return 1; 4505 return 1;
4458 } 4506 }
4459 if (ch == '"') 4507 if (ch == '"')
@@ -4491,7 +4539,7 @@ static int add_till_backquote(o_string *dest, struct in_str *input)
4491 while (1) { 4539 while (1) {
4492 int ch = i_getch(input); 4540 int ch = i_getch(input);
4493 if (ch == EOF) { 4541 if (ch == EOF) {
4494 syntax("unterminated `"); 4542 syntax_error_unterminated('`');
4495 return 1; 4543 return 1;
4496 } 4544 }
4497 if (ch == '`') 4545 if (ch == '`')
@@ -4500,7 +4548,7 @@ static int add_till_backquote(o_string *dest, struct in_str *input)
4500 /* \x. Copy both chars unless it is \` */ 4548 /* \x. Copy both chars unless it is \` */
4501 int ch2 = i_getch(input); 4549 int ch2 = i_getch(input);
4502 if (ch2 == EOF) { 4550 if (ch2 == EOF) {
4503 syntax("unterminated `"); 4551 syntax_error_unterminated('`');
4504 return 1; 4552 return 1;
4505 } 4553 }
4506 if (ch2 != '`' && ch2 != '$' && ch2 != '\\') 4554 if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
@@ -4528,7 +4576,7 @@ static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl
4528 while (1) { 4576 while (1) {
4529 int ch = i_getch(input); 4577 int ch = i_getch(input);
4530 if (ch == EOF) { 4578 if (ch == EOF) {
4531 syntax("unterminated )"); 4579 syntax_error_unterminated(')');
4532 return 1; 4580 return 1;
4533 } 4581 }
4534 if (ch == '(') 4582 if (ch == '(')
@@ -4560,7 +4608,7 @@ static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl
4560 /* \x. Copy verbatim. Important for \(, \) */ 4608 /* \x. Copy verbatim. Important for \(, \) */
4561 ch = i_getch(input); 4609 ch = i_getch(input);
4562 if (ch == EOF) { 4610 if (ch == EOF) {
4563 syntax("unterminated )"); 4611 syntax_error_unterminated(')');
4564 return 1; 4612 return 1;
4565 } 4613 }
4566 o_addchr(dest, ch); 4614 o_addchr(dest, ch);
@@ -4678,7 +4726,7 @@ static int handle_dollar(o_string *as_string,
4678 break; 4726 break;
4679 default: 4727 default:
4680 case_default: 4728 case_default:
4681 syntax("unterminated ${name}"); 4729 syntax_error("unterminated ${name}");
4682 debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); 4730 debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
4683 return 1; 4731 return 1;
4684 } 4732 }
@@ -4783,7 +4831,7 @@ static int parse_stream_dquoted(o_string *as_string,
4783 } 4831 }
4784 /* note: can't move it above ch == dquote_end check! */ 4832 /* note: can't move it above ch == dquote_end check! */
4785 if (ch == EOF) { 4833 if (ch == EOF) {
4786 syntax("unterminated \""); 4834 syntax_error_unterminated('"');
4787 debug_printf_parse("parse_stream_dquoted return 1: unterminated \"\n"); 4835 debug_printf_parse("parse_stream_dquoted return 1: unterminated \"\n");
4788 return 1; 4836 return 1;
4789 } 4837 }
@@ -4794,8 +4842,9 @@ static int parse_stream_dquoted(o_string *as_string,
4794 debug_printf_parse(": ch=%c (%d) escape=%d\n", 4842 debug_printf_parse(": ch=%c (%d) escape=%d\n",
4795 ch, ch, dest->o_escape); 4843 ch, ch, dest->o_escape);
4796 if (ch == '\\') { 4844 if (ch == '\\') {
4845//TODO: check interactive behavior
4797 if (next == EOF) { 4846 if (next == EOF) {
4798 syntax("\\<eof>"); 4847 syntax_error("\\<eof>");
4799 debug_printf_parse("parse_stream_dquoted return 1: \\<eof>\n"); 4848 debug_printf_parse("parse_stream_dquoted return 1: \\<eof>\n");
4800 return 1; 4849 return 1;
4801 } 4850 }
@@ -4839,7 +4888,7 @@ static int parse_stream_dquoted(o_string *as_string,
4839 if (ch == '=' 4888 if (ch == '='
4840 && (dest->o_assignment == MAYBE_ASSIGNMENT 4889 && (dest->o_assignment == MAYBE_ASSIGNMENT
4841 || dest->o_assignment == WORD_IS_KEYWORD) 4890 || dest->o_assignment == WORD_IS_KEYWORD)
4842 && is_assignment(dest->data) 4891 && is_well_formed_var_name(dest->data, '=')
4843 ) { 4892 ) {
4844 dest->o_assignment = DEFINITELY_ASSIGNMENT; 4893 dest->o_assignment = DEFINITELY_ASSIGNMENT;
4845 } 4894 }
@@ -4905,7 +4954,7 @@ static struct pipe *parse_stream(char **pstring,
4905 struct pipe *pi; 4954 struct pipe *pi;
4906 4955
4907 if (heredoc_cnt) { 4956 if (heredoc_cnt) {
4908 syntax("unterminated here document"); 4957 syntax_error("unterminated here document");
4909 goto parse_error; 4958 goto parse_error;
4910 } 4959 }
4911 if (done_word(&dest, &ctx)) { 4960 if (done_word(&dest, &ctx)) {
@@ -4943,7 +4992,7 @@ static struct pipe *parse_stream(char **pstring,
4943 if ((dest.o_assignment == MAYBE_ASSIGNMENT 4992 if ((dest.o_assignment == MAYBE_ASSIGNMENT
4944 || dest.o_assignment == WORD_IS_KEYWORD) 4993 || dest.o_assignment == WORD_IS_KEYWORD)
4945 && ch == '=' 4994 && ch == '='
4946 && is_assignment(dest.data) 4995 && is_well_formed_var_name(dest.data, '=')
4947 ) { 4996 ) {
4948 dest.o_assignment = DEFINITELY_ASSIGNMENT; 4997 dest.o_assignment = DEFINITELY_ASSIGNMENT;
4949 } 4998 }
@@ -4994,7 +5043,7 @@ static struct pipe *parse_stream(char **pstring,
4994 * We require heredoc to be in enclosing {}/(), 5043 * We require heredoc to be in enclosing {}/(),
4995 * if any. 5044 * if any.
4996 */ 5045 */
4997 syntax("unterminated here document"); 5046 syntax_error("unterminated here document");
4998 goto parse_error; 5047 goto parse_error;
4999 } 5048 }
5000 if (done_word(&dest, &ctx)) { 5049 if (done_word(&dest, &ctx)) {
@@ -5051,7 +5100,7 @@ static struct pipe *parse_stream(char **pstring,
5051 break; 5100 break;
5052 case '\\': 5101 case '\\':
5053 if (next == EOF) { 5102 if (next == EOF) {
5054 syntax("\\<eof>"); 5103 syntax_error("\\<eof>");
5055 goto parse_error; 5104 goto parse_error;
5056 } 5105 }
5057 o_addchr(&dest, '\\'); 5106 o_addchr(&dest, '\\');
@@ -5074,7 +5123,7 @@ static struct pipe *parse_stream(char **pstring,
5074 while (1) { 5123 while (1) {
5075 ch = i_getch(input); 5124 ch = i_getch(input);
5076 if (ch == EOF) { 5125 if (ch == EOF) {
5077 syntax("unterminated '"); 5126 syntax_error_unterminated('\'');
5078 goto parse_error; 5127 goto parse_error;
5079 } 5128 }
5080 nommu_addchr(&ctx.as_string, ch); 5129 nommu_addchr(&ctx.as_string, ch);
@@ -5126,7 +5175,7 @@ static struct pipe *parse_stream(char **pstring,
5126 } 5175 }
5127#if 0 5176#if 0
5128 else if (next == '(') { 5177 else if (next == '(') {
5129 syntax(">(process) not supported"); 5178 syntax_error(">(process) not supported");
5130 goto parse_error; 5179 goto parse_error;
5131 } 5180 }
5132#endif 5181#endif
@@ -5152,7 +5201,7 @@ static struct pipe *parse_stream(char **pstring,
5152 } 5201 }
5153#if 0 5202#if 0
5154 else if (next == '(') { 5203 else if (next == '(') {
5155 syntax("<(process) not supported"); 5204 syntax_error("<(process) not supported");
5156 goto parse_error; 5205 goto parse_error;
5157 } 5206 }
5158#endif 5207#endif
@@ -5246,7 +5295,7 @@ static struct pipe *parse_stream(char **pstring,
5246 ch = i_getch(input); 5295 ch = i_getch(input);
5247 } while (ch == ' ' || ch == '\n'); 5296 } while (ch == ' ' || ch == '\n');
5248 if (ch != '{') { 5297 if (ch != '{') {
5249 syntax("was expecting {"); 5298 syntax_error("was expecting {");
5250 goto parse_error; 5299 goto parse_error;
5251 } 5300 }
5252 ch = 'F'; /* magic value */ 5301 ch = 'F'; /* magic value */
@@ -5266,7 +5315,7 @@ static struct pipe *parse_stream(char **pstring,
5266 /* proper use of this character is caught by end_trigger: 5315 /* proper use of this character is caught by end_trigger:
5267 * if we see {, we call parse_group(..., end_trigger='}') 5316 * if we see {, we call parse_group(..., end_trigger='}')
5268 * and it will match } earlier (not here). */ 5317 * and it will match } earlier (not here). */
5269 syntax("unexpected } or )"); 5318 syntax_error("unexpected } or )");
5270 goto parse_error; 5319 goto parse_error;
5271 default: 5320 default:
5272 if (HUSH_DEBUG) 5321 if (HUSH_DEBUG)
@@ -6111,8 +6160,16 @@ static int builtin_pwd(char **argv UNUSED_PARAM)
6111static int builtin_read(char **argv) 6160static int builtin_read(char **argv)
6112{ 6161{
6113 char *string; 6162 char *string;
6114 const char *name = argv[1] ? argv[1] : "REPLY"; 6163 const char *name = "REPLY";
6115//TODO: check that argv[1] is a valid variable name 6164
6165 if (argv[1]) {
6166 name = argv[1];
6167 if (!is_well_formed_var_name(name, '\0')) {
6168 /* Mimic bash message */
6169 bb_error_msg("read: '%s': not a valid identifier", name);
6170 return 1;
6171 }
6172 }
6116 6173
6117 string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL); 6174 string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL);
6118 return set_local_var(string, 0, 0); 6175 return set_local_var(string, 0, 0);
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right
index a35fe893f..8cde0ee53 100644
--- a/shell/hush_test/hush-arith/arith.right
+++ b/shell/hush_test/hush-arith/arith.right
@@ -55,28 +55,28 @@ Format: 'expected actual'
5530 30 5530 30
5620 20 5620 20
5730 30 5730 30
58hush: arith: syntax error 58hush: error in arithmetic
596 6 596 6
606,5,3 6,5,3 606,5,3 6,5,3
61263 263 61263 263
62255 255 62255 255
6340 40 6340 40
64hush: arith: syntax error 64hush: error in arithmetic
65hush: arith: divide by zero 65hush: divide by 0
66hush: can't exec 'let': No such file or directory 66hush: can't exec 'let': No such file or directory
67hush: arith: syntax error 67hush: error in arithmetic
68hush: can't exec 'let': No such file or directory 68hush: can't exec 'let': No such file or directory
69abc 69abc
70def 70def
71ghi 71ghi
72hush: arith: syntax error 72hush: error in arithmetic
7316 16 7316 16
74hush: arith: syntax error 74hush: error in arithmetic
75hush: arith: syntax error 75hush: error in arithmetic
76hush: arith: syntax error 76hush: error in arithmetic
779 9 779 9
78hush: arith: syntax error 78hush: error in arithmetic
79hush: arith: syntax error 79hush: error in arithmetic
809 9 809 9
819 9 819 9
829 9 829 9
@@ -97,18 +97,18 @@ hush: arith: syntax error
973 3 973 3
984 4 984 4
994 4 994 4
100hush: arith: syntax error 100hush: error in arithmetic
101hush: arith: syntax error 101hush: error in arithmetic
102hush: arith: syntax error 102hush: error in arithmetic
103hush: arith: syntax error 103hush: error in arithmetic
104hush: arith: syntax error 104hush: error in arithmetic
1054 4 1054 4
1067 7 1067 7
107-7 -7 107-7 -7
108hush: arith: syntax error 108hush: error in arithmetic
109hush: arith: syntax error 109hush: error in arithmetic
110hush: arith: syntax error 110hush: error in arithmetic
111hush: arith: syntax error 111hush: error in arithmetic
1126 6 1126 6
1133 3 1133 3
1147 7 1147 7
@@ -119,19 +119,19 @@ hush: arith: syntax error
1192 2 1192 2
120-2 -2 120-2 -2
1211 1 1211 1
122hush: arith: syntax error 122hush: error in arithmetic
123hush: arith: syntax error 123hush: error in arithmetic
124hush: arith: syntax error 124hush: error in arithmetic
125hush: arith: syntax error 125hush: error in arithmetic
126hush: arith: syntax error 126hush: error in arithmetic
1275 5 1275 5
1281 1 1281 1
1294 4 1294 4
1300 0 1300 0
131hush: arith: syntax error 131hush: error in arithmetic
132hush: arith: syntax error 132hush: error in arithmetic
1338 12 1338 12
134hush: arith: syntax error 134hush: error in arithmetic
13542 13542
13642 13642
13742 13742
diff --git a/shell/hush_test/hush-vars/param_expand_assign.right b/shell/hush_test/hush-vars/param_expand_assign.right
index fff4ead33..d5b258073 100644
--- a/shell/hush_test/hush-vars/param_expand_assign.right
+++ b/shell/hush_test/hush-vars/param_expand_assign.right
@@ -2,10 +2,10 @@ hush: syntax error: unterminated ${name}
2hush: syntax error: unterminated ${name} 2hush: syntax error: unterminated ${name}
30 30
40 40
5hush: 1: special vars cannot assign in this way 5hush: $1: cannot assign in this way
6hush: 1: special vars cannot assign in this way 6hush: $1: cannot assign in this way
7hush: 1: special vars cannot assign in this way 7hush: $1: cannot assign in this way
8hush: 1: special vars cannot assign in this way 8hush: $1: cannot assign in this way
9_aa 9_aa
10_aa 10_aa
11_aa 11_aa
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.right b/shell/hush_test/hush-vars/param_expand_indicate_error.right
index f440f6fae..ec4908c35 100644
--- a/shell/hush_test/hush-vars/param_expand_indicate_error.right
+++ b/shell/hush_test/hush-vars/param_expand_indicate_error.right
@@ -5,8 +5,8 @@ hush: syntax error: unterminated ${name}
5_ 5_
6hush: 1: parameter null or not set 6hush: 1: parameter null or not set
7hush: 1: parameter null or not set 7hush: 1: parameter null or not set
8hush: 1: word 8hush: 1: message1
9hush: 1: word 9hush: 1: message1
10_aaaa 10_aaaa
11_aaaa 11_aaaa
12_aaaa 12_aaaa
@@ -15,13 +15,13 @@ _aaaa
15_ 15_
16hush: f: parameter null or not set 16hush: f: parameter null or not set
17hush: f: parameter null or not set 17hush: f: parameter null or not set
18hush: f: word 18hush: f: message3
19hush: f: word 19hush: f: message3
20_ 20_
21_ 21_
22hush: f: parameter null or not set 22hush: f: parameter null or not set
23_ 23_
24hush: f: word 24hush: f: message4
25_fff 25_fff
26_fff 26_fff
27_fff 27_fff
diff --git a/shell/hush_test/hush-vars/param_expand_indicate_error.tests b/shell/hush_test/hush-vars/param_expand_indicate_error.tests
index 77834fedd..1f94181a9 100755
--- a/shell/hush_test/hush-vars/param_expand_indicate_error.tests
+++ b/shell/hush_test/hush-vars/param_expand_indicate_error.tests
@@ -12,8 +12,8 @@
12"$THIS_SH" -c 'set --; echo _$1' 12"$THIS_SH" -c 'set --; echo _$1'
13"$THIS_SH" -c 'set --; echo _${1?}' 13"$THIS_SH" -c 'set --; echo _${1?}'
14"$THIS_SH" -c 'set --; echo _${1:?}' 14"$THIS_SH" -c 'set --; echo _${1:?}'
15"$THIS_SH" -c 'set --; echo _${1?word}' 15"$THIS_SH" -c 'set --; echo _${1?message1}'
16"$THIS_SH" -c 'set --; echo _${1:?word}' 16"$THIS_SH" -c 'set --; echo _${1:?message1}'
17 17
18"$THIS_SH" -c 'set -- aaaa; echo _$1' 18"$THIS_SH" -c 'set -- aaaa; echo _$1'
19"$THIS_SH" -c 'set -- aaaa; echo _${1?}' 19"$THIS_SH" -c 'set -- aaaa; echo _${1?}'
@@ -24,14 +24,14 @@
24"$THIS_SH" -c 'unset f; echo _$f' 24"$THIS_SH" -c 'unset f; echo _$f'
25"$THIS_SH" -c 'unset f; echo _${f?}' 25"$THIS_SH" -c 'unset f; echo _${f?}'
26"$THIS_SH" -c 'unset f; echo _${f:?}' 26"$THIS_SH" -c 'unset f; echo _${f:?}'
27"$THIS_SH" -c 'unset f; echo _${f?word}' 27"$THIS_SH" -c 'unset f; echo _${f?message3}'
28"$THIS_SH" -c 'unset f; echo _${f:?word}' 28"$THIS_SH" -c 'unset f; echo _${f:?message3}'
29 29
30"$THIS_SH" -c 'f=; echo _$f' 30"$THIS_SH" -c 'f=; echo _$f'
31"$THIS_SH" -c 'f=; echo _${f?}' 31"$THIS_SH" -c 'f=; echo _${f?}'
32"$THIS_SH" -c 'f=; echo _${f:?}' 32"$THIS_SH" -c 'f=; echo _${f:?}'
33"$THIS_SH" -c 'f=; echo _${f?word}' 33"$THIS_SH" -c 'f=; echo _${f?word}'
34"$THIS_SH" -c 'f=; echo _${f:?word}' 34"$THIS_SH" -c 'f=; echo _${f:?message4}'
35 35
36"$THIS_SH" -c 'f=fff; echo _$f' 36"$THIS_SH" -c 'f=fff; echo _$f'
37"$THIS_SH" -c 'f=fff; echo _${f?}' 37"$THIS_SH" -c 'f=fff; echo _${f?}'