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/math.c | |
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/math.c')
-rw-r--r-- | shell/math.c | 80 |
1 files changed, 49 insertions, 31 deletions
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; |