From 1b355ebba68bdd567dd3961a18291dfd9532c2e8 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Tue, 5 Sep 2000 17:37:48 +0000 Subject: Added expr, from Edward Betts , with some fixups and docs added by me. -Erik --- expr.c | 531 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 531 insertions(+) create mode 100644 expr.c (limited to 'expr.c') diff --git a/expr.c b/expr.c new file mode 100644 index 000000000..9e3c04a11 --- /dev/null +++ b/expr.c @@ -0,0 +1,531 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the gnu + * general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa + * + */ + +/* This program evaluates expressions. Each token (operator, operand, + * parenthesis) of the expression must be a seperate argument. The + * parser used is a reasonably general one, though any incarnation of + * it is language-specific. It is especially nice for expressions. + * + * No parse tree is needed; a new node is evaluated immediately. + * One function can handle multiple operators all of equal precedence, + * provided they all associate ((x op x) op x). */ + +#include "internal.h" +#include +#include + +#include + +/* The kinds of value we can have. */ +enum valtype { + integer, + string +}; +typedef enum valtype TYPE; + +/* A value is.... */ +struct valinfo { + TYPE type; /* Which kind. */ + union { /* The value itself. */ + int i; + char *s; + } u; +}; +typedef struct valinfo VALUE; + +/* The arguments given to the program, minus the program name. */ +static char **args; + +static VALUE *docolon (VALUE *sv, VALUE *pv); +static VALUE *eval (void); +static VALUE *int_value (int i); +static VALUE *str_value (char *s); +static int nextarg (char *str); +static int null (VALUE *v); +static int toarith (VALUE *v); +static void freev (VALUE *v); +static void tostring (VALUE *v); + +int expr_main (int argc, char **argv) +{ + VALUE *v; + + if (argc == 1) { + fatalError("too few arguments\n"); + } + + args = argv + 1; + + v = eval (); + if (*args) + fatalError ("syntax error\n"); + + if (v->type == integer) + printf ("%d\n", v->u.i); + else + printf ("%s\n", v->u.s); + + exit (null (v)); +} + +/* Return a VALUE for I. */ + +static VALUE *int_value (int i) +{ + VALUE *v; + + v = xmalloc (sizeof(VALUE)); + v->type = integer; + v->u.i = i; + return v; +} + +/* Return a VALUE for S. */ + +static VALUE *str_value (char *s) +{ + VALUE *v; + + v = xmalloc (sizeof(VALUE)); + v->type = string; + v->u.s = strdup (s); + return v; +} + +/* Free VALUE V, including structure components. */ + +static void freev (VALUE *v) +{ + if (v->type == string) + free (v->u.s); + free (v); +} + +/* Return nonzero if V is a null-string or zero-number. */ + +static int null (VALUE *v) +{ + switch (v->type) { + case integer: + return v->u.i == 0; + case string: + return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; + default: + abort (); + } +} + +/* Coerce V to a string value (can't fail). */ + +static void tostring (VALUE *v) +{ + char *temp; + + if (v->type == integer) { + temp = xmalloc (4 * (sizeof (int) / sizeof (char))); + sprintf (temp, "%d", v->u.i); + v->u.s = temp; + v->type = string; + } +} + +/* Coerce V to an integer value. Return 1 on success, 0 on failure. */ + +static int toarith (VALUE *v) +{ + int i; + + switch (v->type) { + case integer: + return 1; + case string: + i = 0; + /* Don't interpret the empty string as an integer. */ + if (v->u.s == 0) + return 0; + i = atoi(v->u.s); + free (v->u.s); + v->u.i = i; + v->type = integer; + return 1; + default: + abort (); + } +} + +/* Return nonzero if the next token matches STR exactly. + STR must not be NULL. */ + +static int +nextarg (char *str) +{ + if (*args == NULL) + return 0; + return strcmp (*args, str) == 0; +} + +/* The comparison operator handling functions. */ + +#define cmpf(name, rel) \ +static int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (l->type == string || r->type == string) { \ + tostring (l); \ + tostring (r); \ + return strcmp (l->u.s, r->u.s) rel 0; \ + } \ + else \ + return l->u.i rel r->u.i; \ +} + cmpf (less_than, <) + cmpf (less_equal, <=) + cmpf (equal, ==) + cmpf (not_equal, !=) + cmpf (greater_equal, >=) + cmpf (greater_than, >) + +#undef cmpf + +/* The arithmetic operator handling functions. */ + +#define arithf(name, op) \ +static \ +int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (!toarith (l) || !toarith (r)) \ + fatalError ("non-numeric argument\n"); \ + return l->u.i op r->u.i; \ +} + +#define arithdivf(name, op) \ +int name (l, r) VALUE *l; VALUE *r; \ +{ \ + if (!toarith (l) || !toarith (r)) \ + fatalError ( "non-numeric argument\n"); \ + if (r->u.i == 0) \ + fatalError ( "division by zero\n"); \ + return l->u.i op r->u.i; \ +} + + arithf (plus, +) + arithf (minus, -) + arithf (multiply, *) + arithdivf (divide, /) + arithdivf (mod, %) + +#undef arithf +#undef arithdivf + +/* Do the : operator. + SV is the VALUE for the lhs (the string), + PV is the VALUE for the rhs (the pattern). */ + +static VALUE *docolon (VALUE *sv, VALUE *pv) +{ + VALUE *v; + const char *errmsg; + struct re_pattern_buffer re_buffer; + struct re_registers re_regs; + int len; + + tostring (sv); + tostring (pv); + + if (pv->u.s[0] == '^') { + fprintf (stderr, "\ +warning: unportable BRE: `%s': using `^' as the first character\n\ +of a basic regular expression is not portable; it is being ignored", + pv->u.s); + } + + len = strlen (pv->u.s); + memset (&re_buffer, 0, sizeof (re_buffer)); + memset (&re_regs, 0, sizeof (re_regs)); + re_buffer.allocated = 2 * len; + re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated); + re_buffer.translate = 0; + re_syntax_options = RE_SYNTAX_POSIX_BASIC; + errmsg = re_compile_pattern (pv->u.s, len, &re_buffer); + if (errmsg) { + fatalError("%s\n", errmsg); + } + + len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); + if (len >= 0) { + /* Were \(...\) used? */ + if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */ + sv->u.s[re_regs.end[1]] = '\0'; + v = str_value (sv->u.s + re_regs.start[1]); + } + else + v = int_value (len); + } + else { + /* Match failed -- return the right kind of null. */ + if (re_buffer.re_nsub > 0) + v = str_value (""); + else + v = int_value (0); + } + free (re_buffer.buffer); + return v; +} + +/* Handle bare operands and ( expr ) syntax. */ + +static VALUE *eval7 (void) +{ + VALUE *v; + + if (!*args) + fatalError ( "syntax error\n"); + + if (nextarg ("(")) { + args++; + v = eval (); + if (!nextarg (")")) + fatalError ( "syntax error\n"); + args++; + return v; + } + + if (nextarg (")")) + fatalError ( "syntax error\n"); + + return str_value (*args++); +} + +/* Handle match, substr, index, length, and quote keywords. */ + +static VALUE *eval6 (void) +{ + VALUE *l, *r, *v, *i1, *i2; + + if (nextarg ("quote")) { + args++; + if (!*args) + fatalError ( "syntax error\n"); + return str_value (*args++); + } + else if (nextarg ("length")) { + args++; + r = eval6 (); + tostring (r); + v = int_value (strlen (r->u.s)); + freev (r); + return v; + } + else if (nextarg ("match")) { + args++; + l = eval6 (); + r = eval6 (); + v = docolon (l, r); + freev (l); + freev (r); + return v; + } + else if (nextarg ("index")) { + args++; + l = eval6 (); + r = eval6 (); + tostring (l); + tostring (r); + v = int_value (strcspn (l->u.s, r->u.s) + 1); + if (v->u.i == (int) strlen (l->u.s) + 1) + v->u.i = 0; + freev (l); + freev (r); + return v; + } + else if (nextarg ("substr")) { + args++; + l = eval6 (); + i1 = eval6 (); + i2 = eval6 (); + tostring (l); + if (!toarith (i1) || !toarith (i2) + || i1->u.i > (int) strlen (l->u.s) + || i1->u.i <= 0 || i2->u.i <= 0) + v = str_value (""); + else { + v = xmalloc (sizeof(VALUE)); + v->type = string; + v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1), + l->u.s + i1->u.i - 1, i2->u.i); + v->u.s[i2->u.i] = 0; + } + freev (l); + freev (i1); + freev (i2); + return v; + } + else + return eval7 (); +} + +/* Handle : operator (pattern matching). + Calls docolon to do the real work. */ + +static VALUE *eval5 (void) +{ + VALUE *l, *r, *v; + + l = eval6 (); + while (nextarg (":")) { + args++; + r = eval6 (); + v = docolon (l, r); + freev (l); + freev (r); + l = v; + } + return l; +} + +/* Handle *, /, % operators. */ + +static VALUE *eval4 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval5 (); + while (1) { + if (nextarg ("*")) + fxn = multiply; + else if (nextarg ("/")) + fxn = divide; + else if (nextarg ("%")) + fxn = mod; + else + return l; + args++; + r = eval5 (); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval4 (); + while (1) { + if (nextarg ("+")) + fxn = plus; + else if (nextarg ("-")) + fxn = minus; + else + return l; + args++; + r = eval4 (); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2 (void) +{ + VALUE *l, *r; + int (*fxn) (), val; + + l = eval3 (); + while (1) { + if (nextarg ("<")) + fxn = less_than; + else if (nextarg ("<=")) + fxn = less_equal; + else if (nextarg ("=") || nextarg ("==")) + fxn = equal; + else if (nextarg ("!=")) + fxn = not_equal; + else if (nextarg (">=")) + fxn = greater_equal; + else if (nextarg (">")) + fxn = greater_than; + else + return l; + args++; + r = eval3 (); + toarith (l); + toarith (r); + val = (*fxn) (l, r); + freev (l); + freev (r); + l = int_value (val); + } +} + +/* Handle &. */ + +static VALUE *eval1 (void) +{ + VALUE *l, *r; + + l = eval2 (); + while (nextarg ("&")) { + args++; + r = eval2 (); + if (null (l) || null (r)) { + freev (l); + freev (r); + l = int_value (0); + } + else + freev (r); + } + return l; +} + +/* Handle |. */ + +static VALUE *eval (void) +{ + VALUE *l, *r; + + l = eval1 (); + while (nextarg ("|")) { + args++; + r = eval1 (); + if (null (l)) { + freev (l); + l = r; + } + else + freev (r); + } + return l; +} -- cgit v1.2.3