diff options
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/math.c | 66 |
1 files changed, 36 insertions, 30 deletions
diff --git a/shell/math.c b/shell/math.c index 727c29467..af8e691fe 100644 --- a/shell/math.c +++ b/shell/math.c | |||
| @@ -236,14 +236,19 @@ typedef struct { | |||
| 236 | * then '?' selects one of them based on its left side. | 236 | * then '?' selects one of them based on its left side. |
| 237 | */ | 237 | */ |
| 238 | arith_t second_val; | 238 | arith_t second_val; |
| 239 | char second_val_present; | 239 | #define SECOND_VAL_VALID ((char*)(intptr_t)-1) |
| 240 | /* If NULL then it's just a number, else it's a named variable */ | 240 | /* If NULL then it's just a number, if SECOND_VAL_VALID, |
| 241 | char *var; | 241 | * it's a result of "expr : expr", else it's a named variable. |
| 242 | * (We use SECOND_VAL_VALID instead of a bit flag to keep | ||
| 243 | * var_or_num_t smaller, we allocate a lot of them on stack). | ||
| 244 | */ | ||
| 245 | char *var_name; | ||
| 242 | } var_or_num_t; | 246 | } var_or_num_t; |
| 243 | 247 | ||
| 248 | |||
| 244 | typedef struct remembered_name { | 249 | typedef struct remembered_name { |
| 245 | struct remembered_name *next; | 250 | struct remembered_name *next; |
| 246 | const char *var; | 251 | const char *var_name; |
| 247 | } remembered_name; | 252 | } remembered_name; |
| 248 | 253 | ||
| 249 | 254 | ||
| @@ -253,17 +258,17 @@ evaluate_string(arith_state_t *math_state, const char *expr); | |||
| 253 | static const char* | 258 | static const char* |
| 254 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | 259 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) |
| 255 | { | 260 | { |
| 256 | if (t->var) { | 261 | if (t->var_name && t->var_name != SECOND_VAL_VALID) { |
| 257 | const char *p = math_state->lookupvar(t->var); | 262 | const char *p = math_state->lookupvar(t->var_name); |
| 258 | if (p) { | 263 | if (p) { |
| 259 | remembered_name *cur; | 264 | remembered_name *cur; |
| 260 | remembered_name cur_save; | 265 | remembered_name remember; |
| 261 | 266 | ||
| 262 | /* did we already see this name? | 267 | /* did we already see this name? |
| 263 | * testcase: a=b; b=a; echo $((a)) | 268 | * testcase: a=b; b=a; echo $((a)) |
| 264 | */ | 269 | */ |
| 265 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | 270 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { |
| 266 | if (strcmp(cur->var, t->var) == 0) { | 271 | if (strcmp(cur->var_name, t->var_name) == 0) { |
| 267 | /* Yes */ | 272 | /* Yes */ |
| 268 | return "expression recursion loop detected"; | 273 | return "expression recursion loop detected"; |
| 269 | } | 274 | } |
| @@ -271,9 +276,9 @@ arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | |||
| 271 | 276 | ||
| 272 | /* push current var name */ | 277 | /* push current var name */ |
| 273 | cur = math_state->list_of_recursed_names; | 278 | cur = math_state->list_of_recursed_names; |
| 274 | cur_save.var = t->var; | 279 | remember.var_name = t->var_name; |
| 275 | cur_save.next = cur; | 280 | remember.next = cur; |
| 276 | math_state->list_of_recursed_names = &cur_save; | 281 | math_state->list_of_recursed_names = &remember; |
| 277 | 282 | ||
| 278 | /* recursively evaluate p as expression */ | 283 | /* recursively evaluate p as expression */ |
| 279 | t->val = evaluate_string(math_state, p); | 284 | t->val = evaluate_string(math_state, p); |
| @@ -286,7 +291,7 @@ arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | |||
| 286 | /* treat undefined var as 0 */ | 291 | /* treat undefined var as 0 */ |
| 287 | t->val = 0; | 292 | t->val = 0; |
| 288 | } | 293 | } |
| 289 | return 0; | 294 | return NULL; |
| 290 | } | 295 | } |
| 291 | 296 | ||
| 292 | /* "Applying" a token means performing it on the top elements on the integer | 297 | /* "Applying" a token means performing it on the top elements on the integer |
| @@ -303,7 +308,7 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 303 | 308 | ||
| 304 | /* There is no operator that can work without arguments */ | 309 | /* There is no operator that can work without arguments */ |
| 305 | if (NUMPTR == numstack) | 310 | if (NUMPTR == numstack) |
| 306 | goto err; | 311 | goto syntax_err; |
| 307 | 312 | ||
| 308 | top_of_stack = NUMPTR - 1; | 313 | top_of_stack = NUMPTR - 1; |
| 309 | 314 | ||
| @@ -326,15 +331,15 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 326 | else if (op != TOK_UPLUS) { | 331 | else if (op != TOK_UPLUS) { |
| 327 | /* Binary operators */ | 332 | /* Binary operators */ |
| 328 | arith_t right_side_val; | 333 | arith_t right_side_val; |
| 329 | char bad_second_val; | 334 | int bad_second_val; |
| 330 | 335 | ||
| 331 | /* Binary operators need two arguments */ | 336 | /* Binary operators need two arguments */ |
| 332 | if (top_of_stack == numstack) | 337 | if (top_of_stack == numstack) |
| 333 | goto err; | 338 | goto syntax_err; |
| 334 | /* ...and they pop one */ | 339 | /* ...and they pop one */ |
| 335 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | 340 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ |
| 336 | 341 | ||
| 337 | bad_second_val = top_of_stack->second_val_present; | 342 | bad_second_val = (top_of_stack->var_name == SECOND_VAL_VALID); |
| 338 | if (op == TOK_CONDITIONAL) { /* ? operation */ | 343 | if (op == TOK_CONDITIONAL) { /* ? operation */ |
| 339 | /* Make next if (...) protect against | 344 | /* Make next if (...) protect against |
| 340 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | 345 | * $((expr1 ? expr2)) - that is, missing ": expr" */ |
| @@ -363,8 +368,10 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 363 | /* Protect against $((expr : expr)) */ | 368 | /* Protect against $((expr : expr)) */ |
| 364 | return "malformed ?: operator"; | 369 | return "malformed ?: operator"; |
| 365 | } | 370 | } |
| 366 | top_of_stack->second_val_present = op; | 371 | top_of_stack->val = rez; |
| 367 | top_of_stack->second_val = right_side_val; | 372 | top_of_stack->second_val = right_side_val; |
| 373 | top_of_stack->var_name = SECOND_VAL_VALID; | ||
| 374 | return NULL; | ||
| 368 | } | 375 | } |
| 369 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) | 376 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
| 370 | rez |= right_side_val; | 377 | rez |= right_side_val; |
| @@ -439,13 +446,13 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 439 | if (is_assign_op(op)) { | 446 | if (is_assign_op(op)) { |
| 440 | char buf[sizeof(arith_t)*3 + 2]; | 447 | char buf[sizeof(arith_t)*3 + 2]; |
| 441 | 448 | ||
| 442 | if (top_of_stack->var == NULL) { | 449 | if (!top_of_stack->var_name || top_of_stack->var_name == SECOND_VAL_VALID) { |
| 443 | /* Hmm, 1=2 ? */ | 450 | /* Hmm, 1=2 ? */ |
| 444 | goto err; | 451 | goto syntax_err; |
| 445 | } | 452 | } |
| 446 | /* Save to shell variable */ | 453 | /* Save to shell variable */ |
| 447 | sprintf(buf, ARITH_FMT, rez); | 454 | sprintf(buf, ARITH_FMT, rez); |
| 448 | math_state->setvar(top_of_stack->var, buf); | 455 | math_state->setvar(top_of_stack->var_name, buf); |
| 449 | /* After saving, make previous value for v++ or v-- */ | 456 | /* After saving, make previous value for v++ or v-- */ |
| 450 | if (op == TOK_POST_INC) | 457 | if (op == TOK_POST_INC) |
| 451 | rez--; | 458 | rez--; |
| @@ -455,9 +462,9 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 455 | 462 | ||
| 456 | top_of_stack->val = rez; | 463 | top_of_stack->val = rez; |
| 457 | /* Erase var name, it is just a number now */ | 464 | /* Erase var name, it is just a number now */ |
| 458 | top_of_stack->var = NULL; | 465 | top_of_stack->var_name = NULL; |
| 459 | return NULL; | 466 | return NULL; |
| 460 | err: | 467 | syntax_err: |
| 461 | return "arithmetic syntax error"; | 468 | return "arithmetic syntax error"; |
| 462 | #undef NUMPTR | 469 | #undef NUMPTR |
| 463 | } | 470 | } |
| @@ -660,12 +667,11 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 660 | if (p != expr) { | 667 | if (p != expr) { |
| 661 | /* Name */ | 668 | /* Name */ |
| 662 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ | 669 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ |
| 663 | numstackptr->var = alloca(var_name_size); | 670 | numstackptr->var_name = alloca(var_name_size); |
| 664 | safe_strncpy(numstackptr->var, expr, var_name_size); | 671 | safe_strncpy(numstackptr->var_name, expr, var_name_size); |
| 665 | //bb_error_msg("var:'%s'", numstackptr->var); | 672 | //bb_error_msg("var:'%s'", numstackptr->var); |
| 666 | expr = p; | 673 | expr = p; |
| 667 | num: | 674 | num: |
| 668 | numstackptr->second_val_present = 0; | ||
| 669 | numstackptr++; | 675 | numstackptr++; |
| 670 | lasttok = TOK_NUM; | 676 | lasttok = TOK_NUM; |
| 671 | continue; | 677 | continue; |
| @@ -673,7 +679,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 673 | 679 | ||
| 674 | if (isdigit(*expr)) { | 680 | if (isdigit(*expr)) { |
| 675 | /* Number */ | 681 | /* Number */ |
| 676 | numstackptr->var = NULL; | 682 | numstackptr->var_name = NULL; |
| 677 | errno = 0; | 683 | errno = 0; |
| 678 | numstackptr->val = strto_arith_t(expr, (char**) &expr); | 684 | numstackptr->val = strto_arith_t(expr, (char**) &expr); |
| 679 | /* A number can't be followed by another number, or a variable name. | 685 | /* A number can't be followed by another number, or a variable name. |
| @@ -702,7 +708,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 702 | if ((expr[0] == '+' || expr[0] == '-') | 708 | if ((expr[0] == '+' || expr[0] == '-') |
| 703 | && (expr[1] == expr[0]) | 709 | && (expr[1] == expr[0]) |
| 704 | ) { | 710 | ) { |
| 705 | if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ | 711 | if (numstackptr == numstack || !numstackptr[-1].var_name) { /* not a VAR++ */ |
| 706 | char next = skip_whitespace(expr + 2)[0]; | 712 | char next = skip_whitespace(expr + 2)[0]; |
| 707 | if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ | 713 | if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ |
| 708 | //bb_error_msg("special %c%c", expr[0], expr[0]); | 714 | //bb_error_msg("special %c%c", expr[0], expr[0]); |
| @@ -795,14 +801,14 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 795 | //bb_error_msg("op == TOK_RPAREN"); | 801 | //bb_error_msg("op == TOK_RPAREN"); |
| 796 | if (prev_op == TOK_LPAREN) { | 802 | if (prev_op == TOK_LPAREN) { |
| 797 | //bb_error_msg("prev_op == TOK_LPAREN"); | 803 | //bb_error_msg("prev_op == TOK_LPAREN"); |
| 798 | //bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); | 804 | //bb_error_msg(" %p %p numstackptr[-1].var_name:'%s'", numstack, numstackptr-1, numstackptr[-1].var_name); |
| 799 | if (numstackptr[-1].var) { | 805 | if (numstackptr[-1].var_name && numstackptr[-1].var_name != SECOND_VAL_VALID) { |
| 800 | /* Expression is (var), lookup now */ | 806 | /* Expression is (var), lookup now */ |
| 801 | errmsg = arith_lookup_val(math_state, &numstackptr[-1]); | 807 | errmsg = arith_lookup_val(math_state, &numstackptr[-1]); |
| 802 | if (errmsg) | 808 | if (errmsg) |
| 803 | goto err_with_custom_msg; | 809 | goto err_with_custom_msg; |
| 804 | /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ | 810 | /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ |
| 805 | numstackptr[-1].var = NULL; | 811 | numstackptr[-1].var_name = NULL; |
| 806 | } | 812 | } |
| 807 | /* Any operator directly after a | 813 | /* Any operator directly after a |
| 808 | * close paren should consider itself binary */ | 814 | * close paren should consider itself binary */ |
