diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-14 11:05:48 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-14 11:07:30 +0200 |
| commit | 46dccd2ec0eafd850b2168d4dfe4e74949fd3424 (patch) | |
| tree | cb853ae6459c7fd7c48fbcaa42d46056fe77c03f /shell | |
| parent | a02450ff0bfa45618e72fc7103ea3a8f0e7fff80 (diff) | |
| download | busybox-w32-46dccd2ec0eafd850b2168d4dfe4e74949fd3424.tar.gz busybox-w32-46dccd2ec0eafd850b2168d4dfe4e74949fd3424.tar.bz2 busybox-w32-46dccd2ec0eafd850b2168d4dfe4e74949fd3424.zip | |
shell/math: fix nested ?: and do not parse variables in not-taken branch
Fixes arith-ternary1.tests and arith-ternary_nested.tests
function old new delta
evaluate_string 1043 1101 +58
arith_apply 1087 1137 +50
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 108/0) Total: 108 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash_test/ash-arith/arith-ternary1.right | 3 | ||||
| -rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary1.tests | 7 | ||||
| -rw-r--r-- | shell/ash_test/ash-arith/arith-ternary2.right | 4 | ||||
| -rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary2.tests | 7 | ||||
| -rw-r--r-- | shell/ash_test/ash-arith/arith-ternary_nested.right | 1 | ||||
| -rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary_nested.tests | 2 | ||||
| -rw-r--r-- | shell/math.c | 80 |
7 files changed, 61 insertions, 43 deletions
diff --git a/shell/ash_test/ash-arith/arith-ternary1.right b/shell/ash_test/ash-arith/arith-ternary1.right index c968f1181..6b751d7b8 100644 --- a/shell/ash_test/ash-arith/arith-ternary1.right +++ b/shell/ash_test/ash-arith/arith-ternary1.right | |||
| @@ -1,5 +1,2 @@ | |||
| 1 | 42:42 | 1 | 42:42 |
| 2 | a=0 | 2 | a=0 |
| 3 | 6:6 | ||
| 4 | a=b=+err+ | ||
| 5 | b=6 | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary1.tests b/shell/ash_test/ash-arith/arith-ternary1.tests index 5a54e34b6..3532ce54d 100755 --- a/shell/ash_test/ash-arith/arith-ternary1.tests +++ b/shell/ash_test/ash-arith/arith-ternary1.tests | |||
| @@ -3,10 +3,3 @@ a=0 | |||
| 3 | # The not-taken branch should not evaluate | 3 | # The not-taken branch should not evaluate |
| 4 | echo 42:$((1 ? 42 : (a+=2))) | 4 | echo 42:$((1 ? 42 : (a+=2))) |
| 5 | echo "a=$a" | 5 | echo "a=$a" |
| 6 | |||
| 7 | a='b=+err+' | ||
| 8 | b=5 | ||
| 9 | # The not-taken branch should not even parse variables | ||
| 10 | echo 6:$((0 ? a : ++b)) | ||
| 11 | echo "a=$a" | ||
| 12 | echo "b=$b" | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary2.right b/shell/ash_test/ash-arith/arith-ternary2.right index aa54bd925..a549b1b5c 100644 --- a/shell/ash_test/ash-arith/arith-ternary2.right +++ b/shell/ash_test/ash-arith/arith-ternary2.right | |||
| @@ -1 +1,3 @@ | |||
| 1 | 5:5 | 1 | 6:6 |
| 2 | a=b=+err+ | ||
| 3 | b=6 | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary2.tests b/shell/ash_test/ash-arith/arith-ternary2.tests index eefc8e7ce..cb3163932 100755 --- a/shell/ash_test/ash-arith/arith-ternary2.tests +++ b/shell/ash_test/ash-arith/arith-ternary2.tests | |||
| @@ -1,2 +1,7 @@ | |||
| 1 | exec 2>&1 | 1 | exec 2>&1 |
| 2 | echo 5:$((1?2?3?4?5:6:7:8:9)) | 2 | a='b=+err+' |
| 3 | b=5 | ||
| 4 | # The not-taken branch should not parse variables | ||
| 5 | echo 6:$((0 ? a : ++b)) | ||
| 6 | echo "a=$a" | ||
| 7 | echo "b=$b" | ||
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested.right b/shell/ash_test/ash-arith/arith-ternary_nested.right new file mode 100644 index 000000000..aa54bd925 --- /dev/null +++ b/shell/ash_test/ash-arith/arith-ternary_nested.right | |||
| @@ -0,0 +1 @@ | |||
| 5:5 | |||
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested.tests b/shell/ash_test/ash-arith/arith-ternary_nested.tests new file mode 100755 index 000000000..eefc8e7ce --- /dev/null +++ b/shell/ash_test/ash-arith/arith-ternary_nested.tests | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | exec 2>&1 | ||
| 2 | echo 5:$((1?2?3?4?5:6:7:8:9)) | ||
diff --git a/shell/math.c b/shell/math.c index 9ca7c6bb1..2b7a3494f 100644 --- a/shell/math.c +++ b/shell/math.c | |||
| @@ -331,6 +331,28 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 331 | 331 | ||
| 332 | top_of_stack = NUMPTR - 1; | 332 | top_of_stack = NUMPTR - 1; |
| 333 | 333 | ||
| 334 | if (op == TOK_CONDITIONAL_SEP) { | ||
| 335 | /* "expr1 ? expr2 : expr3" operation */ | ||
| 336 | var_or_num_t *expr1 = &top_of_stack[-2]; | ||
| 337 | if (expr1 < numstack) { | ||
| 338 | return "malformed ?: operator"; | ||
| 339 | } | ||
| 340 | err = arith_lookup_val(math_state, expr1); | ||
| 341 | if (err) | ||
| 342 | return err; | ||
| 343 | if (expr1->val != 0) /* select expr2 or expr3 */ | ||
| 344 | top_of_stack--; | ||
| 345 | err = arith_lookup_val(math_state, top_of_stack); | ||
| 346 | if (err) | ||
| 347 | return err; | ||
| 348 | NUMPTR = expr1 + 1; | ||
| 349 | expr1->val = top_of_stack->val; | ||
| 350 | expr1->var_name = NULL; | ||
| 351 | return NULL; | ||
| 352 | } | ||
| 353 | if (op == TOK_CONDITIONAL) /* Example: $((a ? b)) */ | ||
| 354 | return "malformed ?: operator"; | ||
| 355 | |||
| 334 | /* Resolve name to value, if needed */ | 356 | /* Resolve name to value, if needed */ |
| 335 | err = arith_lookup_val(math_state, top_of_stack); | 357 | err = arith_lookup_val(math_state, top_of_stack); |
| 336 | if (err) | 358 | if (err) |
| @@ -350,25 +372,12 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 350 | else if (op != TOK_UPLUS) { | 372 | else if (op != TOK_UPLUS) { |
| 351 | /* Binary operators */ | 373 | /* Binary operators */ |
| 352 | arith_t right_side_val; | 374 | arith_t right_side_val; |
| 353 | int bad_second_val; | ||
| 354 | 375 | ||
| 355 | /* Binary operators need two arguments */ | 376 | /* Binary operators need two arguments */ |
| 356 | if (top_of_stack == numstack) | 377 | if (top_of_stack == numstack) |
| 357 | goto syntax_err; | 378 | goto syntax_err; |
| 358 | /* ...and they pop one */ | 379 | /* ...and they pop one */ |
| 359 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | 380 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ |
| 360 | |||
| 361 | bad_second_val = (top_of_stack->var_name == SECOND_VAL_VALID); | ||
| 362 | if (op == TOK_CONDITIONAL) { /* ? operation */ | ||
| 363 | /* Make next if (...) protect against | ||
| 364 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | ||
| 365 | bad_second_val = !bad_second_val; | ||
| 366 | } | ||
| 367 | if (bad_second_val) { | ||
| 368 | /* Protect against $((expr <not_?_op> expr1 : expr2)) */ | ||
| 369 | return "malformed ?: operator"; | ||
| 370 | } | ||
| 371 | |||
| 372 | top_of_stack--; /* now points to left side */ | 381 | top_of_stack--; /* now points to left side */ |
| 373 | 382 | ||
| 374 | if (op != TOK_ASSIGN) { | 383 | if (op != TOK_ASSIGN) { |
| @@ -380,19 +389,7 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 380 | 389 | ||
| 381 | right_side_val = rez; | 390 | right_side_val = rez; |
| 382 | rez = top_of_stack->val; | 391 | rez = top_of_stack->val; |
| 383 | if (op == TOK_CONDITIONAL) /* ? operation */ | 392 | if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
| 384 | rez = (rez ? right_side_val : top_of_stack[1].second_val); | ||
| 385 | else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ | ||
| 386 | if (top_of_stack == numstack) { | ||
| 387 | /* Protect against $((expr : expr)) */ | ||
| 388 | return "malformed ?: operator"; | ||
| 389 | } | ||
| 390 | top_of_stack->val = rez; | ||
| 391 | top_of_stack->second_val = right_side_val; | ||
| 392 | top_of_stack->var_name = SECOND_VAL_VALID; | ||
| 393 | return NULL; | ||
| 394 | } | ||
| 395 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) | ||
| 396 | rez |= right_side_val; | 393 | rez |= right_side_val; |
| 397 | else if (op == TOK_OR) | 394 | else if (op == TOK_OR) |
| 398 | rez = right_side_val || rez; | 395 | rez = right_side_val || rez; |
| @@ -833,7 +830,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 833 | lasttok = TOK_NUM; | 830 | lasttok = TOK_NUM; |
| 834 | goto next; | 831 | goto next; |
| 835 | } | 832 | } |
| 836 | /* Not (y), but ...x~y): fall through to evaluate x~y */ | 833 | /* Not (y), but ...x~y). Fall through to evaluate x~y */ |
| 837 | } else { | 834 | } else { |
| 838 | operator prev_prec = PREC(prev_op); | 835 | operator prev_prec = PREC(prev_op); |
| 839 | fix_assignment_prec(prec); | 836 | fix_assignment_prec(prec); |
| @@ -841,11 +838,17 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 841 | if (prev_prec < prec | 838 | if (prev_prec < prec |
| 842 | || (prev_prec == prec && is_right_associative(prec)) | 839 | || (prev_prec == prec && is_right_associative(prec)) |
| 843 | ) { | 840 | ) { |
| 844 | /* ...x~y@: push @ on opstack */ | 841 | /* Unless a?b?c:d:... and we are at the second : */ |
| 845 | opstackptr++; /* undo removal of ~ op */ | 842 | if (op != TOK_CONDITIONAL_SEP |
| 846 | goto push_op; | 843 | || prev_op != TOK_CONDITIONAL_SEP |
| 844 | ) { | ||
| 845 | /* ...x~y@: push @ on opstack */ | ||
| 846 | opstackptr++; /* undo removal of ~ op */ | ||
| 847 | goto push_op; | ||
| 848 | } | ||
| 849 | /* else: a?b?c:d:. Evaluate b?c:d, replace it on stack with result. Then repeat */ | ||
| 847 | } | 850 | } |
| 848 | /* ...x~y@: evaluate x~y, replace it on stack with result. Then repeat */ | 851 | /* else: ...x~y@. Evaluate x~y, replace it on stack with result. Then repeat */ |
| 849 | } | 852 | } |
| 850 | dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack)); | 853 | dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack)); |
| 851 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); | 854 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); |
| @@ -856,6 +859,21 @@ dbg(" numstack:%d val:%lld %lld %p", (int)(numstackptr - numstack), | |||
| 856 | numstackptr[-1].var_name == SECOND_VAL_VALID ? numstackptr[-1].second_val : 0, | 859 | numstackptr[-1].var_name == SECOND_VAL_VALID ? numstackptr[-1].second_val : 0, |
| 857 | numstackptr[-1].var_name | 860 | numstackptr[-1].var_name |
| 858 | ); | 861 | ); |
| 862 | /* For ternary ?: we need to remove ? from opstack too, not just : */ | ||
| 863 | if (prev_op == TOK_CONDITIONAL_SEP) { | ||
| 864 | // This is caught in arith_apply() | ||
| 865 | //if (opstackptr == opstack) { | ||
| 866 | // /* Example: $((2:3)) */ | ||
| 867 | // errmsg = "where is your ? in ?:"; | ||
| 868 | // goto err_with_custom_msg; | ||
| 869 | //} | ||
| 870 | opstackptr--; | ||
| 871 | if (*opstackptr != TOK_CONDITIONAL) { | ||
| 872 | /* Example: $((1,2:3)) */ | ||
| 873 | errmsg = "malformed ?: operator"; | ||
| 874 | goto err_with_custom_msg; | ||
| 875 | } | ||
| 876 | } | ||
| 859 | } /* while (opstack not empty) */ | 877 | } /* while (opstack not empty) */ |
| 860 | if (op == TOK_RPAREN) /* unpaired RPAREN? */ | 878 | if (op == TOK_RPAREN) /* unpaired RPAREN? */ |
| 861 | goto err; | 879 | goto err; |
