busybox/coreutils/expr.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini expr implementation for busybox
   4 *
   5 * based on GNU expr Mike Parker.
   6 * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
   7 *
   8 * Busybox modifications
   9 * Copyright (c) 2000  Edward Betts <edward@debian.org>.
  10 * Copyright (C) 2003-2005  Vladimir Oleynik <dzo@simtreas.ru>
  11 *  - reduced 464 bytes.
  12 *  - 64 math support
  13 *
  14 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  15 */
  16/* This program evaluates expressions.  Each token (operator, operand,
  17 * parenthesis) of the expression must be a separate argument.  The
  18 * parser used is a reasonably general one, though any incarnation of
  19 * it is language-specific.  It is especially nice for expressions.
  20 *
  21 * No parse tree is needed; a new node is evaluated immediately.
  22 * One function can handle multiple operators all of equal precedence,
  23 * provided they all associate ((x op x) op x).
  24 */
  25//config:config EXPR
  26//config:       bool "expr (6.6 kb)"
  27//config:       default y
  28//config:       help
  29//config:       expr is used to calculate numbers and print the result
  30//config:       to standard output.
  31//config:
  32//config:config EXPR_MATH_SUPPORT_64
  33//config:       bool "Extend Posix numbers support to 64 bit"
  34//config:       default y
  35//config:       depends on EXPR
  36//config:       help
  37//config:       Enable 64-bit math support in the expr applet. This will make
  38//config:       the applet slightly larger, but will allow computation with very
  39//config:       large numbers.
  40
  41//applet:IF_EXPR(APPLET_NOEXEC(expr, expr, BB_DIR_USR_BIN, BB_SUID_DROP, expr))
  42
  43//kbuild:lib-$(CONFIG_EXPR) += expr.o
  44
  45//usage:#define expr_trivial_usage
  46//usage:       "EXPRESSION"
  47//usage:#define expr_full_usage "\n\n"
  48//usage:       "Print the value of EXPRESSION\n"
  49//usage:    "\n"
  50//usage:       "EXPRESSION may be:\n"
  51//usage:       "        ARG1 | ARG2     ARG1 if it is neither null nor 0, otherwise ARG2\n"
  52//usage:       "        ARG1 & ARG2     ARG1 if neither argument is null or 0, otherwise 0\n"
  53//usage:       "        ARG1 < ARG2     1 if ARG1 is less than ARG2, else 0. Similarly:\n"
  54//usage:       "        ARG1 <= ARG2\n"
  55//usage:       "        ARG1 = ARG2\n"
  56//usage:       "        ARG1 != ARG2\n"
  57//usage:       "        ARG1 >= ARG2\n"
  58//usage:       "        ARG1 > ARG2\n"
  59//usage:       "        ARG1 + ARG2     Sum of ARG1 and ARG2. Similarly:\n"
  60//usage:       "        ARG1 - ARG2\n"
  61//usage:       "        ARG1 * ARG2\n"
  62//usage:       "        ARG1 / ARG2\n"
  63//usage:       "        ARG1 % ARG2\n"
  64//usage:       "        STRING : REGEXP         Anchored pattern match of REGEXP in STRING\n"
  65//usage:       "        match STRING REGEXP     Same as STRING : REGEXP\n"
  66//usage:       "        substr STRING POS LEN   Substring of STRING, POS counts from 1\n"
  67//usage:       "        index STRING CHARS      Index in STRING where any CHARS is found, or 0\n"
  68//usage:       "        length STRING           Length of STRING\n"
  69//usage:       "        quote TOKEN             Interpret TOKEN as a string, even if\n"
  70//usage:       "                                it is a keyword like 'match' or an\n"
  71//usage:       "                                operator like '/'\n"
  72//usage:       "        (EXPRESSION)            Value of EXPRESSION\n"
  73//usage:       "\n"
  74//usage:       "Beware that many operators need to be escaped or quoted for shells.\n"
  75//usage:       "Comparisons are arithmetic if both ARGs are numbers, else\n"
  76//usage:       "lexicographical. Pattern matches return the string matched between\n"
  77//usage:       "\\( and \\) or null; if \\( and \\) are not used, they return the number\n"
  78//usage:       "of characters matched or 0."
  79
  80#include "libbb.h"
  81#include "common_bufsiz.h"
  82#include "xregex.h"
  83
  84#if ENABLE_EXPR_MATH_SUPPORT_64
  85typedef int64_t arith_t;
  86
  87#define PF_REZ      "ll"
  88#define PF_REZ_TYPE (long long)
  89#define STRTOL(s, e, b) strtoll(s, e, b)
  90#else
  91typedef long arith_t;
  92
  93#define PF_REZ      "l"
  94#define PF_REZ_TYPE (long)
  95#define STRTOL(s, e, b) strtol(s, e, b)
  96#endif
  97
  98/* TODO: use bb_strtol[l]? It's easier to check for errors... */
  99
 100/* The kinds of value we can have.  */
 101enum {
 102        INTEGER,
 103        STRING
 104};
 105
 106/* A value is.... */
 107struct valinfo {
 108        smallint type;                  /* Which kind. */
 109        union {                         /* The value itself. */
 110                arith_t i;
 111                char *s;
 112        } u;
 113};
 114typedef struct valinfo VALUE;
 115
 116/* The arguments given to the program, minus the program name.  */
 117struct globals {
 118        char **args;
 119} FIX_ALIASING;
 120#define G (*(struct globals*)bb_common_bufsiz1)
 121#define INIT_G() do { \
 122        setup_common_bufsiz(); \
 123        /* NB: noexec applet - globals not zeroed */ \
 124} while (0)
 125
 126/* forward declarations */
 127static VALUE *eval(void);
 128
 129
 130/* Return a VALUE for I.  */
 131
 132static VALUE *int_value(arith_t i)
 133{
 134        VALUE *v;
 135
 136        v = xzalloc(sizeof(VALUE));
 137        if (INTEGER) /* otherwise xzalloc did it already */
 138                v->type = INTEGER;
 139        v->u.i = i;
 140        return v;
 141}
 142
 143/* Return a VALUE for S.  */
 144
 145static VALUE *str_value(const char *s)
 146{
 147        VALUE *v;
 148
 149        v = xzalloc(sizeof(VALUE));
 150        if (STRING) /* otherwise xzalloc did it already */
 151                v->type = STRING;
 152        v->u.s = xstrdup(s);
 153        return v;
 154}
 155
 156/* Free VALUE V, including structure components.  */
 157
 158static void freev(VALUE *v)
 159{
 160        if (v->type == STRING)
 161                free(v->u.s);
 162        free(v);
 163}
 164
 165/* Return nonzero if V is a null-string or zero-number.  */
 166
 167static int null(VALUE *v)
 168{
 169        if (v->type == INTEGER)
 170                return v->u.i == 0;
 171        /* STRING: */
 172        return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0');
 173}
 174
 175/* Coerce V to a STRING value (can't fail).  */
 176
 177static void tostring(VALUE *v)
 178{
 179        if (v->type == INTEGER) {
 180                v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
 181                v->type = STRING;
 182        }
 183}
 184
 185/* Coerce V to an INTEGER value.  Return 1 on success, 0 on failure.  */
 186
 187static bool toarith(VALUE *v)
 188{
 189        if (v->type == STRING) {
 190                arith_t i;
 191                char *e;
 192
 193                /* Don't interpret the empty string as an integer.  */
 194                /* Currently does not worry about overflow or int/long differences. */
 195                i = STRTOL(v->u.s, &e, 10);
 196                if ((v->u.s == e) || *e)
 197                        return 0;
 198                free(v->u.s);
 199                v->u.i = i;
 200                v->type = INTEGER;
 201        }
 202        return 1;
 203}
 204
 205/* Return str[0]+str[1] if the next token matches STR exactly.
 206   STR must not be NULL.  */
 207
 208static int nextarg(const char *str)
 209{
 210        if (*G.args == NULL || strcmp(*G.args, str) != 0)
 211                return 0;
 212        return (unsigned char)str[0] + (unsigned char)str[1];
 213}
 214
 215/* The comparison operator handling functions.  */
 216
 217static int cmp_common(VALUE *l, VALUE *r, int op)
 218{
 219        arith_t ll, rr;
 220
 221        ll = l->u.i;
 222        rr = r->u.i;
 223        if (l->type == STRING || r->type == STRING) {
 224                tostring(l);
 225                tostring(r);
 226                ll = strcmp(l->u.s, r->u.s);
 227                rr = 0;
 228        }
 229        /* calculating ll - rr and checking the result is prone to overflows.
 230         * We'll do it differently: */
 231        if (op == '<')
 232                return ll < rr;
 233        if (op == ('<' + '='))
 234                return ll <= rr;
 235        if (op == '=' || (op == '=' + '='))
 236                return ll == rr;
 237        if (op == '!' + '=')
 238                return ll != rr;
 239        if (op == '>')
 240                return ll > rr;
 241        /* >= */
 242        return ll >= rr;
 243}
 244
 245/* The arithmetic operator handling functions.  */
 246
 247static arith_t arithmetic_common(VALUE *l, VALUE *r, int op)
 248{
 249        arith_t li, ri;
 250
 251        if (!toarith(l) || !toarith(r))
 252                bb_simple_error_msg_and_die("non-numeric argument");
 253        li = l->u.i;
 254        ri = r->u.i;
 255        if (op == '+')
 256                return li + ri;
 257        if (op == '-')
 258                return li - ri;
 259        if (op == '*')
 260                return li * ri;
 261        if (ri == 0)
 262                bb_simple_error_msg_and_die("division by zero");
 263        if (op == '/')
 264                return li / ri;
 265        return li % ri;
 266}
 267
 268/* Do the : operator.
 269   SV is the VALUE for the lhs (the string),
 270   PV is the VALUE for the rhs (the pattern).  */
 271
 272static VALUE *docolon(VALUE *sv, VALUE *pv)
 273{
 274        enum { NMATCH = 2 };
 275        VALUE *v;
 276        regex_t re_buffer;
 277        regmatch_t re_regs[NMATCH];
 278
 279        tostring(sv);
 280        tostring(pv);
 281
 282        if (pv->u.s[0] == '^') {
 283                bb_error_msg(
 284"warning: '%s': using '^' as the first character\n"
 285"of a basic regular expression is not portable; it is ignored", pv->u.s);
 286        }
 287
 288        memset(&re_buffer, 0, sizeof(re_buffer));
 289        memset(re_regs, 0, sizeof(re_regs));
 290        xregcomp(&re_buffer, pv->u.s, 0);
 291
 292        /* expr uses an anchored pattern match, so check that there was a
 293         * match and that the match starts at offset 0. */
 294        if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH
 295         && re_regs[0].rm_so == 0
 296        ) {
 297                /* Were \(...\) used? */
 298                if (re_buffer.re_nsub > 0 && re_regs[1].rm_so >= 0) {
 299                        sv->u.s[re_regs[1].rm_eo] = '\0';
 300                        v = str_value(sv->u.s + re_regs[1].rm_so);
 301                } else {
 302                        v = int_value(re_regs[0].rm_eo);
 303                }
 304        } else {
 305                /* Match failed -- return the right kind of null.  */
 306                if (re_buffer.re_nsub > 0)
 307                        v = str_value("");
 308                else
 309                        v = int_value(0);
 310        }
 311        regfree(&re_buffer);
 312        return v;
 313}
 314
 315/* Handle bare operands and ( expr ) syntax.  */
 316
 317static VALUE *eval7(void)
 318{
 319        VALUE *v;
 320
 321        if (!*G.args)
 322                bb_simple_error_msg_and_die("syntax error");
 323
 324        if (nextarg("(")) {
 325                G.args++;
 326                v = eval();
 327                if (!nextarg(")"))
 328                        bb_simple_error_msg_and_die("syntax error");
 329                G.args++;
 330                return v;
 331        }
 332
 333        if (nextarg(")"))
 334                bb_simple_error_msg_and_die("syntax error");
 335
 336        return str_value(*G.args++);
 337}
 338
 339/* Handle match, substr, index, length, and quote keywords.  */
 340
 341static VALUE *eval6(void)
 342{
 343        static const char keywords[] ALIGN1 =
 344                "quote\0""length\0""match\0""index\0""substr\0";
 345
 346        VALUE *r, *i1, *i2;
 347        VALUE *l = l; /* silence gcc */
 348        VALUE *v = v; /* silence gcc */
 349        int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0;
 350
 351        if (key == 0) /* not a keyword */
 352                return eval7();
 353        G.args++; /* We have a valid token, so get the next argument.  */
 354        if (key == 1) { /* quote */
 355                if (!*G.args)
 356                        bb_simple_error_msg_and_die("syntax error");
 357                return str_value(*G.args++);
 358        }
 359        if (key == 2) { /* length */
 360                r = eval6();
 361                tostring(r);
 362                v = int_value(strlen(r->u.s));
 363                freev(r);
 364        } else
 365                l = eval6();
 366
 367        if (key == 3) { /* match */
 368                r = eval6();
 369                v = docolon(l, r);
 370                freev(l);
 371                freev(r);
 372        }
 373        if (key == 4) { /* index */
 374                r = eval6();
 375                tostring(l);
 376                tostring(r);
 377                v = int_value(strcspn(l->u.s, r->u.s) + 1);
 378                if (v->u.i == (arith_t) strlen(l->u.s) + 1)
 379                        v->u.i = 0;
 380                freev(l);
 381                freev(r);
 382        }
 383        if (key == 5) { /* substr */
 384                i1 = eval6();
 385                i2 = eval6();
 386                tostring(l);
 387                if (!toarith(i1) || !toarith(i2)
 388                 || i1->u.i > (arith_t) strlen(l->u.s)
 389                 || i1->u.i <= 0 || i2->u.i <= 0)
 390                        v = str_value("");
 391                else {
 392                        v = xmalloc(sizeof(VALUE));
 393                        v->type = STRING;
 394                        v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
 395                }
 396                freev(l);
 397                freev(i1);
 398                freev(i2);
 399        }
 400        return v;
 401}
 402
 403/* Handle : operator (pattern matching).
 404   Calls docolon to do the real work.  */
 405
 406static VALUE *eval5(void)
 407{
 408        VALUE *l, *r, *v;
 409
 410        l = eval6();
 411        while (nextarg(":")) {
 412                G.args++;
 413                r = eval6();
 414                v = docolon(l, r);
 415                freev(l);
 416                freev(r);
 417                l = v;
 418        }
 419        return l;
 420}
 421
 422/* Handle *, /, % operators.  */
 423
 424static VALUE *eval4(void)
 425{
 426        VALUE *l, *r;
 427        int op;
 428        arith_t val;
 429
 430        l = eval5();
 431        while (1) {
 432                op = nextarg("*");
 433                if (!op) { op = nextarg("/");
 434                 if (!op) { op = nextarg("%");
 435                  if (!op) return l;
 436                }}
 437                G.args++;
 438                r = eval5();
 439                val = arithmetic_common(l, r, op);
 440                freev(l);
 441                freev(r);
 442                l = int_value(val);
 443        }
 444}
 445
 446/* Handle +, - operators.  */
 447
 448static VALUE *eval3(void)
 449{
 450        VALUE *l, *r;
 451        int op;
 452        arith_t val;
 453
 454        l = eval4();
 455        while (1) {
 456                op = nextarg("+");
 457                if (!op) {
 458                        op = nextarg("-");
 459                        if (!op) return l;
 460                }
 461                G.args++;
 462                r = eval4();
 463                val = arithmetic_common(l, r, op);
 464                freev(l);
 465                freev(r);
 466                l = int_value(val);
 467        }
 468}
 469
 470/* Handle comparisons.  */
 471
 472static VALUE *eval2(void)
 473{
 474        VALUE *l, *r;
 475        int op;
 476        arith_t val;
 477
 478        l = eval3();
 479        while (1) {
 480                op = nextarg("<");
 481                if (!op) { op = nextarg("<=");
 482                 if (!op) { op = nextarg("=");
 483                  if (!op) { op = nextarg("==");
 484                   if (!op) { op = nextarg("!=");
 485                    if (!op) { op = nextarg(">=");
 486                     if (!op) { op = nextarg(">");
 487                      if (!op) return l;
 488                }}}}}}
 489                G.args++;
 490                r = eval3();
 491                toarith(l);
 492                toarith(r);
 493                val = cmp_common(l, r, op);
 494                freev(l);
 495                freev(r);
 496                l = int_value(val);
 497        }
 498}
 499
 500/* Handle &.  */
 501
 502static VALUE *eval1(void)
 503{
 504        VALUE *l, *r;
 505
 506        l = eval2();
 507        while (nextarg("&")) {
 508                G.args++;
 509                r = eval2();
 510                if (null(l) || null(r)) {
 511                        freev(l);
 512                        freev(r);
 513                        l = int_value(0);
 514                } else
 515                        freev(r);
 516        }
 517        return l;
 518}
 519
 520/* Handle |.  */
 521
 522static VALUE *eval(void)
 523{
 524        VALUE *l, *r;
 525
 526        l = eval1();
 527        while (nextarg("|")) {
 528                G.args++;
 529                r = eval1();
 530                if (null(l)) {
 531                        freev(l);
 532                        l = r;
 533                } else
 534                        freev(r);
 535        }
 536        return l;
 537}
 538
 539int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 540int expr_main(int argc UNUSED_PARAM, char **argv)
 541{
 542        VALUE *v;
 543
 544        INIT_G();
 545
 546        xfunc_error_retval = 2; /* coreutils compat */
 547        G.args = argv + 1;
 548        if (*G.args == NULL) {
 549                bb_simple_error_msg_and_die("too few arguments");
 550        }
 551        v = eval();
 552        if (*G.args)
 553                bb_simple_error_msg_and_die("syntax error");
 554        if (v->type == INTEGER)
 555                printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
 556        else
 557                puts(v->u.s);
 558        fflush_stdout_and_exit(null(v));
 559}
 560