~vcs-imports/busybox/trunk

« back to all changes in this revision

Viewing changes to shell/math.c

  • Committer: Denys Vlasenko
  • Author(s): Roger Knecht
  • Date: 2022-06-30 15:18:12 UTC
  • Revision ID: git-v1:20a4f70ecaad79bb932af09b7317a058872cd867
tree: new applet

Adds the tree program to list directories and files in a tree structure.

function                                             old     new   delta
tree_print                                             -     343    +343
scandir64                                              -     330    +330
scandir                                                -     330    +330
tree_main                                              -      86     +86
.rodata                                           105150  105228     +78
packed_usage                                       34511   34557     +46
alphasort64                                            -      31     +31
alphasort                                              -      31     +31
strcoll                                                -       5      +5
applet_names                                        2801    2806      +5
applet_main                                         1616    1620      +4
applet_suid                                          101     102      +1
applet_install_loc                                   202     203      +1
------------------------------------------------------------------------------
(add/remove: 11/0 grow/shrink: 6/0 up/down: 1291/0)          Total: 1291 bytes

Signed-off-by: Roger Knecht <rknecht@pm.me>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>

Show diffs side-by-side

added added

removed removed

Lines of Context:
46
46
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
47
47
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48
48
 */
 
49
 
49
50
/* This is my infix parser/evaluator. It is optimized for size, intended
50
51
 * as a replacement for yacc-based parsers. However, it may well be faster
51
52
 * than a comparable parser written in yacc. The supported operators are
60
61
 * to the stack instead of adding them to a queue to end up with an
61
62
 * expression).
62
63
 */
 
64
 
63
65
/*
64
66
 * Aug 24, 2001              Manuel Novoa III
65
67
 *
94
96
 *
95
97
 * Merge in Aaron's comments previously posted to the busybox list,
96
98
 * modified slightly to take account of my changes to the code.
 
99
 *
97
100
 */
98
101
/*
99
102
 *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
113
116
#include "libbb.h"
114
117
#include "math.h"
115
118
 
116
 
#if 1
117
 
# define dbg(...) ((void)0)
118
 
#else
119
 
# define dbg(...) bb_error_msg(__VA_ARGS__)
120
 
#endif
121
 
 
122
119
typedef unsigned char operator;
123
120
 
124
121
/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
128
125
 * Consider * and /
129
126
 */
130
127
#define tok_decl(prec,id)       (((id)<<5) | (prec))
131
 
#define ID_SHIFT                5
132
 
#define PREC(op)                ((op) & 0x1f)
 
128
#define PREC(op)                ((op) & 0x1F)
133
129
 
134
 
#define PREC_LPAREN             0
135
130
#define TOK_LPAREN              tok_decl(0,0)
136
 
/* Precedence value of RPAREN is used only to distinguish it from LPAREN */
137
 
#define TOK_RPAREN              tok_decl(1,1)
138
131
 
139
132
#define TOK_COMMA               tok_decl(1,0)
140
133
 
142
135
 * but there are 11 of them, which doesn't fit into 3 bits for unique id.
143
136
 * Abusing another precedence level:
144
137
 */
145
 
#define PREC_ASSIGN1            2
146
138
#define TOK_ASSIGN              tok_decl(2,0)
147
139
#define TOK_AND_ASSIGN          tok_decl(2,1)
148
140
#define TOK_OR_ASSIGN           tok_decl(2,2)
149
141
#define TOK_XOR_ASSIGN          tok_decl(2,3)
150
 
#define TOK_ADD_ASSIGN          tok_decl(2,4)
151
 
#define TOK_SUB_ASSIGN          tok_decl(2,5)
 
142
#define TOK_PLUS_ASSIGN         tok_decl(2,4)
 
143
#define TOK_MINUS_ASSIGN        tok_decl(2,5)
152
144
#define TOK_LSHIFT_ASSIGN       tok_decl(2,6)
153
145
#define TOK_RSHIFT_ASSIGN       tok_decl(2,7)
154
146
 
155
 
#define PREC_ASSIGN2            3
156
147
#define TOK_MUL_ASSIGN          tok_decl(3,0)
157
 
/* "/" and "/=" ops have the same id bits */
158
 
#define DIV_ID1                 1
159
 
#define TOK_DIV_ASSIGN          tok_decl(3,DIV_ID1)
 
148
#define TOK_DIV_ASSIGN          tok_decl(3,1)
160
149
#define TOK_REM_ASSIGN          tok_decl(3,2)
161
150
 
162
 
#define fix_assignment_prec(prec) do { prec -= (prec == 3); } while (0)
 
151
#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0)
163
152
 
164
153
/* Ternary conditional operator is right associative too */
165
 
/*
166
 
 * bash documentation says that precedence order is:
167
 
 *  ...
168
 
 *  expr ? expr1 : expr2
169
 
 *  = *= /= %= += -= <<= >>= &= ^= |=
170
 
 *  exprA , exprB
171
 
 * What it omits is that expr1 is parsed as if parenthesized
172
 
 * (this matches the rules of ?: in C language):
173
 
 * "v ? 1,2 : 3,4" is parsed as "(v ? (1,2) : 3),4"
174
 
 * "v ? a=2 : b=4" is parsed as "(v ? (a=1) : b)=4" (thus, this is a syntax error)
175
 
 */
176
154
#define TOK_CONDITIONAL         tok_decl(4,0)
177
155
#define TOK_CONDITIONAL_SEP     tok_decl(4,1)
178
156
 
201
179
#define TOK_SUB                 tok_decl(13,1)
202
180
 
203
181
#define TOK_MUL                 tok_decl(14,0)
204
 
#define TOK_DIV                 tok_decl(14,DIV_ID1)
 
182
#define TOK_DIV                 tok_decl(14,1)
205
183
#define TOK_REM                 tok_decl(14,2)
206
184
 
207
185
/* Exponent is right associative */
216
194
#define TOK_UPLUS               tok_decl(UNARYPREC+1,1)
217
195
 
218
196
#define PREC_PRE                (UNARYPREC+2)
219
 
#define TOK_PRE_INC             tok_decl(PREC_PRE,0)
220
 
