aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-07-20 16:18:59 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-07-20 16:29:43 +0200
commit294eb4612cd668521faa48711297196f00af61d9 (patch)
tree15b002f51fdbfc3d32b1c33b8dd886d791f70cbc
parent57235beb696a7dbdb48751b9721c4c025127ae96 (diff)
downloadbusybox-w32-294eb4612cd668521faa48711297196f00af61d9.tar.gz
busybox-w32-294eb4612cd668521faa48711297196f00af61d9.tar.bz2
busybox-w32-294eb4612cd668521faa48711297196f00af61d9.zip
hush: fix word splitting in ${v:+ARG} - dollar_altvalue1 test
ash might be a bit buggy, need to investigate dollar_altvalue9 test function old new delta expand_one_var 1639 2236 +597 expand_variables 112 128 +16 expand_vars_to_list 1117 1097 -20 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 613/-20) Total: 593 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/ash_test/ash-quoting/dollar_altvalue9.right24
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_altvalue9.tests17
-rw-r--r--shell/hush.c263
-rw-r--r--shell/hush_test/hush-quoting/dollar_altvalue9.right24
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_altvalue9.tests17
5 files changed, 284 insertions, 61 deletions
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue9.right b/shell/ash_test/ash-quoting/dollar_altvalue9.right
new file mode 100644
index 000000000..fc6c2697c
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_altvalue9.right
@@ -0,0 +1,24 @@
1Unquoted 1:
2|a|
3|x y|
4|1|
5|2|
6|1 2|
7|A|
8|B|
9|C D|
10|zb|
11Quoted 1:
12|a 'x y' 1 2 '' 1 2 A B C D zb|
13Unquoted 2:
14|ax y|
15|1|
16|2|
17|1 2|
18|A|
19|B|
20|C D|
21|z|
22|b|
23Quoted 2:
24|a 'x y' 1 2 '' 1 2 A B C D z b|
diff --git a/shell/ash_test/ash-quoting/dollar_altvalue9.tests b/shell/ash_test/ash-quoting/dollar_altvalue9.tests
new file mode 100755
index 000000000..27a6f4f3c
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_altvalue9.tests
@@ -0,0 +1,17 @@
1f() { for i; do echo "|$i|"; done; }
2
3echo Unquoted 1:
4x='1 2'; f a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b
5echo Quoted 1:
6x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b"
7
8echo Unquoted 2:
9x='1 2'; f a${x:+'x y' $x '' "$x" `echo A B` "`echo C D`" z }b
10echo Quoted 2:
11x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z }b"
12
13#echo Unquoted 3:
14#e=
15#x='1 2'; f a${x:+'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b
16#echo Quoted 3:
17#x='1 2'; f "a${x:+ 'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b"
diff --git a/shell/hush.c b/shell/hush.c
index b8af1b088..fc77b89fc 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -5873,6 +5873,138 @@ static char *encode_then_expand_vararg(const char *str, int handle_squotes, int
5873 return exp_str; 5873 return exp_str;
5874} 5874}
5875 5875
5876static int expand_vars_to_list(o_string *output, int n, char *arg);
5877
5878static int encode_then_append_var_plusminus(o_string *output, int n,
5879 const char *str, int dquoted)
5880{
5881 struct in_str input;
5882 o_string dest = NULL_O_STRING;
5883
5884#if 0 //todo?
5885 const char *cp;
5886 cp = str;
5887 for (;;) {
5888 if (!*cp) return NULL; /* string has no special chars */
5889 if (*cp == '$') break;
5890 if (*cp == '\\') break;
5891 if (*cp == '\'') break;
5892 if (*cp == '"') break;
5893#if ENABLE_HUSH_TICK
5894 if (*cp == '`') break;
5895#endif
5896 cp++;
5897 }
5898#endif
5899
5900 /* Expanding ARG in ${var+ARG}, ${var-ARG} */
5901
5902 setup_string_in_str(&input, str);
5903
5904 for (;;) {
5905 int ch;
5906
5907 ch = i_getch(&input);
5908 debug_printf_parse("%s: ch=%c (%d) escape=%x\n",
5909 __func__, ch, ch, dest.o_expflags);
5910
5911 if (!dest.o_expflags) {
5912 if (ch == EOF)
5913 break;
5914 if (!dquoted && strchr(G.ifs, ch)) {
5915 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
5916 * do not assume we are at the start of the word (PREFIX above).
5917 */
5918 if (dest.data) {
5919 n = expand_vars_to_list(output, n, dest.data);
5920 o_free(&dest);
5921 o_addchr(output, '\0');
5922 n = o_save_ptr(output, n); /* create next word */
5923 } else
5924 if (output->length != o_get_last_ptr(output, n)
5925 || output->has_quoted_part
5926 ) {
5927 /* For these cases:
5928 * f() { for i; do echo "|$i|"; done; }; x=x
5929 * f a${x:+ }b # 1st condition
5930 * |a|
5931 * |b|
5932 * f ""${x:+ }b # 2nd condition
5933 * ||
5934 * |b|
5935 */
5936 o_addchr(output, '\0');
5937 n = o_save_ptr(output, n); /* create next word */
5938 }
5939 continue;
5940 }
5941 if (!dquoted && ch == '\'') {
5942//quoting version of add_till_single_quote() (try to merge?):
5943 for (;;) {
5944 ch = i_getch(&input);
5945 if (ch == EOF) {
5946 syntax_error_unterm_ch('\'');
5947 goto ret; /* error */
5948 }
5949 if (ch == '\'')
5950 break;
5951 o_addqchr(&dest, ch);
5952 }
5953 continue;
5954 }
5955 }
5956 if (ch == EOF) {
5957 syntax_error_unterm_ch('"');
5958 goto ret; /* error */
5959 }
5960 if (ch == '"') {
5961 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
5962 continue;
5963 }
5964 if (ch == '\\') {
5965 ch = i_getch(&input);
5966 if (ch == EOF) {
5967//example? error message? syntax_error_unterm_ch('"');
5968 debug_printf_parse("%s: error: \\<eof>\n", __func__);
5969 goto ret;
5970 }
5971 o_addqchr(&dest, ch);
5972 continue;
5973 }
5974 if (ch == '$') {
5975 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) {
5976 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
5977 goto ret;
5978 }
5979 continue;
5980 }
5981#if ENABLE_HUSH_TICK
5982 if (ch == '`') {
5983 //unsigned pos = dest->length;
5984 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
5985 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
5986 if (!add_till_backquote(&dest, &input,
5987 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
5988 )
5989 ) {
5990 goto ret; /* error */
5991 }
5992 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
5993 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5994 continue;
5995 }
5996#endif
5997 o_addQchr(&dest, ch);
5998 } /* for (;;) */
5999
6000 if (dest.data) {
6001 n = expand_vars_to_list(output, n, dest.data);
6002 }
6003 ret:
6004 o_free_unsafe(&dest);
6005 return n;
6006}
6007
5876#if ENABLE_FEATURE_SH_MATH 6008#if ENABLE_FEATURE_SH_MATH
5877static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) 6009static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
5878{ 6010{
@@ -6231,78 +6363,86 @@ static NOINLINE int expand_one_var(o_string *output,
6231 * 6363 *
6232 * Colon forms (${var:-word}, ${var:=word} etc) do the same, 6364 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
6233 * but also treat null var as if it is unset. 6365 * but also treat null var as if it is unset.
6366 *
6367 * Word-splitting and single quote behavior:
6368 *
6369 * $ f() { for i; do echo "|$i|"; done; };
6370 *
6371 * $ x=; f ${x:?'x y' z}
6372 * bash: x: x y z #BUG: does not abort, ${} results in empty expansion
6373 * $ x=; f "${x:?'x y' z}"
6374 * bash: x: x y z # dash prints: dash: x: 'x y' z #BUG: does not abort, ${} results in ""
6375 *
6376 * $ x=; f ${x:='x y' z}
6377 * |x|
6378 * |y|
6379 * |z|
6380 * $ x=; f "${x:='x y' z}"
6381 * |'x y' z|
6382 *
6383 * $ x=x; f ${x:+'x y' z}|
6384 * |x y|
6385 * |z|
6386 * $ x=x; f "${x:+'x y' z}"
6387 * |'x y' z|
6388 *
6389 * $ x=; f ${x:-'x y' z}
6390 * |x y|
6391 * |z|
6392 * $ x=; f "${x:-'x y' z}"
6393 * |'x y' z|
6234 */ 6394 */
6235/*
6236 * Word-splitting and squote behavior of bash:
6237 * $ f() { for i; do echo "|$i|"; done; };
6238 *
6239 * $ x=; f ${x:?'x y' z}
6240 * bash: x: x y z #BUG: does not abort, ${} results in empty expansion
6241 * $ x=; f "${x:?'x y' z}"
6242 * bash: x: x y z # dash prints: dash: x: 'x y' z #BUG: does not abort, ${} results in ""
6243 *
6244 * $ x=; f ${x:='x y' z}
6245 * |x|
6246 * |y|
6247 * |z|
6248 * $ x=; f "${x:='x y' z}"
6249 * |'x y' z|
6250 *
6251 * $ x=x; f ${x:+'x y' z}
6252 * |x y|
6253 * |z|
6254 * $ x=x; f "${x:+'x y' z}"
6255 * |'x y' z|
6256 *
6257 * $ x=; f ${x:-'x y' z}
6258 * |x y|
6259 * |z|
6260 * $ x=; f "${x:-'x y' z}"
6261 * |'x y' z|
6262 */
6263 int use_word = (!val || ((exp_save == ':') && !val[0])); 6395 int use_word = (!val || ((exp_save == ':') && !val[0]));
6264 if (exp_op == '+') 6396 if (exp_op == '+')
6265 use_word = !use_word; 6397 use_word = !use_word;
6266 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, 6398 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
6267 (exp_save == ':') ? "true" : "false", use_word); 6399 (exp_save == ':') ? "true" : "false", use_word);
6268 if (use_word) { 6400 if (use_word) {
6269//FIXME: unquoted ${x:+"b c" d} and ${x:+'b c' d} should expand to two words 6401 if (exp_op == '+' || exp_op == '-') {
6270//currently it expands to three. 6402 /* ${var+word} - use alternative value */
6271 to_be_freed = encode_then_expand_vararg(exp_word, 6403 /* ${var-word} - use default value */
6272 /*handle_squotes:*/ !(arg0 & 0x80), 6404 n = encode_then_append_var_plusminus(output, n, exp_word,
6273 /*unbackslash:*/ 0 6405 /*dquoted:*/ (arg0 & 0x80)
6274 );
6275 if (to_be_freed)
6276 exp_word = to_be_freed;
6277 if (exp_op == '?') {
6278 /* mimic bash message */
6279 msg_and_die_if_script("%s: %s",
6280 var,
6281 exp_word[0]
6282 ? exp_word
6283 : "parameter null or not set"
6284 /* ash has more specific messages, a-la: */
6285 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
6286 ); 6406 );
6407 val = NULL;
6408 } else {
6409 /* ${var?word} - indicate error if unset */
6410 /* ${var=word} - assign and use default value */
6411 to_be_freed = encode_then_expand_vararg(exp_word,
6412 /*handle_squotes:*/ !(arg0 & 0x80),
6413 /*unbackslash:*/ 0
6414 );
6415 if (to_be_freed)
6416 exp_word = to_be_freed;
6417 if (exp_op == '?') {
6418 /* mimic bash message */
6419 msg_and_die_if_script("%s: %s",
6420 var,
6421 exp_word[0]
6422 ? exp_word
6423 : "parameter null or not set"
6424 /* ash has more specific messages, a-la: */
6425 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
6426 );
6287//TODO: how interactive bash aborts expansion mid-command? 6427//TODO: how interactive bash aborts expansion mid-command?
6288//It aborts the entire line, returns to prompt: 6428//It aborts the entire line, returns to prompt:
6289// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO 6429// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO
6290// bash: x: x y z 6430// bash: x: x y z
6291// $ 6431// $
6292// ("echo YO" is not executed, neither the f function call) 6432// ("echo YO" is not executed, neither the f function call)
6293 } else {
6294 val = exp_word;
6295 }
6296
6297 if (exp_op == '=') {
6298 /* ${var=[word]} or ${var:=[word]} */
6299 if (isdigit(var[0]) || var[0] == '#') {
6300 /* mimic bash message */
6301 msg_and_die_if_script("$%s: cannot assign in this way", var);
6302 val = NULL;
6303 } else { 6433 } else {
6304 char *new_var = xasprintf("%s=%s", var, val); 6434 val = exp_word;
6305 set_local_var(new_var, /*flag:*/ 0); 6435 }
6436 if (exp_op == '=') {
6437 /* ${var=[word]} or ${var:=[word]} */
6438 if (isdigit(var[0]) || var[0] == '#') {
6439 /* mimic bash message */
6440 msg_and_die_if_script("$%s: cannot assign in this way", var);
6441 val = NULL;
6442 } else {
6443 char *new_var = xasprintf("%s=%s", var, val);
6444 set_local_var(new_var, /*flag:*/ 0);
6445 }
6306 } 6446 }
6307 } 6447 }
6308 } 6448 }
@@ -6482,8 +6622,9 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
6482 } 6622 }
6483 debug_print_list("expand_vars_to_list[a]", output, n); 6623 debug_print_list("expand_vars_to_list[a]", output, n);
6484 /* this part is literal, and it was already pre-quoted 6624 /* this part is literal, and it was already pre-quoted
6485 * if needed (much earlier), do not use o_addQstr here! */ 6625 * if needed (much earlier), do not use o_addQstr here!
6486 o_addstr_with_NUL(output, arg); 6626 */
6627 o_addstr(output, arg);
6487 debug_print_list("expand_vars_to_list[b]", output, n); 6628 debug_print_list("expand_vars_to_list[b]", output, n);
6488 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ 6629 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
6489 && !(cant_be_null & 0x80) /* and all vars were not quoted. */ 6630 && !(cant_be_null & 0x80) /* and all vars were not quoted. */
@@ -6491,8 +6632,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
6491 n--; 6632 n--;
6492 /* allow to reuse list[n] later without re-growth */ 6633 /* allow to reuse list[n] later without re-growth */
6493 output->has_empty_slot = 1; 6634 output->has_empty_slot = 1;
6494 } else {
6495 o_addchr(output, '\0');
6496 } 6635 }
6497 6636
6498 return n; 6637 return n;
@@ -6517,6 +6656,8 @@ static char **expand_variables(char **argv, unsigned expflags)
6517 6656
6518 /* expand argv[i] */ 6657 /* expand argv[i] */
6519 n = expand_vars_to_list(&output, n, *argv++); 6658 n = expand_vars_to_list(&output, n, *argv++);
6659 /* if (!output->has_empty_slot) -- need this?? */
6660 o_addchr(&output, '\0');
6520 } 6661 }
6521 debug_print_list("expand_variables", &output, n); 6662 debug_print_list("expand_variables", &output, n);
6522 6663
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue9.right b/shell/hush_test/hush-quoting/dollar_altvalue9.right
new file mode 100644
index 000000000..fc6c2697c
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_altvalue9.right
@@ -0,0 +1,24 @@
1Unquoted 1:
2|a|
3|x y|
4|1|
5|2|
6|1 2|
7|A|
8|B|
9|C D|
10|zb|
11Quoted 1:
12|a 'x y' 1 2 '' 1 2 A B C D zb|
13Unquoted 2:
14|ax y|
15|1|
16|2|
17|1 2|
18|A|
19|B|
20|C D|
21|z|
22|b|
23Quoted 2:
24|a 'x y' 1 2 '' 1 2 A B C D z b|
diff --git a/shell/hush_test/hush-quoting/dollar_altvalue9.tests b/shell/hush_test/hush-quoting/dollar_altvalue9.tests
new file mode 100755
index 000000000..27a6f4f3c
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_altvalue9.tests
@@ -0,0 +1,17 @@
1f() { for i; do echo "|$i|"; done; }
2
3echo Unquoted 1:
4x='1 2'; f a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b
5echo Quoted 1:
6x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z}b"
7
8echo Unquoted 2:
9x='1 2'; f a${x:+'x y' $x '' "$x" `echo A B` "`echo C D`" z }b
10echo Quoted 2:
11x='1 2'; f "a${x:+ 'x y' $x '' "$x" `echo A B` "`echo C D`" z }b"
12
13#echo Unquoted 3:
14#e=
15#x='1 2'; f a${x:+'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b
16#echo Quoted 3:
17#x='1 2'; f "a${x:+ 'x y' $x '' "$x" $e $e "$e" $e `echo A B` "`echo C D`" z }b"