diff options
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/math.c | 83 |
1 files changed, 51 insertions, 32 deletions
diff --git a/shell/math.c b/shell/math.c index f6fed805c..4c1b1b762 100644 --- a/shell/math.c +++ b/shell/math.c | |||
| @@ -245,7 +245,7 @@ is_right_associative(operator prec) | |||
| 245 | 245 | ||
| 246 | typedef struct { | 246 | typedef struct { |
| 247 | arith_t val; | 247 | arith_t val; |
| 248 | char *var_name; | 248 | const char *var_name; |
| 249 | } var_or_num_t; | 249 | } var_or_num_t; |
| 250 | 250 | ||
| 251 | #define VALID_NAME(name) (name) | 251 | #define VALID_NAME(name) (name) |
| @@ -256,44 +256,58 @@ typedef struct remembered_name { | |||
| 256 | const char *var_name; | 256 | const char *var_name; |
| 257 | } remembered_name; | 257 | } remembered_name; |
| 258 | 258 | ||
| 259 | static ALWAYS_INLINE int isalnum_(int c) | ||
| 260 | { | ||
| 261 | return (isalnum(c) || c == '_'); | ||
| 262 | } | ||
| 263 | |||
| 259 | static arith_t | 264 | static arith_t |
| 260 | evaluate_string(arith_state_t *math_state, const char *expr); | 265 | evaluate_string(arith_state_t *math_state, const char *expr); |
| 261 | 266 | ||
| 262 | static const char* | 267 | static const char* |
| 263 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | 268 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) |
| 264 | { | 269 | { |
| 265 | if (VALID_NAME(t->var_name)) { | 270 | const char *name = t->var_name; |
| 266 | const char *p = math_state->lookupvar(t->var_name); | 271 | char c; |
| 267 | if (p) { | 272 | const char *p; |
| 268 | remembered_name *cur; | 273 | char *e = (char*)endofname(name); |
| 269 | remembered_name remember; | 274 | |
| 270 | 275 | c = *e; | |
| 271 | /* did we already see this name? | 276 | *e = '\0'; |
| 272 | * testcase: a=b; b=a; echo $((a)) | 277 | p = math_state->lookupvar(name); |
| 273 | */ | 278 | *e = c; |
| 274 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | 279 | if (p) { |
| 275 | if (strcmp(cur->var_name, t->var_name) == 0) { | 280 | size_t len = e - name; |
| 276 | /* yes */ | 281 | remembered_name *cur; |
| 277 | return "expression recursion loop detected"; | 282 | remembered_name remember; |
| 278 | } | 283 | |
| 284 | /* did we already see this name? | ||
| 285 | * testcase: a=b; b=a; echo $((a)) | ||
| 286 | */ | ||
| 287 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | ||
| 288 | if (strncmp(cur->var_name, name, len) == 0 | ||
| 289 | && !isalnum_(cur->var_name[len]) | ||
| 290 | ) { | ||
| 291 | /* yes */ | ||
| 292 | return "expression recursion loop detected"; | ||
| 279 | } | 293 | } |
| 294 | } | ||
| 280 | 295 | ||
| 281 | /* push current var name */ | 296 | /* push current var name */ |
| 282 | remember.var_name = t->var_name; | 297 | remember.var_name = name; |
| 283 | remember.next = math_state->list_of_recursed_names; | 298 | remember.next = math_state->list_of_recursed_names; |
| 284 | math_state->list_of_recursed_names = &remember; | 299 | math_state->list_of_recursed_names = &remember; |
| 285 | 300 | ||
| 286 | /* recursively evaluate p as expression */ | 301 | /* recursively evaluate p as expression */ |
| 287 | t->val = evaluate_string(math_state, p); | 302 | t->val = evaluate_string(math_state, p); |
| 288 | 303 | ||
| 289 | /* pop current var name */ | 304 | /* pop current var name */ |
| 290 | math_state->list_of_recursed_names = remember.next; | 305 | math_state->list_of_recursed_names = remember.next; |
| 291 | 306 | ||
| 292 | return math_state->errmsg; | 307 | return math_state->errmsg; |
| 293 | } | ||
| 294 | /* treat undefined var as 0 */ | ||
| 295 | t->val = 0; | ||
| 296 | } | 308 | } |
| 309 | /* treat undefined var as 0 */ | ||
| 310 | t->val = 0; | ||
| 297 | return NULL; | 311 | return NULL; |
| 298 | } | 312 | } |
| 299 | 313 | ||
| @@ -447,7 +461,13 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
| 447 | } | 461 | } |
| 448 | /* Save to shell variable */ | 462 | /* Save to shell variable */ |
| 449 | sprintf(buf, ARITH_FMT, rez); | 463 | sprintf(buf, ARITH_FMT, rez); |
| 450 | math_state->setvar(top_of_stack->var_name, buf); | 464 | { |
| 465 | char *e = (char*)endofname(top_of_stack->var_name); | ||
| 466 | char c = *e; | ||
| 467 | *e = '\0'; | ||
| 468 | math_state->setvar(top_of_stack->var_name, buf); | ||
| 469 | *e = c; | ||
| 470 | } | ||
| 451 | /* After saving, make previous value for v++ or v-- */ | 471 | /* After saving, make previous value for v++ or v-- */ |
| 452 | if (op == TOK_POST_INC) | 472 | if (op == TOK_POST_INC) |
| 453 | rez--; | 473 | rez--; |
| @@ -610,6 +630,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 610 | * (modulo "09v09v09v09v09v" case, | 630 | * (modulo "09v09v09v09v09v" case, |
| 611 | * but we have code to detect that early) | 631 | * but we have code to detect that early) |
| 612 | */ | 632 | */ |
| 633 | dbg("expr:'%s' expr_len:%u", expr, expr_len); | ||
| 613 | numstackptr = numstack = alloca((expr_len / 2) * sizeof(numstack[0])); | 634 | numstackptr = numstack = alloca((expr_len / 2) * sizeof(numstack[0])); |
| 614 | opstackptr = opstack = alloca(expr_len * sizeof(opstack[0])); | 635 | opstackptr = opstack = alloca(expr_len * sizeof(opstack[0])); |
| 615 | } | 636 | } |
| @@ -655,10 +676,8 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
| 655 | if (p != expr) { | 676 | if (p != expr) { |
| 656 | /* Name */ | 677 | /* Name */ |
| 657 | if (!math_state->evaluation_disabled) { | 678 | if (!math_state->evaluation_disabled) { |
| 658 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ | 679 | numstackptr->var_name = expr; |
| 659 | numstackptr->var_name = alloca(var_name_size); | 680 | dbg("[%d] var:'%.*s'", (int)(numstackptr - numstack), (int)(p - expr), expr); |
| 660 | safe_strncpy(numstackptr->var_name, expr, var_name_size); | ||
| 661 | dbg("[%d] var:'%s'", (int)(numstackptr - numstack), numstackptr->var_name); | ||
| 662 | expr = skip_whitespace(p); | 681 | expr = skip_whitespace(p); |
| 663 | /* If it is not followed by "=" operator... */ | 682 | /* If it is not followed by "=" operator... */ |
| 664 | if (expr[0] != '=' /* not "=..." */ | 683 | if (expr[0] != '=' /* not "=..." */ |