#define TOK_PRE_DEC             tok_decl(PREC_PRE,1)
 
197
 
 
198
#define TOK_PRE_INC             tok_decl(PREC_PRE, 0)
 
199
#define TOK_PRE_DEC             tok_decl(PREC_PRE, 1)
221
200
 
222
201
#define PREC_POST               (UNARYPREC+3)
223
 
#define TOK_POST_INC            tok_decl(PREC_POST,0)
224
 
#define TOK_POST_DEC            tok_decl(PREC_POST,1)
225
 
 
226
 
/* TOK_VALUE marks a number, name, name++/name--, or (EXPR):
227
 
 * IOW: something which can be used as the left side of a binary op.
228
 
 * Since it's never pushed to opstack, its precedence does not matter.
229
 
 */
230
 
#define TOK_VALUE               tok_decl(PREC_POST,2)
 
202
 
 
203
#define TOK_POST_INC            tok_decl(PREC_POST, 0)
 
204
#define TOK_POST_DEC            tok_decl(PREC_POST, 1)
 
205
 
 
206
#define SPEC_PREC               (UNARYPREC+4)
 
207
 
 
208
#define TOK_NUM                 tok_decl(SPEC_PREC, 0)
 
209
#define TOK_RPAREN              tok_decl(SPEC_PREC, 1)
231
210
 
232
211
static int
233
212
is_assign_op(operator op)
234
213
{
235
214
        operator prec = PREC(op);
236
 
        return prec == PREC_ASSIGN1
237
 
        || prec == PREC_ASSIGN2
 
215
        fix_assignment_prec(prec);
 
216
        return prec == PREC(TOK_ASSIGN)
238
217
        || prec == PREC_PRE
239
218
        || prec == PREC_POST;
240
219
}
247
226
        || prec == PREC(TOK_CONDITIONAL);
248
227
}
249
228
 
 
229
 
250
230
typedef struct {
251
231
        arith_t val;
252
 
        const char *var_name;
 
232
        /* We acquire second_val only when "expr1 : expr2" part
 
233
         * of ternary ?: op is evaluated.
 
234
         * We treat ?: as two binary ops: (expr ? (expr1 : expr2)).
 
235
         * ':' produces a new value which has two parts, val and second_val;
 
236
         * then '?' selects one of them based on its left side.
 
237
         */
 
238
        arith_t second_val;
 
239
        char second_val_present;
 
240
        /* If NULL then it's just a number, else it's a named variable */
 
241
        char *var;
253
242
} var_or_num_t;
254
243
 
255
 
#define VALID_NAME(name) (name)
256
 
#define NOT_NAME(name)   (!(name))
257
 
 
258
244
typedef struct remembered_name {
259
245
        struct remembered_name *next;
260
 
        const char *var_name;
 
246
        const char *var;
261
247
} remembered_name;
262
248
 
263
 
static ALWAYS_INLINE int isalnum_(int c)
264
 
{
265
 
        return (isalnum(c) || c == '_');
266
 
}
267
249
 
268
250
static arith_t
269
251
evaluate_string(arith_state_t *math_state, const char *expr);
270
252
 
271
 
static arith_t
272
 
arith_lookup_val(arith_state_t *math_state, const char *name, char *endname)
 
253
static const char*
 
