From 1b355ebba68bdd567dd3961a18291dfd9532c2e8 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Tue, 5 Sep 2000 17:37:48 +0000 Subject: [PATCH] Added expr, from Edward Betts , with some fixups and docs added by me. -Erik --- AUTHORS | 3 + TODO | 1 - applets/busybox.c | 3 + applets/busybox.sh | 2 +- applets/usage.c | 38 ++++ busybox.c | 3 + busybox.def.h | 1 + busybox.sh | 2 +- coreutils/expr.c | 531 +++++++++++++++++++++++++++++++++++++++++++++ coreutils/wc.c | 2 +- docs/busybox.pod | 41 +++- docs/busybox.sgml | 52 +++++ expr.c | 531 +++++++++++++++++++++++++++++++++++++++++++++ internal.h | 3 + usage.c | 38 ++++ wc.c | 2 +- 16 files changed, 1247 insertions(+), 6 deletions(-) create mode 100644 coreutils/expr.c create mode 100644 expr.c diff --git a/AUTHORS b/AUTHORS index 64114a5fc..918a0015b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,6 +12,9 @@ Erik Andersen , Tons of new stuff, major rewrite of most of the core apps, tons of new apps as noted in header files. +Edward Betts + expr, hostid, logname, tty, wc, whoami, yes + John Beppu du, head, nslookup, sort, tee, uniq diff --git a/TODO b/TODO index d34572311..b8a25019f 100644 --- a/TODO +++ b/TODO @@ -22,7 +22,6 @@ around to it some time. If you have any good ideas, please let me know. * rdate * hwclock * stty -* expr * wget (or whatever I call it) * tftp * ftp diff --git a/applets/busybox.c b/applets/busybox.c index 0ffa94e09..a7c5d37a0 100644 --- a/applets/busybox.c +++ b/applets/busybox.c @@ -76,6 +76,9 @@ const struct BB_applet applets[] = { #ifdef BB_ECHO {"echo", echo_main, _BB_DIR_BIN, echo_usage}, #endif +#ifdef BB_EXPR + {"expr", expr_main, _BB_DIR_USR_BIN, expr_usage}, +#endif #ifdef BB_TRUE_FALSE {"false", false_main, _BB_DIR_BIN, false_usage}, #endif diff --git a/applets/busybox.sh b/applets/busybox.sh index 5dd6e1c3d..2b4521065 100755 --- a/applets/busybox.sh +++ b/applets/busybox.sh @@ -5,6 +5,6 @@ ls -1 ` \ gcc -E -dM busybox.def.h | \ sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\/\1.c/gp;' \ - | tr [:upper:] [:lower:] | sort + | tr '[:upper:]' '[:lower:]' | sort ` 2>/dev/null | sed -e 's/\.c$/\.o/g' diff --git a/applets/usage.c b/applets/usage.c index 91064f52c..05913f3e3 100644 --- a/applets/usage.c +++ b/applets/usage.c @@ -250,6 +250,44 @@ const char echo_usage[] = ; #endif +#if defined BB_EXPR +const char expr_usage[] = + "expr EXPRESSION\n" +#ifndef BB_FEATURE_TRIVIAL_HELP + "\nPrints the value of EXPRESSION to standard output.\n\n" + "EXPRESSION may be:\n" + "ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" + "ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" + "ARG1 < ARG2 ARG1 is less than ARG2\n" + "ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" + "ARG1 = ARG2 ARG1 is equal to ARG2\n" + "ARG1 != ARG2 ARG1 is unequal to ARG2\n" + "ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" + "ARG1 > ARG2 ARG1 is greater than ARG2\n" + "ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n" + "ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n" + "ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n" + "ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n" + "ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n" + "STRING : REGEXP anchored pattern match of REGEXP in STRING\n" + "match STRING REGEXP same as STRING : REGEXP\n" + "substr STRING POS LENGTH substring of STRING, POS counted from 1\n" + "index STRING CHARS index in STRING where any CHARS is found, or 0\n" + "length STRING length of STRING\n" + "quote TOKEN interpret TOKEN as a string, even if it is a \n" + " keyword like `match' or an operator like `/'\n" + "( EXPRESSION ) value of EXPRESSION\n\n" + "Beware that many operators need to be escaped or quoted for shells.\n" + "Comparisons are arithmetic if both ARGs are numbers, else\n" + "lexicographical. Pattern matches return the string matched between \n" + "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" + "of characters matched or 0.\n" + +#endif + ; +#endif + + #if defined BB_TRUE_FALSE const char false_usage[] = "false\n" diff --git a/busybox.c b/busybox.c index 0ffa94e09..a7c5d37a0 100644 --- a/busybox.c +++ b/busybox.c @@ -76,6 +76,9 @@ const struct BB_applet applets[] = { #ifdef BB_ECHO {"echo", echo_main, _BB_DIR_BIN, echo_usage}, #endif +#ifdef BB_EXPR + {"expr", expr_main, _BB_DIR_USR_BIN, expr_usage}, +#endif #ifdef BB_TRUE_FALSE {"false", false_main, _BB_DIR_BIN, false_usage}, #endif diff --git a/busybox.def.h b/busybox.def.h index 3ad118dd6..f88851af5 100644 --- a/busybox.def.h +++ b/busybox.def.h @@ -27,6 +27,7 @@ #define BB_DU #define BB_DUMPKMAP #define BB_ECHO +#define BB_EXPR #define BB_FBSET #define BB_FDFLUSH #define BB_FIND diff --git a/busybox.sh b/busybox.sh index 5dd6e1c3d..2b4521065 100755 --- a/busybox.sh +++ b/busybox.sh @@ -5,6 +5,6 @@ ls -1 ` \ gcc -E -dM busybox.def.h | \ sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\/\1.c/gp;' \ - | tr [:upper:] [:lower:] | sort + | tr '[:upper:]' '[:lower:]' | sort ` 2>/dev/null | sed -e 's/\.c$/\.o/g' diff --git a/coreutils/expr.c b/coreutils/expr.c new file mode 100644 index 000000000..9e3c04a11 --- /dev/null +++ b/coreutils/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; +} diff --git a/coreutils/wc.c b/coreutils/wc.c index 02e2b2aa6..ca5b3680a 100644 --- a/coreutils/wc.c +++ b/coreutils/wc.c @@ -2,7 +2,7 @@ /* * Mini wc implementation for busybox * - * by Edward Betts + * 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 diff --git a/docs/busybox.pod b/docs/busybox.pod index accdaed23..3bedcefb7 100644 --- a/docs/busybox.pod +++ b/docs/busybox.pod @@ -497,6 +497,45 @@ Example: ------------------------------- +=item echo + + +Usage: expr EXPRESSION + +Prints the value of EXPRESSION to standard output. + +EXPRESSION may be: + + ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 + ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 + ARG1 < ARG2 ARG1 is less than ARG2 + ARG1 <= ARG2 ARG1 is less than or equal to ARG2 + ARG1 = ARG2 ARG1 is equal to ARG2 + ARG1 != ARG2 ARG1 is unequal to ARG2 + ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 + ARG1 > ARG2 ARG1 is greater than ARG2 + ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 + ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 + ARG1 * ARG2 arithmetic product of ARG1 and ARG2 + ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 + ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 + STRING : REGEXP anchored pattern match of REGEXP in STRING + match STRING REGEXP same as STRING : REGEXP + substr STRING POS LENGTH substring of STRING, POS counted from 1 + index STRING CHARS index in STRING where any CHARS is found, or 0 + length STRING length of STRING + quote TOKEN interpret TOKEN as a string, even if it is a + keyword like `match' or an operator like `/' + ( EXPRESSION ) value of EXPRESSION + +Beware that many operators need to be escaped or quoted for shells. +Comparisons are arithmetic if both ARGs are numbers, else +lexicographical. Pattern matches return the string matched between +\( and \) or null; if \( and \) are not used, they return the number +of characters matched or 0. + +------------------------------- + =item false Returns an exit code of FALSE (1) @@ -2112,4 +2151,4 @@ Enrique Zanardi =cut -# $Id: busybox.pod,v 1.65 2000/09/01 16:12:57 andersen Exp $ +# $Id: busybox.pod,v 1.66 2000/09/05 17:37:48 andersen Exp $ diff --git a/docs/busybox.sgml b/docs/busybox.sgml index ec6f5041d..a25aa0189 100644 --- a/docs/busybox.sgml +++ b/docs/busybox.sgml @@ -879,6 +879,58 @@ + + expr + + + Usage: expr EXPRESSION + + + + Prints the value of EXPRESSION to standard output. + + + + EXPRESSION may be: + + + + + ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 + ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 + ARG1 < ARG2 ARG1 is less than ARG2 + ARG1 <= ARG2 ARG1 is less than or equal to ARG2 + ARG1 = ARG2 ARG1 is equal to ARG2 + ARG1 != ARG2 ARG1 is unequal to ARG2 + ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 + ARG1 > ARG2 ARG1 is greater than ARG2 + ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 + ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 + ARG1 * ARG2 arithmetic product of ARG1 and ARG2 + ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 + ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 + STRING : REGEXP anchored pattern match of REGEXP in STRING + match STRING REGEXP same as STRING : REGEXP + substr STRING POS LENGTH substring of STRING, POS counted from 1 + index STRING CHARS index in STRING where any CHARS is found, or 0 + length STRING length of STRING + quote TOKEN interpret TOKEN as a string, even if it is a + keyword like `match' or an operator like `/' + ( EXPRESSION ) value of EXPRESSION + + + + + Beware that many operators need to be escaped or quoted for shells. + Comparisons are arithmetic if both ARGs are numbers, else + lexicographical. Pattern matches return the string matched between + \( and \) or null; if \( and \) are not used, they return the number + of characters matched or 0. + + + + + false 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; +} diff --git a/internal.h b/internal.h index 33f101e09..b2034da5e 100644 --- a/internal.h +++ b/internal.h @@ -110,6 +110,7 @@ extern const struct BB_applet applets[]; extern int ar_main(int argc, char **argv); extern int basename_main(int argc, char **argv); +extern int bogomips_main(int argc, char **argv); extern int busybox_main(int argc, char** argv); extern int cat_main(int argc, char** argv); extern int chmod_chown_chgrp_main(int argc, char** argv); @@ -129,6 +130,7 @@ extern int du_main(int argc, char** argv); extern int dumpkmap_main(int argc, char** argv); extern int dutmp_main(int argc, char** argv); extern int echo_main(int argc, char** argv); +extern int expr_main(int argc, char** argv); extern int false_main(int argc, char** argv); extern int fbset_main(int argc, char** argv); extern int fdisk_main(int argc, char** argv); @@ -240,6 +242,7 @@ extern const char du_usage[]; extern const char dumpkmap_usage[]; extern const char dutmp_usage[]; extern const char echo_usage[]; +extern const char expr_usage[]; extern const char false_usage[]; extern const char fdflush_usage[]; extern const char find_usage[]; diff --git a/usage.c b/usage.c index 91064f52c..05913f3e3 100644 --- a/usage.c +++ b/usage.c @@ -250,6 +250,44 @@ const char echo_usage[] = ; #endif +#if defined BB_EXPR +const char expr_usage[] = + "expr EXPRESSION\n" +#ifndef BB_FEATURE_TRIVIAL_HELP + "\nPrints the value of EXPRESSION to standard output.\n\n" + "EXPRESSION may be:\n" + "ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" + "ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" + "ARG1 < ARG2 ARG1 is less than ARG2\n" + "ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" + "ARG1 = ARG2 ARG1 is equal to ARG2\n" + "ARG1 != ARG2 ARG1 is unequal to ARG2\n" + "ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" + "ARG1 > ARG2 ARG1 is greater than ARG2\n" + "ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n" + "ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n" + "ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n" + "ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n" + "ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n" + "STRING : REGEXP anchored pattern match of REGEXP in STRING\n" + "match STRING REGEXP same as STRING : REGEXP\n" + "substr STRING POS LENGTH substring of STRING, POS counted from 1\n" + "index STRING CHARS index in STRING where any CHARS is found, or 0\n" + "length STRING length of STRING\n" + "quote TOKEN interpret TOKEN as a string, even if it is a \n" + " keyword like `match' or an operator like `/'\n" + "( EXPRESSION ) value of EXPRESSION\n\n" + "Beware that many operators need to be escaped or quoted for shells.\n" + "Comparisons are arithmetic if both ARGs are numbers, else\n" + "lexicographical. Pattern matches return the string matched between \n" + "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" + "of characters matched or 0.\n" + +#endif + ; +#endif + + #if defined BB_TRUE_FALSE const char false_usage[] = "false\n" diff --git a/wc.c b/wc.c index 02e2b2aa6..ca5b3680a 100644 --- a/wc.c +++ b/wc.c @@ -2,7 +2,7 @@ /* * Mini wc implementation for busybox * - * by Edward Betts + * 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