shell: fix parsing of $(( (v)++ + NUM ))

function                                             old     new   delta
evaluate_string                                      988    1011     +23

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2021-09-26 13:25:49 +02:00
parent 62e433131b
commit 1be73dd9ad
5 changed files with 44 additions and 22 deletions

View File

@ -2,4 +2,7 @@
1 1 1 1
1 1 1 1
1 1 1 1
6 6
7 7
7 7
Ok:0 Ok:0

View File

@ -2,4 +2,8 @@ echo 1 $((0++1))
echo 1 $((0--1)) echo 1 $((0--1))
x=-1; echo 1 $((0-$x)) x=-1; echo 1 $((0-$x))
x=+1; echo 1 $((0+$x)) x=+1; echo 1 $((0+$x))
a=3
echo 6 $((a+++3)) # a++ + 3
echo 7 $(((a)+++3)) # a + + + 3
echo 7 $(((a)+++3)) # a + + + 3
echo Ok:$? echo Ok:$?

View File

@ -2,4 +2,7 @@
1 1 1 1
1 1 1 1
1 1 1 1
6 6
7 7
7 7
Ok:0 Ok:0

View File

@ -2,4 +2,8 @@ echo 1 $((0++1))
echo 1 $((0--1)) echo 1 $((0--1))
x=-1; echo 1 $((0-$x)) x=-1; echo 1 $((0-$x))
x=+1; echo 1 $((0+$x)) x=+1; echo 1 $((0+$x))
a=3
echo 6 $((a+++3)) # a++ + 3
echo 7 $(((a)+++3)) # a + + + 3
echo 7 $(((a)+++3)) # a + + + 3
echo Ok:$? echo Ok:$?

View File