254
arith_lookup_val(arith_state_t *math_state, var_or_num_t *t)
273
255
{
274
 
        char c;
275
 
        const char *p;
276
 
 
277
 
        c = *endname;
278
 
        *endname = '\0';
279
 
        p = math_state->lookupvar(name);
280
 
        *endname = c;
281
 
        if (p) {
282
 
                arith_t val;
283
 
                size_t len = endname - name;
284
 
                remembered_name *cur;
285
 
                remembered_name remember;
286
 
 
287
 
                /* did we already see this name?
288
 
                 * testcase: a=b; b=a; echo $((a))
289
 
                 */
290
 
                for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) {
291
 
                        if (strncmp(cur->var_name, name, len) == 0
292
 
                         && !isalnum_(cur->var_name[len])
293
 
                        ) {
294
 
                                /* yes */
295
 
                                math_state->errmsg = "expression recursion loop detected";
296
 
                                return -1;
 
256
        if (t->var) {
 
257
                const char *p = math_state->lookupvar(t->var);
 
258
                if (p) {
 
259
                        remembered_name *cur;
 
260
                        remembered_name cur_save;
 
261
 
 
262
                        /* did we already see this name?
 
263
                         * testcase: a=b; b=a; echo $((a))
 
264
                         */
 
265
                        for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) {
 
266
                                if (strcmp(cur->var, t->var) == 0) {
 
267
                                        /* Yes */
 
268
                                        return "expression recursion loop detected";
 
269
                                }
297
270
                        }
 
271
 
 
272
                        /* push current var name */
 
273
                        cur = math_state->list_of_recursed_names;
 
274
                        cur_save.var = t->var;
 
275
                        cur_save.next = cur;
 
276
                        math_state->list_of_recursed_names = &cur_save;
 
277
 
 
278
                        /* recursively evaluate p as expression */
 
279
                        t->val = evaluate_string(math_state, p);
 
280
 
 
281
                        /* pop current var name */
 
282
                        math_state->list_of_recursed_names = cur;
 
283
 
 
284
                        return math_state->errmsg;
298
285
                }
299
 
 
300
 
                /* push current var name */
301
 
                remember.var_name = name;
302
 
                remember.next = math_state->list_of_recursed_names;
303
 
                math_state->list_of_recursed_names = &remember;
304
 
 
305
 
                /* recursively evaluate p as expression */
306
 
                /* this sets math_state->errmsg on error */
307
 
                val = evaluate_string(math_state, p);
308
 
 
309
 
                /* pop current var name */
310
 
                math_state->list_of_recursed_names = remember.next;
311
 
 
312
 
                return val;
 
286
                /* treat undefined var as 0 */
 
287
                t->val = 0;
313
288
        }
314
 
        /* treat undefined var as 0 */
315
289
        return 0;
316
290
}
317
291
 
318
292
/* "Applying" a token means performing it on the top elements on the integer
319
 
 * stack. For an unary operator it will only change the top element,
320
 
 * a binary operator will pop two arguments and push the result,
321
 
 * the ternary ?: op will pop three arguments and push the result.
322
 
 */
 
293
 * stack. For an unary operator it will only change the top element, but a
 
294
 * binary operator will pop two arguments and push the result */
323
295
static NOINLINE const char*
324
296
arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr)
325
297
{
326
 
#define NUMSTACKPTR (*numstackptr)
 
298
#define NUMPTR (*numstackptr)
327
299
 
328
300
        var_or_num_t *top_of_stack;
329
301
        arith_t rez;
 
302
        const char *err;
330
303
 
331
304
        /* There is no operator that can work without arguments */
332
 
        if (NUMSTACKPTR == numstack)
333
 
                goto syntax_err;
334
 
 
335
 
        top_of_stack = NUMSTACKPTR - 1;
336
 
 
337
 
        if (op == TOK_CONDITIONAL_SEP) {
338
 
                /* "expr1 ? expr2 : expr3" operation */
339
 
                var_or_num_t *expr1 = &top_of_stack[-2];
340
 
                NUMSTACKPTR = expr1 + 1;
341
 
                if (expr1 < numstack) /* Example: $((2:3)) */
342
 
                        return "malformed ?: operator";
343
 
                if (expr1->val != 0) /* select expr2 or expr3 */
344
 
                        top_of_stack--;
345
 
                rez = top_of_stack->val;
346
 
                top_of_stack = expr1;
347
 
                goto ret_rez;
348
 
        }
349
 
        if (op == TOK_CONDITIONAL) /* Example: $((a ? b)) */
350
 
                return "malformed ?: operator";
 
305
        if (NUMPTR == numstack)
 
306
                goto err;
 
307
 
 
308
        top_of_stack = NUMPTR - 1;
 
309
 
 
310
        /* Resolve name to value, if needed */
 
311
        err = arith_lookup_val(math_state, top_of_stack);
 
312
        if (err)
 
313
                return err;
351
314
 
352
315
        rez = top_of_stack->val;
353
316
        if (op == TOK_UMINUS)
360
323
                rez++;
361
324
        else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
362
325
                rez--;
363
 
        else /*if (op != TOK_UPLUS) - always true, we drop TOK_UPLUS earlier */ {
 
326
        else if (op != TOK_UPLUS) {
364
327
                /* Binary operators */
365
328
                arith_t right_side_val;
366
 
 
367
 
                if (top_of_stack == numstack) /* have two arguments? */
368
 
                        goto syntax_err; /* no */
369
 
 
370
 
                /* Pop numstack */
371
 
                NUMSTACKPTR = top_of_stack; /* this decrements NUMSTACKPTR */
372
 
 
373
 
                if (math_state->evaluation_disabled) {
374
 
                        dbg("binary op %02x skipped", op);
375
 
                        return NULL;
376
 
                        /* bash 5.2.12 does not execute "2/0" in disabled
377
 
                         * branches of ?: (and thus does not complain),
378
 
                         * but complains about negative exp: "2**-1".
379
 
                         * I don't think we need to emulate that.
380
 
                         */
 
329
                char bad_second_val;
 
330
 
 
331
                /* Binary operators need two arguments */
 
332
                if (top_of_stack == numstack)
 
333
                        goto err;
 
334
                /* ...and they pop one */
 
335
                NUMPTR = top_of_stack; /* this decrements NUMPTR */
 
336
 
 
337
                bad_second_val = top_of_stack->second_val_present;
 
338
                if (op == TOK_CONDITIONAL) { /* ? operation */
 
339
                        /* Make next if (...) protect against
 
340
                         * $((expr1 ? expr2)) - that is, missing ": expr" */
 
341
                        bad_second_val = !bad_second_val;
 
342
                }
 
343
                if (bad_second_val) {
 
344
                        /* Protect against $((expr <not_?_op> expr1 : expr2)) */
 
345
                        return "malformed ?: operator";
381
346
                }
382
347
 
383
348
                top_of_stack--; /* now points to left side */
 
349
 
 
350
                if (op != TOK_ASSIGN) {
 
351
                        /* Resolve left side value (unless the op is '=') */
 
352
                        err = arith_lookup_val(math_state, top_of_stack);
 
353
                        if (err)
 
354
                                return err;
 
355
                }
 
356
 
384
357
                right_side_val = rez;
385
358
                rez = top_of_stack->val;
386
 
                if (op == TOK_BOR || op == TOK_OR_ASSIGN)
 
359
                if (op == TOK_CONDITIONAL) /* ? operation */
 
360
                        rez = (rez ? right_side_val : top_of_stack[1].second_val);
 
361
                else if (op == TOK_CONDITIONAL_SEP) { /* : operation */
 
362
                        if (top_of_stack == numstack) {
 
363
                                /* Protect against $((expr : expr)) */
 
364
                                return "malformed ?: operator";
 
365
                        }
 
366
                        top_of_stack->second_val_present = op;
 
367
                        top_of_stack->second_val = right_side_val;
 
368
                }
 
369
                else if (op == TOK_BOR || op == TOK_OR_ASSIGN)
387
370
                        rez |= right_side_val;
388
371
                else if (op == TOK_OR)
389
372
                        rez = right_side_val || rez;
411
394
                        rez = (rez <= right_side_val);
412
395
                else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
413
396
                        rez *= right_side_val;
414
 
                else if (op == TOK_ADD || op == TOK_ADD_ASSIGN)
 
397
                else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
415
398
                        rez += right_side_val;
416
 
                else if (op == TOK_SUB || op == TOK_SUB_ASSIGN)
 
399
                else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
417
400
                        rez -= right_side_val;
418
401
                else if (op == TOK_ASSIGN || op == TOK_COMMA)
419
402
                        rez = right_side_val;
422
405
                        if (right_side_val < 0)
423
406
                                return "exponent less than 0";
424
407
                        c = 1;
425
 
                        while (right_side_val != 0) {
426
 
                                if ((right_side_val & 1) == 0) {
427
 
                                        /* this if() block is not necessary for correctness,
428
 
                                         * but otherwise echo $((3**999999999999999999))
429
 
                                         * takes a VERY LONG time
430
 
                                         * (and it's not interruptible by ^C)
431
 
                                         */
432
 
                                        rez *= rez;
433
 
                                        right_side_val >>= 1;
434
 
                                }
 
408
                        while (--right_side_val >= 0)
435
409
                                c *= rez;
436
 
                                right_side_val--;
437
 
                        }
438
410
                        rez = c;
439
411
                }
440
 
                else /*if (op == TOK_DIV || op == TOK_DIV_ASSIGN
441
 
                      || op == TOK_REM || op == TOK_REM_ASSIGN) - always true */
442
 
                {
443
 
                        if (right_side_val == 0)
444
 
                                return "divide by zero";
 
412
                else if (right_side_val == 0)
 
413
                        return "divide by zero";
 
414
                else if (op == TOK_DIV || op == TOK_DIV_ASSIGN
 
415
                      || op == TOK_REM || op == TOK_REM_ASSIGN) {
445
416
                        /*
446
417
                         * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))'
447
418
                         *
453
424
                         * Make sure to at least not SEGV here:
454
425
                         */
455
426
                        if (right_side_val == -1
456
 
                         && (rez << 1) == 0 /* MAX_NEGATIVE_INT or 0 */
 
427
                         && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */
457
428
                        ) {
458
429
                                right_side_val = 1;
459
430
                        }
460
 
                        if (op & (DIV_ID1 << ID_SHIFT)) /* DIV or DIV_ASSIGN? */
 
431
                        if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
461
432
                                rez /= right_side_val;
462
 
                        else
 
433
                        else {
463
434
                                rez %= right_side_val;
 
435
                        }
464
436
                }
465
437
        }
466
438
 
467
 
        if (math_state->evaluation_disabled) {
468
 
                dbg("unary op %02x skipped", op);
469
 
                return NULL;
470
 
        }
471
 
 
472
439
        if (is_assign_op(op)) {
473
440
                char buf[sizeof(arith_t)*3 + 2];
474
441
 
475
 
                if (NOT_NAME(top_of_stack->var_name)) {
 
442
                if (top_of_stack->var == NULL) {
476
443
                        /* Hmm, 1=2 ? */
477
 
                        goto syntax_err;
 
444
                        goto err;
478
445
                }
479
446
                /* Save to shell variable */
480
447
                sprintf(buf, ARITH_FMT, rez);
481
 
                {
482
 
                        char *e = (char*)endofname(top_of_stack->var_name);
483
 
                        char c = *e;
484
 
                        *e = '\0';
485
 
                        math_state->setvar(top_of_stack->var_name, buf);
486
 
                        *e = c;
487
 
                }
488
 
                /* VAR++ or VAR--? */
489
 
                if (PREC(op) == PREC_POST) {
490
 
                        /* Do not store new value to stack (keep old value) */
491
 
                        goto ret_NULL;
492
 
                }
 
448
                math_state->setvar(top_of_stack->var, buf);
 
449
                /* After saving, make previous value for v++ or v-- */
 
450
                if (op == TOK_POST_INC)
 
451
                        rez--;
 
452
                if (op == TOK_POST_DEC)
 
453
                        rez++;
493
454
        }
494
 
 ret_rez:
 
455
 
495
456
        top_of_stack->val = rez;
496
 
 ret_NULL:
497
457
        /* Erase var name, it is just a number now */
498
 
        top_of_stack->var_name = NULL;
 
458
        top_of_stack->var = NULL;
499
459
        return NULL;
500
 
 syntax_err:
 
460
 err:
501
461
        return "arithmetic syntax error";
502
 
#undef NUMSTACKPTR
 
462
#undef NUMPTR
503
463
}
504
464
 
505
465
/* longest must be first */
519
479
        '*','=',    0, TOK_MUL_ASSIGN,
520
480
        '/','=',    0, TOK_DIV_ASSIGN,
521
481
        '%','=',    0, TOK_REM_ASSIGN,
522
 
        '+','=',    0, TOK_ADD_ASSIGN,
523
 
        '-','=',    0, TOK_SUB_ASSIGN,
 
482
        '+','=',    0, TOK_PLUS_ASSIGN,
 
483
        '-','=',    0, TOK_MINUS_ASSIGN,
524
484
        '-','-',    0, TOK_POST_DEC,
525
485
        '^','=',    0, TOK_XOR_ASSIGN,
526
486
        '+','+',    0, TOK_POST_INC,
537
497
        '+',        0, TOK_ADD,
538
498
        '-',        0, TOK_SUB,
539
499
        '^',        0, TOK_BXOR,
 
500
        /* uniq */
540
501
        '~',        0, TOK_BNOT,
541
502
        ',',        0, TOK_COMMA,
542
503
        '?',        0, TOK_CONDITIONAL,
545
506
        '(',        0, TOK_LPAREN,
546
507
        0
547
508
};
548
 
#define END_POINTER (&op_tokens[sizeof(op_tokens)-1])
 
509
#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
549
510
 
550
511
#if ENABLE_FEATURE_SH_MATH_BASE
551
 
static arith_t parse_with_base(const char *nptr, char **endptr, unsigned base)
 
512
static arith_t strto_arith_t(const char *nptr, char **endptr)
552
513
{
553
 
        arith_t n = 0;
554
 
        const char *start = nptr;
555
 
 
 
514
        unsigned base;
 
515
        arith_t n;
 
516
 
 
517
# if ENABLE_FEATURE_SH_MATH_64
 
518
        n = strtoull(nptr, endptr, 0);
 
519
# else
 
520
        n = strtoul(nptr, endptr, 0);
 
521
# endif
 
522
        if (**endptr != '#'
 
523
         || (*nptr < '1' || *nptr > '9')
 
524
         || (n < 2 || n > 64)
 
525
        ) {
 
526
                return n;
 
527
        }
 
528
 
 
529
        /* It's "N#nnnn" or "NN#nnnn" syntax, NN can't start with 0,
 
530
         * NN is in 2..64 range.
 
531
         */
 
532
        base = (unsigned)n;
 
533
        n = 0;
 
534
        nptr = *endptr + 1;
556
535
        for (;;) {
557
536
                unsigned digit = (unsigned)*nptr - '0';
558
537
                if (digit >= 10 /* not 0..9 */
559
 
                 && digit <= 'z' - '0' /* reject e.g. $((64#~)) */
 
538
                 && digit <= 'z' - '0' /* needed to reject e.g. $((64#~)) */
560
539
                ) {
561
 
                        /* current char is one of :;<=>?@A..Z[\]^_`a..z */
562
 
 
563
 
                        /* in bases up to 36, case does not matter for a-z,
564
 
                         * map @A..Z and `a..z to 9..35: */
 
540
                        /* in bases up to 36, case does not matter for a-z */
565
541
                        digit = (unsigned)(*nptr | 0x20) - ('a' - 10);
566
542
                        if (base > 36 && *nptr <= '_') {
567
 
                                /* base > 36: A-Z,@,_ are 36-61,62,63 */
 
543
                                /* otherwise, A-Z,@,_ are 36-61,62,63 */
568
544
                                if (*nptr == '_')
569
545
                                        digit = 63;
570
546
                                else if (*nptr == '@')
575
551
                                        break; /* error: one of [\]^ */
576
552
                        }
577
553
                        //bb_error_msg("ch:'%c'%d digit:%u", *nptr, *nptr, digit);
578
 
                        if (digit < 10) /* reject e.g. $((36#@)) */
579
 
                                break;
 
554
                        //if (digit < 10) - example where we need this?
 
555
                        //      break;
580
556
                }
581
557
                if (digit >= base)
582
558
                        break;
584
560
                n = n * base + digit;
585
561
                nptr++;
586
562
        }
 
563
        /* Note: we do not set errno on bad chars, we just set a pointer
 
564
         * to the first invalid char. For example, this allows
 
565
         * "N#" (empty "nnnn" part): 64#+1 is a valid expression,
 
566
         * it means 64# + 1, whereas 64#~... is not, since ~ is not a valid
 
567
         * operator.
 
568
         */
587
569
        *endptr = (char*)nptr;
588
 
        /* "64#" and "64#+1" used to be valid expressions, but bash 5.2.15
589
 
         * no longer allow such, detect this:
590
 
         */
591
 
// NB: bash allows $((0x)), this is probably a bug...
592
 
        if (nptr == start)
593
 
                *endptr = NULL; /* there weren't any digits, bad */
594
570
        return n;
595
571
}
596
 
 
597
 
static arith_t strto_arith_t(const char *nptr, char **endptr)
598
 
{
599
 
/* NB: we do not use strtoull here to be bash-compatible:
600
 
 * $((99999999999999999999)) is 7766279631452241919
601
 
 * (the 64-bit truncated value).
602
 
 */
603
 
        unsigned base;
604
 
 
605
 
        /* nptr[0] is '0'..'9' here */
606
 
 
607
 
        base = nptr[0] - '0';
608
 
        if (base == 0) { /* nptr[0] is '0' */
609
 
                base = 8;
610
 
                if ((nptr[1] | 0x20) == 'x') {
611
 
                        base = 16;
612
 
                        nptr += 2;
613
 
                }
614
 
// NB: bash allows $((0x)), this is probably a bug...
615
 
                return parse_with_base(nptr, endptr, base);
616
 
        }
617
 
 
618
 
        /* base is 1..9 here */
619
 
 
620
 
        if (nptr[1] == '#') {
621
 
                if (base > 1)
622
 
                        return parse_with_base(nptr + 2, endptr, base);
623
 
                /* else: "1#NN", bash says "invalid arithmetic base" */
624
 
        }
625
 
 
626
 
        if (isdigit(nptr[1]) && nptr[2] == '#') {
627
 
                base = 10 * base + (nptr[1] - '0');
628
 
                /* base is at least 10 here */
629
 
                if (base <= 64)
630
 
                        return parse_with_base(nptr + 3, endptr, base);
631
 
                /* else: bash says "invalid arithmetic base" */
632
 
        }
633
 
 
634
 
        return parse_with_base(nptr, endptr, 10);
635
 
}
636
572
#else /* !ENABLE_FEATURE_SH_MATH_BASE */
637
573
# if ENABLE_FEATURE_SH_MATH_64
638
574
#  define strto_arith_t(nptr, endptr) strtoull(nptr, endptr, 0)
644
580
static arith_t
645
581
evaluate_string(arith_state_t *math_state, const char *expr)
646
582
{
647
 
        /* Stack of integers/names */
648
 
        var_or_num_t *numstack, *numstackptr;
649
 
        /* Stack of operator tokens */
650
 
        operator *opstack, *opstackptr;
651
 
        /* To detect whether we are after a "value": */
652
583
        operator lasttok;
653
 
        /* To insert implicit () in ?: ternary op: */
654
 
        operator insert_op = 0xff;
655
 
        unsigned ternary_level = 0;
656
584
        const char *errmsg;
657
585
        const char *start_expr = expr = skip_whitespace(expr);
658
 
 
659
 
        {
660
 
                unsigned expr_len = strlen(expr);
661
 
                /* If LOTS of whitespace, do not blow up the estimation */
662
 
                const char *p = expr;
663
 
                while (*p) {
664
 
                        /* in a run of whitespace, count only 1st char */
665
 
                        if (isspace(*p)) {
666
 
                                while (p++, isspace(*p))
667
 
                                        expr_len--;
668
 
                        } else {
669
 
                                p++;
670
 
                        }
671
 
                }
672
 
                dbg("expr:'%s' expr_len:%u", expr, expr_len);
673
 
                /* expr_len deep opstack is needed. Think "------------7".
674
 
                 * Only "?" operator temporarily needs two opstack slots
675
 
                 * (IOW: more than one slot), but its second slot (LPAREN)
676
 
                 * is popped off when ":" is reached.
677
 
                 */
678
 
                expr_len++; /* +1 for 1st LPAREN. See what $((1?)) pushes to opstack */
679
 
                opstackptr = opstack = alloca(expr_len * sizeof(opstack[0]));
680
 
                /* There can be no more than (expr_len/2 + 1)
681
 
                 * integers/names in any given correct or incorrect expression.
682
 
                 * (modulo "09", "0v" cases where 2 chars are 2 ints/names,
683
 
                 * but we have code to detect that early)
684
 
                 */
685
 
                expr_len = (expr_len / 2)
686
 
                        + 1 /* "1+2" has two nums, 2 = len/2+1, NOT len/2 */;
687
 
                numstackptr = numstack = alloca(expr_len * sizeof(numstack[0]));
688
 
        }
 
586
        unsigned expr_len = strlen(expr) + 2;
 
587
        /* Stack of integers */
 
588
        /* The proof that there can be no more than strlen(startbuf)/2+1
 
589
         * integers in any given correct or incorrect expression
 
590
         * is left as an exercise to the reader. */
 
591
        var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0]));
 
