Added expr, from Edward Betts <edward@debian.org>, with some fixups
authorEric Andersen <andersen@codepoet.org>
Tue, 5 Sep 2000 17:37:48 +0000 (17:37 -0000)
committerEric Andersen <andersen@codepoet.org>
Tue, 5 Sep 2000 17:37:48 +0000 (17:37 -0000)
and docs added by me.
 -Erik

16 files changed:
AUTHORS
TODO
applets/busybox.c
applets/busybox.sh
applets/usage.c
busybox.c
busybox.def.h
busybox.sh
coreutils/expr.c [new file with mode: 0644]
coreutils/wc.c
docs/busybox.pod
docs/busybox.sgml
expr.c [new file with mode: 0644]
internal.h
usage.c
wc.c

diff --git a/AUTHORS b/AUTHORS
index 64114a5..918a001 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,9 @@ Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
     Tons of new stuff, major rewrite of most of the
     core apps, tons of new apps as noted in header files.
 
     Tons of new stuff, major rewrite of most of the
     core apps, tons of new apps as noted in header files.
 
+Edward Betts <edward@debian.org>
+    expr, hostid, logname, tty, wc, whoami, yes
 John Beppu <beppu@lineo.com>
     du, head, nslookup, sort, tee, uniq
 
 John Beppu <beppu@lineo.com>
     du, head, nslookup, sort, tee, uniq
 
diff --git a/TODO b/TODO
index d345723..b8a2501 100644 (file)
--- 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
 * rdate
 * hwclock
 * stty
-* expr
 * wget (or whatever I call it)
 * tftp
 * ftp
 * wget (or whatever I call it)
 * tftp
 * ftp
index 0ffa94e..a7c5d37 100644 (file)
@@ -76,6 +76,9 @@ const struct BB_applet applets[] = {
 #ifdef BB_ECHO
        {"echo", echo_main, _BB_DIR_BIN, echo_usage},
 #endif
 #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
 #ifdef BB_TRUE_FALSE
        {"false", false_main, _BB_DIR_BIN, false_usage},
 #endif
index 5dd6e1c..2b45210 100755 (executable)
@@ -5,6 +5,6 @@
 ls -1 ` \
     gcc -E -dM busybox.def.h | \
     sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
 ls -1 ` \
     gcc -E -dM busybox.def.h | \
     sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
-    | tr [:upper:] [:lower:] | sort
+    | tr '[:upper:]' '[:lower:]' | sort
 ` 2>/dev/null | sed -e 's/\.c$/\.o/g'
 
 ` 2>/dev/null | sed -e 's/\.c$/\.o/g'
 
index 91064f5..05913f3 100644 (file)
@@ -250,6 +250,44 @@ const char echo_usage[] =
        ;
 #endif
 
        ;
 #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"
 #if defined BB_TRUE_FALSE
 const char false_usage[] =
        "false\n"
index 0ffa94e..a7c5d37 100644 (file)
--- 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_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
 #ifdef BB_TRUE_FALSE
        {"false", false_main, _BB_DIR_BIN, false_usage},
 #endif
index 3ad118d..f88851a 100644 (file)
@@ -27,6 +27,7 @@
 #define BB_DU
 #define BB_DUMPKMAP
 #define BB_ECHO
 #define BB_DU
 #define BB_DUMPKMAP
 #define BB_ECHO
+#define BB_EXPR
 #define BB_FBSET
 #define BB_FDFLUSH
 #define BB_FIND
 #define BB_FBSET
 #define BB_FDFLUSH
 #define BB_FIND
index 5dd6e1c..2b45210 100755 (executable)
@@ -5,6 +5,6 @@
 ls -1 ` \
     gcc -E -dM busybox.def.h | \
     sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
 ls -1 ` \
     gcc -E -dM busybox.def.h | \
     sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
-    | tr [:upper:] [:lower:] | sort
+    | tr '[:upper:]' '[:lower:]' | sort
 ` 2>/dev/null | sed -e 's/\.c$/\.o/g'
 
 ` 2>/dev/null | sed -e 's/\.c$/\.o/g'
 
diff --git a/coreutils/expr.c b/coreutils/expr.c
new file mode 100644 (file)
index 0000000..9e3c04a
--- /dev/null
@@ -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 <edward@debian.org>.
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+
+#include <regex.h>
+
+/* 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;
+}
index 02e2b2a..ca5b368 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Mini wc implementation for busybox
  *
 /*
  * Mini wc implementation for busybox
  *
- * by Edward Betts <edward@debian.org>
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>
  *
  * 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
  *
  * 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
index accdaed..3bedcef 100644 (file)
@@ -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)
 =item false
 
 Returns an exit code of FALSE (1)
@@ -2112,4 +2151,4 @@ Enrique Zanardi <ezanardi@ull.es>
 
 =cut
 
 
 =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 $
index ec6f504..a25aa01 100644 (file)
                </para>
        </sect1>
 
                </para>
        </sect1>
 
+       <sect1 id="expr">
+           <title>expr</title>
+
+               <para>
+               Usage: expr EXPRESSION
+               </para>
+
+               <para>
+               Prints the value of EXPRESSION to standard output.
+               </para>
+
+               <para>
+               EXPRESSION may be:
+               </para>
+
+               <para>
+               <screen>
+                       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 &lt  ARG2    ARG1 is less than ARG2
+                       ARG1 &lt= ARG2    ARG1 is less than or equal to ARG2
+                       ARG1 =  ARG2    ARG1 is equal to ARG2
+                       ARG1 != ARG2    ARG1 is unequal to ARG2
+                       ARG1 &gt= ARG2    ARG1 is greater than or equal to ARG2
+                       ARG1 &gt  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
+               </screen>
+               </para>
+
+               <para>
+               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.
+               </para>
+
+       </sect1>
+
+
        <sect1 id="false">
            <title>false</title>
 
        <sect1 id="false">
            <title>false</title>
 
diff --git a/expr.c b/expr.c
new file mode 100644 (file)
index 0000000..9e3c04a
--- /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 <edward@debian.org>.
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+
+#include <regex.h>
+
+/* 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;
+}
index 33f101e..b2034da 100644 (file)
@@ -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 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);
 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 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);
 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 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[];
 extern const char false_usage[];
 extern const char fdflush_usage[];
 extern const char find_usage[];
diff --git a/usage.c b/usage.c
index 91064f5..05913f3 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -250,6 +250,44 @@ const char echo_usage[] =
        ;
 #endif
 
        ;
 #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"
 #if defined BB_TRUE_FALSE
 const char false_usage[] =
        "false\n"
diff --git a/wc.c b/wc.c
index 02e2b2a..ca5b368 100644 (file)
--- a/wc.c
+++ b/wc.c
@@ -2,7 +2,7 @@
 /*
  * Mini wc implementation for busybox
  *
 /*
  * Mini wc implementation for busybox
  *
- * by Edward Betts <edward@debian.org>
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>
  *
  * 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
  *
  * 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