diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-16 11:50:46 +0200 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2010-09-16 11:50:46 +0200 |
commit | bed7c81ea24e9e9ba2a897e233de2abefe611e8b (patch) | |
tree | b18a4559e60300fecd7275c0088f8942441072a2 | |
parent | 063847d6bd23e184c409f37645ba90fa4d039ada (diff) | |
download | busybox-w32-bed7c81ea24e9e9ba2a897e233de2abefe611e8b.tar.gz busybox-w32-bed7c81ea24e9e9ba2a897e233de2abefe611e8b.tar.bz2 busybox-w32-bed7c81ea24e9e9ba2a897e233de2abefe611e8b.zip |
shell/math: deconvolute and explain ?: handling. Give better error message
function old new delta
arith_apply 1271 1283 +12
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r-- | shell/ash.c | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-arith/arith.right | 2 | ||||
-rw-r--r-- | shell/hush.c | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith.right | 2 | ||||
-rw-r--r-- | shell/math.c | 243 | ||||
-rw-r--r-- | shell/math.h | 20 |
6 files changed, 144 insertions, 129 deletions
diff --git a/shell/ash.c b/shell/ash.c index ec887e088..9089adc63 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -5516,7 +5516,7 @@ cvtnum(arith_t num) | |||
5516 | int len; | 5516 | int len; |
5517 | 5517 | ||
5518 | expdest = makestrspace(32, expdest); | 5518 | expdest = makestrspace(32, expdest); |
5519 | len = fmtstr(expdest, 32, arith_t_fmt, num); | 5519 | len = fmtstr(expdest, 32, ARITH_FMT, num); |
5520 | STADJUST(len, expdest); | 5520 | STADJUST(len, expdest); |
5521 | return len; | 5521 | return len; |
5522 | } | 5522 | } |
diff --git a/shell/ash_test/ash-arith/arith.right b/shell/ash_test/ash-arith/arith.right index 7257cc566..9b9ca8e2f 100644 --- a/shell/ash_test/ash-arith/arith.right +++ b/shell/ash_test/ash-arith/arith.right | |||
@@ -72,7 +72,7 @@ ghi | |||
72 | ./arith.tests: line 191: arithmetic syntax error | 72 | ./arith.tests: line 191: arithmetic syntax error |
73 | 16 16 | 73 | 16 16 |
74 | ./arith.tests: line 196: arithmetic syntax error | 74 | ./arith.tests: line 196: arithmetic syntax error |
75 | ./arith.tests: line 197: arithmetic syntax error | 75 | ./arith.tests: line 197: malformed ?: operator |
76 | ./arith.tests: line 198: arithmetic syntax error | 76 | ./arith.tests: line 198: arithmetic syntax error |
77 | 9 9 | 77 | 9 9 |
78 | ./arith.tests: line 205: arithmetic syntax error | 78 | ./arith.tests: line 205: arithmetic syntax error |
diff --git a/shell/hush.c b/shell/hush.c index ad30ac1ea..a888332bc 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -4938,8 +4938,8 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
4938 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 4938 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
4939 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); | 4939 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); |
4940 | res = expand_and_evaluate_arith(arg, NULL); | 4940 | res = expand_and_evaluate_arith(arg, NULL); |
4941 | debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res); | 4941 | debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res); |
4942 | sprintf(arith_buf, arith_t_fmt, res); | 4942 | sprintf(arith_buf, ARITH_FMT, res); |
4943 | val = arith_buf; | 4943 | val = arith_buf; |
4944 | break; | 4944 | break; |
4945 | } | 4945 | } |
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right index fd4ea8e01..8a201fb3b 100644 --- a/shell/hush_test/hush-arith/arith.right +++ b/shell/hush_test/hush-arith/arith.right | |||
@@ -81,7 +81,7 @@ ghi | |||
81 | hush: arithmetic syntax error | 81 | hush: arithmetic syntax error |
82 | 16 16 | 82 | 16 16 |
83 | hush: arithmetic syntax error | 83 | hush: arithmetic syntax error |
84 | hush: arithmetic syntax error | 84 | hush: malformed ?: operator |
85 | hush: arithmetic syntax error | 85 | hush: arithmetic syntax error |
86 | 9 9 | 86 | 9 9 |
87 | hush: arithmetic syntax error | 87 | hush: arithmetic syntax error |
diff --git a/shell/math.c b/shell/math.c index 871c06c3e..9d3b912a7 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * arithmetic code ripped out of ash shell for code sharing | 2 | * Arithmetic code ripped out of ash shell for code sharing. |
3 | * | 3 | * |
4 | * This code is derived from software contributed to Berkeley by | 4 | * This code is derived from software contributed to Berkeley by |
5 | * Kenneth Almquist. | 5 | * Kenneth Almquist. |
@@ -154,7 +154,7 @@ typedef unsigned char operator; | |||
154 | 154 | ||
155 | #define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) | 155 | #define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) |
156 | 156 | ||
157 | /* ternary conditional operator is right associative too */ | 157 | /* Ternary conditional operator is right associative too */ |
158 | #define TOK_CONDITIONAL tok_decl(4,0) | 158 | #define TOK_CONDITIONAL tok_decl(4,0) |
159 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | 159 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) |
160 | 160 | ||
@@ -186,10 +186,10 @@ typedef unsigned char operator; | |||
186 | #define TOK_DIV tok_decl(14,1) | 186 | #define TOK_DIV tok_decl(14,1) |
187 | #define TOK_REM tok_decl(14,2) | 187 | #define TOK_REM tok_decl(14,2) |
188 | 188 | ||
189 | /* exponent is right associative */ | 189 | /* Exponent is right associative */ |
190 | #define TOK_EXPONENT tok_decl(15,1) | 190 | #define TOK_EXPONENT tok_decl(15,1) |
191 | 191 | ||
192 | /* unary operators */ | 192 | /* Unary operators */ |
193 | #define UNARYPREC 16 | 193 | #define UNARYPREC 16 |
194 | #define TOK_BNOT tok_decl(UNARYPREC,0) | 194 | #define TOK_BNOT tok_decl(UNARYPREC,0) |
195 | #define TOK_NOT tok_decl(UNARYPREC,1) | 195 | #define TOK_NOT tok_decl(UNARYPREC,1) |
@@ -213,30 +213,37 @@ typedef unsigned char operator; | |||
213 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) | 213 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) |
214 | 214 | ||
215 | static int | 215 | static int |
216 | tok_have_assign(operator op) | 216 | is_assign_op(operator op) |
217 | { | 217 | { |
218 | operator prec = PREC(op); | 218 | operator prec = PREC(op); |
219 | |||
220 | fix_assignment_prec(prec); | 219 | fix_assignment_prec(prec); |
221 | return (prec == PREC(TOK_ASSIGN) || | 220 | return prec == PREC(TOK_ASSIGN) |
222 | prec == PREC_PRE || prec == PREC_POST); | 221 | || prec == PREC_PRE |
222 | || prec == PREC_POST; | ||
223 | } | 223 | } |
224 | 224 | ||
225 | static int | 225 | static int |
226 | is_right_associative(operator prec) | 226 | is_right_associative(operator prec) |
227 | { | 227 | { |
228 | return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) | 228 | return prec == PREC(TOK_ASSIGN) |
229 | || prec == PREC(TOK_CONDITIONAL)); | 229 | || prec == PREC(TOK_EXPONENT) |
230 | || prec == PREC(TOK_CONDITIONAL); | ||
230 | } | 231 | } |
231 | 232 | ||
232 | 233 | ||
233 | typedef struct { | 234 | typedef struct { |
234 | arith_t val; | 235 | arith_t val; |
235 | arith_t contidional_second_val; | 236 | /* We acquire second_val only when "expr1 : expr2" part |
236 | char contidional_second_val_initialized; | 237 | * of ternary ?: op is evaluated. |
237 | char *var; /* if NULL then is regular number, | 238 | * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). |
238 | else is variable name */ | 239 | * ':' produces a new value which has two parts, val and second_val; |
239 | } v_n_t; | 240 | * then '?' selects one of them based on its left side. |
241 | */ | ||
242 | arith_t second_val; | ||
243 | char second_val_present; | ||
244 | /* If NULL then it's just a number, else it's a named variable */ | ||
245 | char *var; | ||
246 | } var_or_num_t; | ||
240 | 247 | ||
241 | typedef struct remembered_name { | 248 | typedef struct remembered_name { |
242 | struct remembered_name *next; | 249 | struct remembered_name *next; |
@@ -248,7 +255,7 @@ static arith_t FAST_FUNC | |||
248 | evaluate_string(arith_state_t *math_state, const char *expr); | 255 | evaluate_string(arith_state_t *math_state, const char *expr); |
249 | 256 | ||
250 | static const char* | 257 | static const char* |
251 | arith_lookup_val(arith_state_t *math_state, v_n_t *t) | 258 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) |
252 | { | 259 | { |
253 | if (t->var) { | 260 | if (t->var) { |
254 | const char *p = lookupvar(t->var); | 261 | const char *p = lookupvar(t->var); |
@@ -290,27 +297,28 @@ arith_lookup_val(arith_state_t *math_state, v_n_t *t) | |||
290 | * stack. For an unary operator it will only change the top element, but a | 297 | * stack. For an unary operator it will only change the top element, but a |
291 | * binary operator will pop two arguments and push the result */ | 298 | * binary operator will pop two arguments and push the result */ |
292 | static NOINLINE const char* | 299 | static NOINLINE const char* |
293 | arith_apply(arith_state_t *math_state, operator op, v_n_t *numstack, v_n_t **numstackptr) | 300 | arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) |
294 | { | 301 | { |
295 | #define NUMPTR (*numstackptr) | 302 | #define NUMPTR (*numstackptr) |
296 | 303 | ||
297 | v_n_t *numptr_m1; | 304 | var_or_num_t *top_of_stack; |
298 | arith_t numptr_val, rez; | 305 | arith_t rez; |
299 | const char *err; | 306 | const char *err; |
300 | 307 | ||
301 | /* There is no operator that can work without arguments */ | 308 | /* There is no operator that can work without arguments */ |
302 | if (NUMPTR == numstack) | 309 | if (NUMPTR == numstack) |
303 | goto err; | 310 | goto err; |
304 | numptr_m1 = NUMPTR - 1; | ||
305 | 311 | ||
306 | /* Check operand is var with noninteger value */ | 312 | top_of_stack = NUMPTR - 1; |
307 | err = arith_lookup_val(math_state, numptr_m1); | 313 | |
314 | /* Resolve name to value, if needed */ | ||
315 | err = arith_lookup_val(math_state, top_of_stack); | ||
308 | if (err) | 316 | if (err) |
309 | return err; | 317 | return err; |
310 | 318 | ||
311 | rez = numptr_m1->val; | 319 | rez = top_of_stack->val; |
312 | if (op == TOK_UMINUS) | 320 | if (op == TOK_UMINUS) |
313 | rez *= -1; | 321 | rez = -rez; |
314 | else if (op == TOK_NOT) | 322 | else if (op == TOK_NOT) |
315 | rez = !rez; | 323 | rez = !rez; |
316 | else if (op == TOK_BNOT) | 324 | else if (op == TOK_BNOT) |
@@ -321,112 +329,119 @@ arith_apply(arith_state_t *math_state, operator op, v_n_t *numstack, v_n_t **num | |||
321 | rez--; | 329 | rez--; |
322 | else if (op != TOK_UPLUS) { | 330 | else if (op != TOK_UPLUS) { |
323 | /* Binary operators */ | 331 | /* Binary operators */ |
332 | arith_t right_side_val; | ||
333 | char bad_second_val; | ||
324 | 334 | ||
325 | /* check and binary operators need two arguments */ | 335 | /* Binary operators need two arguments */ |
326 | if (numptr_m1 == numstack) goto err; | 336 | if (top_of_stack == numstack) |
327 | |||
328 | /* ... and they pop one */ | ||
329 | --NUMPTR; | ||
330 | numptr_val = rez; | ||
331 | if (op == TOK_CONDITIONAL) { | ||
332 | if (!numptr_m1->contidional_second_val_initialized) { | ||
333 | /* protect $((expr1 ? expr2)) without ": expr" */ | ||
334 | goto err; | ||
335 | } | ||
336 | rez = numptr_m1->contidional_second_val; | ||
337 | } else if (numptr_m1->contidional_second_val_initialized) { | ||
338 | /* protect $((expr1 : expr2)) without "expr ? " */ | ||
339 | goto err; | 337 | goto err; |
338 | /* ...and they pop one */ | ||
339 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | ||
340 | |||
341 | bad_second_val = top_of_stack->second_val_present; | ||
342 | if (op == TOK_CONDITIONAL) { /* ? operation */ | ||
343 | /* Make next if (...) protect against | ||
344 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | ||
345 | bad_second_val = !bad_second_val; | ||
346 | } | ||
347 | if (bad_second_val) { | ||
348 | /* Protect against $((expr <not_?_op> expr1 : expr2)) */ | ||
349 | return "malformed ?: operator"; | ||
340 | } | 350 | } |
341 | numptr_m1 = NUMPTR - 1; | 351 | |
352 | top_of_stack--; /* now points to left side */ | ||
353 | |||
342 | if (op != TOK_ASSIGN) { | 354 | if (op != TOK_ASSIGN) { |
343 | /* check operand is var with noninteger value for not '=' */ | 355 | /* Resolve left side value (unless the op is '=') */ |
344 | err = arith_lookup_val(math_state, numptr_m1); | 356 | err = arith_lookup_val(math_state, top_of_stack); |
345 | if (err) | 357 | if (err) |
346 | return err; | 358 | return err; |
347 | } | 359 | } |
348 | if (op == TOK_CONDITIONAL) { | 360 | |
349 | numptr_m1->contidional_second_val = rez; | 361 | right_side_val = rez; |
362 | rez = top_of_stack->val; | ||
363 | if (op == TOK_CONDITIONAL) /* ? operation */ | ||
364 | rez = (rez ? right_side_val : top_of_stack[1].second_val); | ||
365 | else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ | ||
366 | if (top_of_stack == numstack) { | ||
367 | /* Protect against $((expr : expr)) */ | ||
368 | return "malformed ?: operator"; | ||
369 | } | ||
370 | top_of_stack->second_val_present = op; | ||
371 | top_of_stack->second_val = right_side_val; | ||
350 | } | 372 | } |
351 | rez = numptr_m1->val; | 373 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
352 | if (op == TOK_BOR || op == TOK_OR_ASSIGN) | 374 | rez |= right_side_val; |
353 | rez |= numptr_val; | ||
354 | else if (op == TOK_OR) | 375 | else if (op == TOK_OR) |
355 | rez = numptr_val || rez; | 376 | rez = right_side_val || rez; |
356 | else if (op == TOK_BAND || op == TOK_AND_ASSIGN) | 377 | else if (op == TOK_BAND || op == TOK_AND_ASSIGN) |
357 | rez &= numptr_val; | 378 | rez &= right_side_val; |
358 | else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) | 379 | else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) |
359 | rez ^= numptr_val; | 380 | rez ^= right_side_val; |
360 | else if (op == TOK_AND) | 381 | else if (op == TOK_AND) |
361 | rez = rez && numptr_val; | 382 | rez = rez && right_side_val; |
362 | else if (op == TOK_EQ) | 383 | else if (op == TOK_EQ) |
363 | rez = (rez == numptr_val); | 384 | rez = (rez == right_side_val); |
364 | else if (op == TOK_NE) | 385 | else if (op == TOK_NE) |
365 | rez = (rez != numptr_val); | 386 | rez = (rez != right_side_val); |
366 | else if (op == TOK_GE) | 387 | else if (op == TOK_GE) |
367 | rez = (rez >= numptr_val); | 388 | rez = (rez >= right_side_val); |
368 | else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) | 389 | else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) |
369 | rez >>= numptr_val; | 390 | rez >>= right_side_val; |
370 | else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) | 391 | else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) |
371 | rez <<= numptr_val; | 392 | rez <<= right_side_val; |
372 | else if (op == TOK_GT) | 393 | else if (op == TOK_GT) |
373 | rez = (rez > numptr_val); | 394 | rez = (rez > right_side_val); |
374 | else if (op == TOK_LT) | 395 | else if (op == TOK_LT) |
375 | rez = (rez < numptr_val); | 396 | rez = (rez < right_side_val); |
376 | else if (op == TOK_LE) | 397 | else if (op == TOK_LE) |
377 | rez = (rez <= numptr_val); | 398 | rez = (rez <= right_side_val); |
378 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) | 399 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) |
379 | rez *= numptr_val; | 400 | rez *= right_side_val; |
380 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) | 401 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) |
381 | rez += numptr_val; | 402 | rez += right_side_val; |
382 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) | 403 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) |
383 | rez -= numptr_val; | 404 | rez -= right_side_val; |
384 | else if (op == TOK_ASSIGN || op == TOK_COMMA) | 405 | else if (op == TOK_ASSIGN || op == TOK_COMMA) |
385 | rez = numptr_val; | 406 | rez = right_side_val; |
386 | else if (op == TOK_CONDITIONAL_SEP) { | 407 | else if (op == TOK_EXPONENT) { |
387 | if (numptr_m1 == numstack) { | ||
388 | /* protect $((expr : expr)) without "expr ? " */ | ||
389 | goto err; | ||
390 | } | ||
391 | numptr_m1->contidional_second_val_initialized = op; | ||
392 | numptr_m1->contidional_second_val = numptr_val; | ||
393 | } else if (op == TOK_CONDITIONAL) { | ||
394 | rez = rez ? | ||
395 | numptr_val : numptr_m1->contidional_second_val; | ||
396 | } else if (op == TOK_EXPONENT) { | ||
397 | arith_t c; | 408 | arith_t c; |
398 | if (numptr_val < 0) | 409 | if (right_side_val < 0) |
399 | return "exponent less than 0"; | 410 | return "exponent less than 0"; |
400 | c = 1; | 411 | c = 1; |
401 | while (--numptr_val >= 0) | 412 | while (--right_side_val >= 0) |
402 | c *= rez; | 413 | c *= rez; |
403 | rez = c; | 414 | rez = c; |
404 | } else if (numptr_val == 0) | 415 | } |
416 | else if (right_side_val == 0) | ||
405 | return "divide by zero"; | 417 | return "divide by zero"; |
406 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) | 418 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) |
407 | rez /= numptr_val; | 419 | rez /= right_side_val; |
408 | else if (op == TOK_REM || op == TOK_REM_ASSIGN) | 420 | else if (op == TOK_REM || op == TOK_REM_ASSIGN) |
409 | rez %= numptr_val; | 421 | rez %= right_side_val; |
410 | } | 422 | } |
411 | if (tok_have_assign(op)) { | 423 | |
424 | if (is_assign_op(op)) { | ||
412 | char buf[sizeof(arith_t)*3 + 2]; | 425 | char buf[sizeof(arith_t)*3 + 2]; |
413 | 426 | ||
414 | if (numptr_m1->var == NULL) { | 427 | if (top_of_stack->var == NULL) { |
415 | /* Hmm, 1=2 ? */ | 428 | /* Hmm, 1=2 ? */ |
429 | //TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8 | ||
416 | goto err; | 430 | goto err; |
417 | } | 431 | } |
418 | /* save to shell variable */ | 432 | /* Save to shell variable */ |
419 | sprintf(buf, arith_t_fmt, rez); | 433 | sprintf(buf, ARITH_FMT, rez); |
420 | setvar(numptr_m1->var, buf); | 434 | setvar(top_of_stack->var, buf); |
421 | /* after saving, make previous value for v++ or v-- */ | 435 | /* After saving, make previous value for v++ or v-- */ |
422 | if (op == TOK_POST_INC) | 436 | if (op == TOK_POST_INC) |
423 | rez--; | 437 | rez--; |
424 | else if (op == TOK_POST_DEC) | 438 | else if (op == TOK_POST_DEC) |
425 | rez++; | 439 | rez++; |
426 | } | 440 | } |
427 | numptr_m1->val = rez; | 441 | |
428 | /* erase var name, it is just a number now */ | 442 | top_of_stack->val = rez; |
429 | numptr_m1->var = NULL; | 443 | /* Erase var name, it is just a number now */ |
444 | top_of_stack->var = NULL; | ||
430 | return NULL; | 445 | return NULL; |
431 | err: | 446 | err: |
432 | return "arithmetic syntax error"; | 447 | return "arithmetic syntax error"; |
@@ -499,16 +514,17 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
499 | const char *start_expr = expr = skip_whitespace(expr); | 514 | const char *start_expr = expr = skip_whitespace(expr); |
500 | unsigned expr_len = strlen(expr) + 2; | 515 | unsigned expr_len = strlen(expr) + 2; |
501 | /* Stack of integers */ | 516 | /* Stack of integers */ |
502 | /* The proof that there can be no more than strlen(startbuf)/2+1 integers | 517 | /* The proof that there can be no more than strlen(startbuf)/2+1 |
503 | * in any given correct or incorrect expression is left as an exercise to | 518 | * integers in any given correct or incorrect expression |
504 | * the reader. */ | 519 | * is left as an exercise to the reader. */ |
505 | v_n_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); | 520 | var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); |
506 | v_n_t *numstackptr = numstack; | 521 | var_or_num_t *numstackptr = numstack; |
507 | /* Stack of operator tokens */ | 522 | /* Stack of operator tokens */ |
508 | operator *const stack = alloca(expr_len * sizeof(stack[0])); | 523 | operator *const stack = alloca(expr_len * sizeof(stack[0])); |
509 | operator *stackptr = stack; | 524 | operator *stackptr = stack; |
510 | 525 | ||
511 | *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ | 526 | /* Start with a left paren */ |
527 | *stackptr++ = lasttok = TOK_LPAREN; | ||
512 | errmsg = NULL; | 528 | errmsg = NULL; |
513 | 529 | ||
514 | while (1) { | 530 | while (1) { |
@@ -521,7 +537,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
521 | arithval = *expr; | 537 | arithval = *expr; |
522 | if (arithval == '\0') { | 538 | if (arithval == '\0') { |
523 | if (expr == start_expr) { | 539 | if (expr == start_expr) { |
524 | /* Null expression. */ | 540 | /* Null expression */ |
525 | numstack->val = 0; | 541 | numstack->val = 0; |
526 | goto ret; | 542 | goto ret; |
527 | } | 543 | } |
@@ -558,7 +574,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
558 | safe_strncpy(numstackptr->var, expr, var_name_size); | 574 | safe_strncpy(numstackptr->var, expr, var_name_size); |
559 | expr = p; | 575 | expr = p; |
560 | num: | 576 | num: |
561 | numstackptr->contidional_second_val_initialized = 0; | 577 | numstackptr->second_val_present = 0; |
562 | numstackptr++; | 578 | numstackptr++; |
563 | lasttok = TOK_NUM; | 579 | lasttok = TOK_NUM; |
564 | continue; | 580 | continue; |
@@ -577,21 +593,32 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
577 | /* Should be an operator */ | 593 | /* Should be an operator */ |
578 | p = op_tokens; | 594 | p = op_tokens; |
579 | while (1) { | 595 | while (1) { |
580 | const char *e = expr; | 596 | // TODO: bash allows 7+++v, treats it as 7 + ++v |
597 | // we treat it as 7++ + v and reject | ||
581 | /* Compare expr to current op_tokens[] element */ | 598 | /* Compare expr to current op_tokens[] element */ |
582 | while (*p && *e == *p) | 599 | const char *e = expr; |
583 | p++, e++; | 600 | while (1) { |
584 | if (*p == '\0') { /* match: operator is found */ | 601 | if (*p == '\0') { |
585 | expr = e; | 602 | /* Match: operator is found */ |
586 | break; | 603 | expr = e; |
604 | goto tok_found; | ||
605 | } | ||
606 | if (*p != *e) | ||
607 | break; | ||
608 | p++; | ||
609 | e++; | ||
587 | } | 610 | } |
588 | /* Go to next element of op_tokens[] */ | 611 | /* No match, go to next element of op_tokens[] */ |
589 | while (*p) | 612 | while (*p) |
590 | p++; | 613 | p++; |
591 | p += 2; /* skip NUL and TOK_foo bytes */ | 614 | p += 2; /* skip NUL and TOK_foo bytes */ |
592 | if (*p == '\0') /* no next element, operator not found */ | 615 | if (*p == '\0') { |
616 | /* No next element, operator not found */ | ||
617 | //math_state->syntax_error_at = expr; | ||
593 | goto err; | 618 | goto err; |
619 | } | ||
594 | } | 620 | } |
621 | tok_found: | ||
595 | op = p[1]; /* fetch TOK_foo value */ | 622 | op = p[1]; /* fetch TOK_foo value */ |
596 | /* NB: expr now points past the operator */ | 623 | /* NB: expr now points past the operator */ |
597 | 624 | ||
@@ -662,21 +689,21 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
662 | } | 689 | } |
663 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); | 690 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); |
664 | if (errmsg) | 691 | if (errmsg) |
665 | goto ret; | 692 | goto err_with_custom_msg; |
666 | } | 693 | } |
667 | if (op == TOK_RPAREN) { | 694 | if (op == TOK_RPAREN) |
668 | goto err; | 695 | goto err; |
669 | } | ||
670 | } | 696 | } |
671 | 697 | ||
672 | /* Push this operator to the stack and remember it. */ | 698 | /* Push this operator to the stack and remember it */ |
673 | *stackptr++ = lasttok = op; | 699 | *stackptr++ = lasttok = op; |
674 | next: ; | 700 | next: ; |
675 | } /* while (1) */ | 701 | } /* while (1) */ |
676 | 702 | ||
677 | err: | 703 | err: |
678 | numstack->val = -1; | ||
679 | errmsg = "arithmetic syntax error"; | 704 | errmsg = "arithmetic syntax error"; |
705 | err_with_custom_msg: | ||
706 | numstack->val = -1; | ||
680 | ret: | 707 | ret: |
681 | math_state->errmsg = errmsg; | 708 | math_state->errmsg = errmsg; |
682 | return numstack->val; | 709 | return numstack->val; |
diff --git a/shell/math.h b/shell/math.h index 2dcab130d..2d305eb12 100644 --- a/shell/math.h +++ b/shell/math.h | |||
@@ -9,19 +9,13 @@ | |||
9 | 9 | ||
10 | /* The math library has just one function: | 10 | /* The math library has just one function: |
11 | * | 11 | * |
12 | * arith_t arith(arith_state_t *states, const char *expr); | 12 | * arith_t arith(arith_state_t *state, const char *expr); |
13 | * | 13 | * |
14 | * The expr argument is the math string to parse. All normal expansions must | 14 | * The expr argument is the math string to parse. All normal expansions must |
15 | * be done already. i.e. no dollar symbols should be present. | 15 | * be done already. i.e. no dollar symbols should be present. |
16 | * | 16 | * |
17 | * The state argument is a pointer to a struct of hooks for your shell (see below), | 17 | * The state argument is a pointer to a struct of hooks for your shell (see below), |
18 | * and a semi-detailed error code. Currently, those values are (for | 18 | * and an error message string (NULL if no error). |
19 | * compatibility, you should assume all negative values are errors): | ||
20 | * 0 - no errors (yay!) | ||
21 | * -1 - unspecified problem | ||
22 | * -2 - divide by zero | ||
23 | * -3 - exponent less than 0 | ||
24 | * -5 - expression recursion loop detected | ||
25 | * | 19 | * |
26 | * The function returns the answer to the expression. So if you called it | 20 | * The function returns the answer to the expression. So if you called it |
27 | * with the expression: | 21 | * with the expression: |
@@ -64,12 +58,6 @@ | |||
64 | * the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]* | 58 | * the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]* |
65 | */ | 59 | */ |
66 | 60 | ||
67 | /* To make your life easier when dealing with optional 64bit math support, | ||
68 | * rather than assume that the type is "signed long" and you can always | ||
69 | * use "%ld" to scan/print the value, use the arith_t helper defines. See | ||
70 | * below for the exact things that are available. | ||
71 | */ | ||
72 | |||
73 | #ifndef SHELL_MATH_H | 61 | #ifndef SHELL_MATH_H |
74 | #define SHELL_MATH_H 1 | 62 | #define SHELL_MATH_H 1 |
75 | 63 | ||
@@ -77,11 +65,11 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
77 | 65 | ||
78 | #if ENABLE_SH_MATH_SUPPORT_64 | 66 | #if ENABLE_SH_MATH_SUPPORT_64 |
79 | typedef long long arith_t; | 67 | typedef long long arith_t; |
80 | #define arith_t_fmt "%lld" | 68 | #define ARITH_FMT "%lld" |
81 | #define strto_arith_t strtoull | 69 | #define strto_arith_t strtoull |
82 | #else | 70 | #else |
83 | typedef long arith_t; | 71 | typedef long arith_t; |
84 | #define arith_t_fmt "%ld" | 72 | #define ARITH_FMT "%ld" |
85 | #define strto_arith_t strtoul | 73 | #define strto_arith_t strtoul |
86 | #endif | 74 | #endif |
87 | 75 | ||