592
        var_or_num_t *numstackptr = numstack;
 
593
        /* Stack of operator tokens */
 
594
        operator *const stack = alloca(expr_len * sizeof(stack[0]));
 
595
        operator *stackptr = stack;
689
596
 
690
597
        /* Start with a left paren */
691
 
        dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack));
692
 
        *opstackptr++ = lasttok = TOK_LPAREN;
 
598
        *stackptr++ = lasttok = TOK_LPAREN;
 
599
        errmsg = NULL;
693
600
 
694
601
        while (1) {
695
602
                const char *p;
700
607
                if (*expr == '\0') {
701
608
                        if (expr == start_expr) {
702
609
                                /* Null expression */
703
 
                                return 0;
 
610
                                numstack->val = 0;
 
611
                                goto ret;
704
612
                        }
705
613
 
706
614
                        /* This is only reached after all tokens have been extracted from the
708
616
                         * are to be applied in order. At the end, there should be a final
709
617
                         * result on the integer stack */
710
618
 
711
 
                        if (expr != END_POINTER) {
 
619
                        if (expr != ptr_to_rparen + 1) {
712
620
                                /* If we haven't done so already,
713
621
                                 * append a closing right paren
714
622
                                 * and let the loop process it */
715
 
                                expr = END_POINTER;
716
 
                                op = TOK_RPAREN;
717
 
                                goto tok_found1;
 
623
                                expr = ptr_to_rparen;
 
624
//bb_error_msg("expr=')'");
 
625
                                continue;
718
626
                        }
719
627
                        /* At this point, we're done with the expression */
720
628
                        if (numstackptr != numstack + 1) {
721
 
                                /* if there is not exactly one result, it's bad */
722
 
                                /* Example: $((1 2)) */
723
 
                                goto syntax_err;
 
629
                                /* ...but if there isn't, it's bad */
 
630
                                goto err;
724
631
                        }
725
 
                        return numstack->val;
 
632
                        goto ret;
726
633
                }
727
634
 
728
635
                p = endofname(expr);
729
636
                if (p != expr) {
730
637
                        /* Name */
731
 
                        if (!math_state->evaluation_disabled) {
732
 
                                numstackptr->var_name = expr;
733
 
                                dbg("[%d] var:'%.*s'", (int)(numstackptr - numstack), (int)(p - expr), expr);
734
 
                                expr = skip_whitespace(p);
735
 
                                /* If it is not followed by "=" operator... */
736
 
                                if (expr[0] != '=' /* not "=..." */
737
 
                                 || expr[1] == '=' /* or "==..." */
738
 
                                ) {
739
 
                                        /* Evaluate variable to value */
740
 
                                        arith_t val = arith_lookup_val(math_state, numstackptr->var_name, (char*)p);
741
 
                                        if (math_state->errmsg)
742
 
                                                return val; /* -1 */
743
 
                                        numstackptr->val = val;
744
 
                                }
745
 
                        } else {
746
 
                                dbg("[%d] var:IGNORED", (int)(numstackptr - numstack));
747
 
                                expr = p;
748
 
                                numstackptr->var_name = NULL; /* not needed, paranoia */
749
 
                                numstackptr->val = 0; /* not needed, paranoia */
750
 
                        }
751
 
 push_value:
 
638
                        size_t var_name_size = (p - expr) + 1;  /* +1 for NUL */
 
639
                        numstackptr->var = alloca(var_name_size);
 
640
                        safe_strncpy(numstackptr->var, expr, var_name_size);
 
641
//bb_error_msg("var:'%s'", numstackptr->var);
 
642
                        expr = p;
 
643
 num:
 
644
                        numstackptr->second_val_present = 0;
752
645
                        numstackptr++;
753
 
                        lasttok = TOK_VALUE;
 
646
                        lasttok = TOK_NUM;
754
647
                        continue;
755
648
                }
756
649
 
757
650
                if (isdigit(*expr)) {
758
651
                        /* Number */
759
 
                        char *end;
760
 
                        numstackptr->var_name = NULL;
761
 
                        /* code is smaller compared to using &expr here: */
762
 
                        numstackptr->val = strto_arith_t(expr, &end);
763
 
                        expr = end;
764
 
                        dbg("[%d] val:%lld", (int)(numstackptr - numstack), numstackptr->val);
765
 
                        if (!expr) /* example: $((10#)) */
766
 
                                goto syntax_err;
767
 
                        /* A number can't be followed by another number, or a variable name.
768
 
                         * We'd catch this later anyway, but this would require numstack[]
769
 
                         * to be ~twice as deep to handle strings where _every_ char is
770
 
                         * a new number or name.
771
 
                         * Examples: "09" is two numbers, "0v" is number and name.
772
 
                         */
773
 
                        if (isalnum(*expr) || *expr == '_')
774
 
                                goto syntax_err;
775
 
                        goto push_value;
 
652
                        numstackptr->var = NULL;
 
653
                        errno = 0;
 
654
                        numstackptr->val = strto_arith_t(expr, (char**) &expr);
 
655
//bb_error_msg("val:%lld", numstackptr->val);
 
656
                        if (errno)
 
657
                                numstackptr->val = 0; /* bash compat */
 
658
                        goto num;
776
659
                }
777
660
 
778
661
                /* Should be an operator */
788
671
                if ((expr[0] == '+' || expr[0] == '-')
789
672
                 && (expr[1] == expr[0])
790
673
                ) {
791
 
                        if (numstackptr == numstack || NOT_NAME(numstackptr[-1].var_name)) {
792
 
                                /* not a VAR++ */
 
674
                        if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */
793
675
                                char next = skip_whitespace(expr + 2)[0];
794
 
                                if (!(isalpha(next) || next == '_')) {
795
 
                                        /* not a ++VAR */
 
676
                                if (!(isalpha(next) || next == '_')) { /* not a ++VAR */
 
677
                                        //bb_error_msg("special %c%c", expr[0], expr[0]);
796
678
                                        op = (expr[0] == '+' ? TOK_ADD : TOK_SUB);
797
679
                                        expr++;
798
680
                                        goto tok_found1;
822
704
                        if (*p == '\0') {
823
705
                                /* No next element, operator not found */
824
706
                                //math_state->syntax_error_at = expr;
825
 
                                goto syntax_err;
 
707
                                goto err;
826
708
                        }
827
709
                }
828
 
                /* NB: expr now points past the operator */
829
710
 tok_found:
830
711
                op = p[1]; /* fetch TOK_foo value */
831
 
 
832
 
                /* Special rule for "? EXPR :"
833
 
                 * "EXPR in the middle of ? : is parsed as if parenthesized"
834
 
                 * (this quirk originates in C grammar, I think).
835
 
                 */
836
 
                if (op == TOK_CONDITIONAL) {
837
 
                        insert_op = TOK_LPAREN;
838
 
                        dbg("insert_op=%02x", insert_op);
839
 
                }
840
 
                if (op == TOK_CONDITIONAL_SEP) {
841
 
                        insert_op = op;
842
 
                        op = TOK_RPAREN;
843
 
                        dbg("insert_op=%02x op=%02x", insert_op, op);
844
 
                }
845
712
 tok_found1:
846
 
                /* NAME++ is a "value" (something suitable for a binop) */
847
 
                if (PREC(lasttok) == PREC_POST)
848
 
                        lasttok = TOK_VALUE;
 
713
                /* NB: expr now points past the operator */
 
714
 
 
715
                /* post grammar: a++ reduce to num */
 
716
                if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
 
717
                        lasttok = TOK_NUM;
849
718
 
850
719
                /* Plus and minus are binary (not unary) _only_ if the last
851
 
                 * token was a "value". Think about it. It makes sense.
852
 
                 */
853
 
                if (lasttok != TOK_VALUE) {
 
720
                 * token was a number, or a right paren (which pretends to be
 
721
                 * a number, since it evaluates to one). Think about it.
 
722
                 * It makes sense. */
 
723
                if (lasttok != TOK_NUM) {
854
724
                        switch (op) {
855
725
                        case TOK_ADD:
856
 
                                //op = TOK_UPLUS;
857
 
                                //break;
858
 
                                /* Unary plus does nothing, do not even push it to opstack */
859
 
                                continue;
 
726
                                op = TOK_UPLUS;
 
727
                                break;
860
728
                        case TOK_SUB:
861
729
                                op = TOK_UMINUS;
862
730
                                break;
876
744
                 * stack until we find an operator with a lesser priority than the
877
745
                 * one we have just extracted. If op is right-associative,
878
746
                 * then stop "applying" on the equal priority too.
879
 
                 * Left paren will never be "applied" in this way.
 
747
                 * Left paren is given the lowest priority so it will never be
 
748
                 * "applied" in this way.
880
749
                 */
881
750
                prec = PREC(op);
882
 
                if (prec != PREC_LPAREN && prec < UNARYPREC) {
883
 
                        /* Binary, ternary or RPAREN */
884
 
                        if (lasttok != TOK_VALUE) {
885
 
                                /* Must be preceded by a value.
886
 
                                 * $((2 2 + * 3)) would be accepted without this.
887
 
                                 */
888
 
                                goto syntax_err;
 
751
//bb_error_msg("prec:%02x", prec);
 
752
                if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
 
753
                        /* not left paren or unary */
 
754
                        if (lasttok != TOK_NUM) {
 
755
                                /* binary op must be preceded by a num */
 
756
                                goto err;
889
757
                        }
890
 
                        /* if op is RPAREN:
891
 
                         *     while opstack is not empty:
892
 
                         *         pop prev_op
893
 
                         *         if prev_op is LPAREN (finished evaluating (EXPR)):
894
 
                         *             goto N
895
 
                         *         evaluate prev_op on top of numstack
896
 
                         *     BUG (unpaired RPAREN)
897
 
                         * else (op is not RPAREN):
898
 
                         *     while opstack is not empty:
899
 
                         *         pop prev_op
900
 
                         *         if can't evaluate prev_op (it is lower precedence than op):
901
 
                         *             push prev_op back
902
 
                         *             goto C
903
 
                         *         evaluate prev_op on top of numstack
904
 
                         *     C:if op is "?": check result, set disable flag if needed
905
 
                         * push op
906
 
                         * N:loop to parse the rest of string
907
 
                         */
908
 
                        while (opstackptr != opstack) {
909
 
                                operator prev_op = *--opstackptr;
 
758
                        /* The algorithm employed here is simple: while we don't
 
759
                         * hit an open paren nor the bottom of the stack, pop
 
760
                         * tokens and apply them */
 
761
                        while (stackptr != stack) {
 
762
                                operator prev_op = *--stackptr;
910
763
                                if (op == TOK_RPAREN) {
 
764
//bb_error_msg("op == TOK_RPAREN");
911
765
                                        if (prev_op == TOK_LPAREN) {
912
 
                                                /* Erase var name: for example, (VAR) = 1 is not valid */
913
 
                                                numstackptr[-1].var_name = NULL;
914
 
                                                /* (EXPR) is a "value": next operator directly after
915
 
                                                 * close paren should be considered binary
916
 
                                                 */
917
 
                                                lasttok = TOK_VALUE;
 
766
//bb_error_msg("prev_op == TOK_LPAREN");
 
767
//bb_error_msg("  %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var);
 
768
                                                if (numstackptr[-1].var) {
 
769
                                                        /* Expression is (var), lookup now */
 
770
                                                        errmsg = arith_lookup_val(math_state, &numstackptr[-1]);
 
771
                                                        if (errmsg)
 
772
                                                                goto err_with_custom_msg;
 
773
                                                        /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */
 
774
                                                        numstackptr[-1].var = NULL;
 
775
                                                }
 
776
                                                /* Any operator directly after a
 
777
                                                 * close paren should consider itself binary */
 
778
                                                lasttok = TOK_NUM;
918
779
                                                goto next;
919
780
                                        }
920
 
                                        /* Not (y), but ...x~y). Fall through to evaluate x~y */
 
781
//bb_error_msg("prev_op != TOK_LPAREN");
921
782
                                } else {
922
783
                                        operator prev_prec = PREC(prev_op);
 
784
//bb_error_msg("op != TOK_RPAREN");
923
785
                                        fix_assignment_prec(prec);
924
786
                                        fix_assignment_prec(prev_prec);
925
787
                                        if (prev_prec < prec
926
788
                                         || (prev_prec == prec && is_right_associative(prec))
927
789
                                        ) {
928
 
                                                /* ...x~y@. push @ on opstack */
929
 
                                                opstackptr++; /* undo removal of ~ op */
930
 
                                                goto check_cond;
 
790
                                                stackptr++;
 
791
                                                break;
931
792
                                        }
932
 
                                        /* else: ...x~y@. Evaluate x~y, replace it on stack with result. Then repeat */
933
793
                                }
934
 
                                dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack));
 
794
//bb_error_msg("arith_apply(prev_op:%02x)", prev_op);
935
795
                                errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
936
796
                                if (errmsg)
937
797
                                        goto err_with_custom_msg;
938
 
dbg("    numstack:%d val:%lld '%s'", (int)(numstackptr - numstack), numstackptr[-1].val, numstackptr[-1].var_name);
939
 
                                if (prev_op == TOK_CONDITIONAL_SEP) {
940
 
                                        /* We just executed ":" */
941
 
                                        /* Remove "?" from opstack too, not just ":" */
942
 
                                        opstackptr--;
943
 
                                        if (*opstackptr != TOK_CONDITIONAL) {
944
 
                                                /* Example: $((1,2:3)) */
945
 
                                                errmsg = "malformed ?: operator";
946
 
                                                goto err_with_custom_msg;
947
 
                                        }
948
 
                                        /* Example: a=1?2:3,a. We just executed ":".
949
 
                                         * Prevent assignment from being still disabled.
950
 
                                         */
951
 
                                        if (ternary_level == math_state->evaluation_disabled) {
952
 
                                                math_state->evaluation_disabled = 0;
953
 
                                                dbg("':' executed: evaluation_disabled=CLEAR");
954
 
                                        }
955
 
                                        ternary_level--;
956
 
                                }
957
 
                        } /* while (opstack not empty) */
958
 
 
959
 
                        if (op == TOK_RPAREN) /* unpaired RPAREN? */
960
 
                                goto syntax_err;
961
 
 check_cond:
962
 
                        if (op == TOK_CONDITIONAL) {
963
 
                                /* We just now evaluated EXPR before "?".
964
 
                                 * Should we disable evaluation now?
965
 
                                 */
966
 
                                ternary_level++;
967
 
                                if (numstackptr[-1].val == 0 && !math_state->evaluation_disabled) {
968
 
                                        math_state->evaluation_disabled = ternary_level;
969
 
                                        dbg("'?' entered: evaluation_disabled=%u", math_state->evaluation_disabled);
970
 
                                }
971
 
                        }
972
 
                } /* if */
973
 
                /* else: LPAREN or UNARY: push it on opstack */
974
 
 
975
 
                /* Push this operator to opstack */
976
 
                dbg("(%d) op:%02x insert_op:%02x", (int)(opstackptr - opstack), op, insert_op);
977
 
                *opstackptr++ = lasttok = op;
978
 
 next:
979
 
                if (insert_op != 0xff) {
980
 
                        op = insert_op;
981
 
                        insert_op = 0xff;
982
 
                        dbg("inserting %02x", op);
983
 
                        if (op == TOK_CONDITIONAL_SEP) {
984
 
                                /* The next token is ":". Toggle "do not evaluate" state */
985
 
                                if (!math_state->evaluation_disabled) {
986
 
                                        math_state->evaluation_disabled = ternary_level;
987
 
                                        dbg("':' entered: evaluation_disabled=%u", math_state->evaluation_disabled);
988
 
                                } else if (ternary_level == math_state->evaluation_disabled) {
989
 
                                        math_state->evaluation_disabled = 0;
990
 
                                        dbg("':' entered: evaluation_disabled=CLEAR");
991
 
                                } /* else: ternary_level > evaluation_disabled && evaluation_disabled != 0 */
992
 
                                        /* We are in nested "?:" while in outer "?:" disabled branch */
993
 
                                        /* do_nothing */
994
 
                        }
995
 
                        goto tok_found1;
 
798
                        }
 
799
                        if (op == TOK_RPAREN)
 
800
                                goto err;
996
801
                }
 
802
 
 
803
                /* Push this operator to the stack and remember it */
 
804
//bb_error_msg("push op:%02x", op);
 
805
                *stackptr++ = lasttok = op;
 
806
 next: ;
997
807
        } /* while (1) */
998
808
 
999
 
 syntax_err:
 
809
 err:
1000
810
        errmsg = "arithmetic syntax error";
1001
811
 err_with_custom_msg:
 
812
        numstack->val = -1;
 
813
 ret:
1002
814
        math_state->errmsg = errmsg;
1003
 
        return -1;
 
815
        return numstack->val;
1004
816
}
1005
817
 
1006
818
arith_t FAST_FUNC
1007
819
arith(arith_state_t *math_state, const char *expr)
1008
820
{
1009
 
        math_state->evaluation_disabled = 0;
1010
821
        math_state->errmsg = NULL;
1011
822
        math_state->list_of_recursed_names = NULL;
1012
823
        return evaluate_string(math_state, expr);