@ -116,10 +116,6 @@
#include "libbb.h" #include "libbb.h"
#include "math.h" #include "math.h"
#define lookupvar (math_state->lookupvar)
#define setvar (math_state->setvar )
//#define endofname (math_state->endofname)
typedef unsigned char operator; typedef unsigned char operator;
/* An operator's token id is a bit of a bitfield. The lower 5 bits are the /* An operator's token id is a bit of a bitfield. The lower 5 bits are the
@ -258,7 +254,7 @@ static const char*
arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
{ {
if (t->var) { if (t->var) {
const char *p = lookupvar(t->var); const char *p = math_state->lookupvar(t->var);
if (p) { if (p) {
remembered_name *cur; remembered_name *cur;
remembered_name cur_save; remembered_name cur_save;
@ -445,16 +441,15 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
if (top_of_stack->var == NULL) { if (top_of_stack->var == NULL) {
/* Hmm, 1=2 ? */ /* Hmm, 1=2 ? */
//TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8
goto err; goto err;
} }
/* Save to shell variable */ /* Save to shell variable */
sprintf(buf, ARITH_FMT, rez); sprintf(buf, ARITH_FMT, rez);
setvar(top_of_stack->var, buf); math_state->setvar(top_of_stack->var, buf);
/* After saving, make previous value for v++ or v-- */ /* After saving, make previous value for v++ or v-- */
if (op == TOK_POST_INC) if (op == TOK_POST_INC)
rez--; rez--;
else if (op == TOK_POST_DEC) if (op == TOK_POST_DEC)
rez++; rez++;
} }
@ -607,11 +602,9 @@ evaluate_string(arith_state_t *math_state, const char *expr)
const char *p; const char *p;
operator op; operator op;
operator prec; operator prec;
char arithval;
expr = skip_whitespace(expr); expr = skip_whitespace(expr);
arithval = *expr; if (*expr == '\0') {
if (arithval == '\0') {
if (expr == start_expr) { if (expr == start_expr) {
/* Null expression */ /* Null expression */
numstack->val = 0; numstack->val = 0;
@ -628,6 +621,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
* append a closing right paren * append a closing right paren
* and let the loop process it */ * and let the loop process it */
expr = ptr_to_rparen; expr = ptr_to_rparen;
//bb_error_msg("expr=')'");
continue; continue;
} }
/* At this point, we're done with the expression */ /* At this point, we're done with the expression */
@ -635,19 +629,16 @@ evaluate_string(arith_state_t *math_state, const char *expr)
/* ...but if there isn't, it's bad */ /* ...but if there isn't, it's bad */
goto err; goto err;
} }
if (numstack->var) {
/* expression is $((var)) only, lookup now */
errmsg = arith_lookup_val(math_state, numstack);
}
goto ret; goto ret;
} }
p = endofname(expr); p = endofname(expr);
if (p != expr) { if (p != expr) {
/* Name */ /* Name */
size_t var_name_size = (p-expr) + 1; /* +1 for NUL */ size_t var_name_size = (p - expr) + 1; /* +1 for NUL */
numstackptr->var = alloca(var_name_size); numstackptr->var = alloca(var_name_size);
safe_strncpy(numstackptr->var, expr, var_name_size); safe_strncpy(numstackptr->var, expr, var_name_size);
//bb_error_msg("var:'%s'", numstackptr->var);
expr = p; expr = p;
num: num:
numstackptr->second_val_present = 0; numstackptr->second_val_present = 0;
@ -656,11 +647,12 @@ evaluate_string(arith_state_t *math_state, const char *expr)
continue; continue;
} }
if (isdigit(arithval)) { if (isdigit(*expr)) {
/* Number */ /* Number */
numstackptr->var = NULL; numstackptr->var = NULL;
errno = 0; errno = 0;
numstackptr->val = strto_arith_t(expr, (char**) &expr); numstackptr->val = strto_arith_t(expr, (char**) &expr);
//bb_error_msg("val:%lld", numstackptr->val);
if (errno) if (errno)
numstackptr->val = 0; /* bash compat */ numstackptr->val = 0; /* bash compat */
goto num; goto num;
@ -671,10 +663,10 @@ evaluate_string(arith_state_t *math_state, const char *expr)
/* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized /* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized
* only if XYZ is a variable name, not a number or EXPR. IOW: * only if XYZ is a variable name, not a number or EXPR. IOW:
* "a+++v" is a++ + v. * "a+++v" is a++ + v.
* "(a)+++7" is ( a ) + + + 7.
* "7+++v" is 7 + ++v, not 7++ + v. * "7+++v" is 7 + ++v, not 7++ + v.
* "--7" is - - 7, not --7. * "--7" is - - 7, not --7.
* "++++a" is + + ++a, not ++ ++ a. * "++++a" is + + ++a, not ++ ++a.
* (we still mishandle "(a)+++7", should be treated as (a) + + + 7, but we do increment a)
*/ */
if ((expr[0] == '+' || expr[0] == '-') if ((expr[0] == '+' || expr[0] == '-')
&& (expr[1] == expr[0]) && (expr[1] == expr[0])
@ -756,26 +748,40 @@ evaluate_string(arith_state_t *math_state, const char *expr)
* "applied" in this way. * "applied" in this way.
*/ */
prec = PREC(op); prec = PREC(op);
//bb_error_msg("prec:%02x", prec);
if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
/* not left paren or unary */ /* not left paren or unary */
if (lasttok != TOK_NUM) { if (lasttok != TOK_NUM) {
/* binary op must be preceded by a num */ /* binary op must be preceded by a num */
goto err; goto err;
} }
/* The algorithm employed here is simple: while we don't
* hit an open paren nor the bottom of the stack, pop
* tokens and apply them */
while (stackptr != stack) { while (stackptr != stack) {
operator prev_op = *--stackptr; operator prev_op = *--stackptr;
if (op == TOK_RPAREN) { if (op == TOK_RPAREN) {
/* The algorithm employed here is simple: while we don't //bb_error_msg("op == TOK_RPAREN");
* hit an open paren nor the bottom of the stack, pop
* tokens and apply them */
if (prev_op == TOK_LPAREN) { if (prev_op == TOK_LPAREN) {
//bb_error_msg("prev_op == TOK_LPAREN");
//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var);
if (numstackptr[-1].var) {
/* Expression is (var), lookup now */
errmsg = arith_lookup_val(math_state, &numstackptr[-1]);
if (errmsg)
goto err_with_custom_msg;
/* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */
numstackptr[-1].var = NULL;
}
/* Any operator directly after a /* Any operator directly after a
* close paren should consider itself binary */ * close paren should consider itself binary */
lasttok = TOK_NUM; lasttok = TOK_NUM;
goto next; goto next;
} }
//bb_error_msg("prev_op != TOK_LPAREN");
} else { } else {
operator prev_prec = PREC(prev_op); operator prev_prec = PREC(prev_op);
//bb_error_msg("op != TOK_RPAREN");
fix_assignment_prec(prec); fix_assignment_prec(prec);
fix_assignment_prec(prev_prec); fix_assignment_prec(prev_prec);
if (prev_prec < prec if (prev_prec < prec
@ -785,6 +791,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
break; break;
} }
} }
//bb_error_msg("arith_apply(prev_op:%02x)", prev_op);
errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
if (errmsg) if (errmsg)
goto err_with_custom_msg; goto err_with_custom_msg;
@ -794,6 +801,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
} }
/* Push this operator to the stack and remember it */ /* Push this operator to the stack and remember it */
//bb_error_msg("push op:%02x", op);
*stackptr++ = lasttok = op; *stackptr++ = lasttok = op;
next: ; next: ;
} /* while (1) */ } /* while (1) */