This incorporates Posix math support into ash. The Posix math support
authorEric Andersen <andersen@codepoet.org>
Mon, 30 Jul 2001 21:41:37 +0000 (21:41 -0000)
committerEric Andersen <andersen@codepoet.org>
Mon, 30 Jul 2001 21:41:37 +0000 (21:41 -0000)
was written by Aaron Lehmann <aaronl@vitelus.com> for busybox.  This
patch makes a few trivial changes to Aaron's code so that it can be
used (in theory) by the other shells as well...
 -Erik

Makefile
ash.c
include/libbb.h
libbb/arith.c [new file with mode: 0644]
libbb/libbb.h
shell/ash.c

index 7ee55f8..88a7aa0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -247,7 +247,7 @@ safe_read.c safe_strncpy.c syscalls.c syslog_msg_with_name.c time_string.c \
 trim.c unzip.c vdprintf.c verror_msg.c vperror_msg.c wfopen.c xfuncs.c \
 xgetcwd.c xreadlink.c xregcomp.c interface.c remove_file.c last_char_is.c \
 copyfd.c vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \
-dirname.c make_directory.c create_icmp_socket.c
+dirname.c make_directory.c create_icmp_socket.c arith.c
 LIBBB_OBJS=$(patsubst %.c,$(LIBBB)/%.o, $(LIBBB_CSRC))
 LIBBB_CFLAGS = -I$(LIBBB)
 ifneq ($(strip $(BB_SRC_DIR)),)
diff --git a/ash.c b/ash.c
index bb5bf36..9a5435e 100644 (file)
--- a/ash.c
+++ b/ash.c
@@ -51,9 +51,8 @@
 #define ASH_ALIAS
 
 /* If you need ash to act as a full Posix shell, with full math
- * support, enable this.   This option needs some work, since it
- * doesn't compile right now... */
-#undef ASH_MATH_SUPPORT
+ * support, enable this.   This adds a bit over 2k an x86 system. */
+#define ASH_MATH_SUPPORT
 
 /* Getopts is used by shell procedures to parse positional parameters.
  * You probably want to leave this disabled, and use the busybox getopt
@@ -80,6 +79,7 @@
 #undef FNMATCH_BROKEN
 #undef GLOB_BROKEN
 #undef _GNU_SOURCE
+#undef __USE_GNU
 
 #include <assert.h>
 #include <ctype.h>
@@ -1562,8 +1562,10 @@ __lookupalias(const char *name) {
 #endif
 
 #ifdef ASH_MATH_SUPPORT
-/* The generated file arith.c has been snipped.  If you want this
- * stuff back in, feel free to add it to your own copy.  */
+/* The generated file arith.c has been replaced with a custom hand
+ * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.  
+ * This is now part of libbb, so that it can be used by all the shells 
+ * in busybox. */
 #define ARITH_NUM 257
 #define ARITH_LPAREN 258
 #define ARITH_RPAREN 259
@@ -1592,11 +1594,8 @@ __lookupalias(const char *name) {
 
 static void expari (int);
 /* From arith.y */
-static int arith (const char *);
+static long ash_arith(const char *p);
 static int expcmd (int , char **);
-static void arith_lex_reset (void);
-static int yylex (void);
-
 #endif
 
 static char *trap[NSIG];                /* trap handler commands */
@@ -2173,52 +2172,22 @@ exverror(int cond, const char *msg, va_list ap)
 }
 
 
-#ifdef __STDC__
-static void
+static void 
 error(const char *msg, ...)
-#else
-static void
-error(va_alist)
-       va_dcl
-#endif
 {
-#ifndef __STDC__
-       const char *msg;
-#endif
        va_list ap;
-#ifdef __STDC__
        va_start(ap, msg);
-#else
-       va_start(ap);
-       msg = va_arg(ap, const char *);
-#endif
        exverror(EXERROR, msg, ap);
        /* NOTREACHED */
        va_end(ap);
 }
 
 
-#ifdef __STDC__
 static void
 exerror(int cond, const char *msg, ...)
-#else
-static void
-exerror(va_alist)
-       va_dcl
-#endif
 {
-#ifndef __STDC__
-       int cond;
-       const char *msg;
-#endif
        va_list ap;
-#ifdef __STDC__
        va_start(ap, msg);
-#else
-       va_start(ap);
-       cond = va_arg(ap, int);
-       msg = va_arg(ap, const char *);
-#endif
        exverror(cond, msg, ap);
        /* NOTREACHED */
        va_end(ap);
@@ -4914,7 +4883,7 @@ expari(int flag)
        removerecordregions(begoff);
        if (quotes)
                rmescapes(p+2);
-       result = arith(p+2);
+       result = ash_arith(p+2);
        snprintf(p, 12, "%d", result);
 
        while (*p++)
@@ -11952,13 +11921,7 @@ static void
 trace(const char *fmt, ...)
 {
        va_list va;
-#ifdef __STDC__
        va_start(va, fmt);
-#else
-       char *fmt;
-       va_start(va);
-       fmt = va_arg(va, char *);
-#endif
        if (tracefile != NULL) {
                (void) vfprintf(tracefile, fmt, va);
                if (strchr(fmt, '\n'))
@@ -12657,7 +12620,6 @@ found:;
        return 0;
 }
 
-
 /*
  * The "local" command.
  */
@@ -12916,7 +12878,7 @@ findvar(struct var **vpp, const char *name)
 /*
  * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
  * This file contains code for the times builtin.
- * $Id: ash.c,v 1.13 2001/07/26 05:58:40 russ Exp $
+ * $Id: ash.c,v 1.14 2001/07/30 21:41:37 andersen Exp $
  */
 static int timescmd (int argc, char **argv)
 {
@@ -12937,6 +12899,51 @@ static int timescmd (int argc, char **argv)
 }
 
 
+#ifdef ASH_MATH_SUPPORT
+/* The exp(1) builtin.  */
+int expcmd(int argc, char **argv)
+{
+       const char *p;
+       char *concat;
+       char **ap;
+       long i;
+
+       if (argc > 1) {
+               p = argv[1];
+               if (argc > 2) {
+                       /* concatenate arguments */
+                       STARTSTACKSTR(concat);
+                       ap = argv + 2;
+                       for (;;) {
+                               while (*p)
+                                       STPUTC(*p++, concat);
+                               if ((p = *ap++) == NULL)
+                                       break;
+                               STPUTC(' ', concat);
+                       }
+                       STPUTC('\0', concat);
+                       p = grabstackstr(concat);
+               }
+       } else
+               p = "";
+
+       i = ash_arith(p);
+
+       printf("%ld\n", i);
+       return (! i);
+}
+       
+static long ash_arith(const char *p)
+{
+       long i = arith(p);
+       if (i <0)
+           error("arith: syntax error: \"%s\"\n", p);
+       return i;
+}
+#endif
+
+
+
 /*-
  * Copyright (c) 1989, 1991, 1993, 1994
  *      The Regents of the University of California.  All rights reserved.
index 3cf932d..66acc22 100644 (file)
@@ -212,6 +212,8 @@ char *xreadlink(const char *path);
 char *concat_path_file(const char *path, const char *filename);
 char *last_char_is(const char *s, int c);
 
+extern long arith (const char *startbuf);
+
 typedef struct file_headers_s {
        char *name;
        char *link_name;
diff --git a/libbb/arith.c b/libbb/arith.c
new file mode 100644 (file)
index 0000000..c7a3cf9
--- /dev/null
@@ -0,0 +1,250 @@
+/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+   
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+   
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+   
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* This is my infix parser/evaluator. It is optimized for size, intended
+ * as a replacement for yacc-based parsers. However, it may well be faster
+ * than a comparable parser writen in yacc. The supported operators are
+ * listed in #defines below. Parens, order of operations, and error handling
+ * are supported. This code is threadsafe. */
+
+/* To use the routine, call it with an expression string. It returns an
+ * integer result. You will also need to define an "error" function
+ * that takes printf arguments and _does not return_, or modify the code
+ * to use another error mechanism. */
+
+#include <stdlib.h>
+#include <string.h>
+#include "libbb.h"
+
+typedef char operator;
+
+#define tok_decl(prec,id) (((id)<<5)|(prec))
+#define PREC(op) ((op)&0x1F)
+
+#define TOK_LPAREN tok_decl(0,0)
+
+#define TOK_OR tok_decl(1,0)
+
+#define TOK_AND tok_decl(2,0)
+
+#define TOK_BOR tok_decl(3,0)
+
+#define TOK_BXOR tok_decl(4,0)
+
+#define TOK_BAND tok_decl(5,0)
+
+#define TOK_EQ tok_decl(6,0)
+#define TOK_NE tok_decl(6,1)
+
+#define TOK_LT tok_decl(7,0)
+#define TOK_GT tok_decl(7,1)
+#define TOK_GE tok_decl(7,2)
+#define TOK_LE tok_decl(7,3)
+
+#define TOK_LSHIFT tok_decl(8,0)
+#define TOK_RSHIFT tok_decl(8,1)
+
+#define TOK_ADD tok_decl(9,0)
+#define TOK_SUB tok_decl(9,1)
+
+#define TOK_MUL tok_decl(10,0)
+#define TOK_DIV tok_decl(10,1)
+#define TOK_REM tok_decl(10,2)
+
+#define UNARYPREC 14
+#define TOK_BNOT tok_decl(UNARYPREC,0)
+#define TOK_NOT tok_decl(UNARYPREC,1)
+#define TOK_UMINUS tok_decl(UNARYPREC,2)
+
+#define TOK_NUM tok_decl(15,0)
+
+#define ARITH_APPLY(op) arith_apply(op, numstack, &numstackptr)
+#define NUMPTR (*numstackptr)
+static short arith_apply(operator op, long *numstack, long **numstackptr)
+{
+       if (NUMPTR == numstack) goto err;
+       if (op == TOK_UMINUS)
+               NUMPTR[-1] *= -1;
+       else if (op == TOK_NOT)
+               NUMPTR[-1] = !(NUMPTR[-1]);
+       else if (op == TOK_BNOT)
+               NUMPTR[-1] = ~(NUMPTR[-1]);
+
+       /* Binary operators */
+       else {
+       if (NUMPTR-1 == numstack) goto err;
+       --NUMPTR;
+       if (op == TOK_BOR)
+               NUMPTR[-1] |= *NUMPTR;
+       else if (op == TOK_OR)
+               NUMPTR[-1] = *NUMPTR || NUMPTR[-1];
+       else if (op == TOK_BAND)
+               NUMPTR[-1] &= *NUMPTR;
+       else if (op == TOK_AND)
+               NUMPTR[-1] = NUMPTR[-1] && *NUMPTR;
+       else if (op == TOK_EQ)
+               NUMPTR[-1] = (NUMPTR[-1] == *NUMPTR);
+       else if (op == TOK_NE)
+               NUMPTR[-1] = (NUMPTR[-1] != *NUMPTR);
+       else if (op == TOK_GE)
+               NUMPTR[-1] = (NUMPTR[-1] >= *NUMPTR);
+       else if (op == TOK_RSHIFT)
+               NUMPTR[-1] >>= *NUMPTR;
+       else if (op == TOK_LSHIFT)
+               NUMPTR[-1] <<= *NUMPTR;
+       else if (op == TOK_GT)
+               NUMPTR[-1] = (NUMPTR[-1] > *NUMPTR);
+       else if (op == TOK_LT)
+               NUMPTR[-1] = (NUMPTR[-1] < *NUMPTR);
+       else if (op == TOK_LE)
+               NUMPTR[-1] = (NUMPTR[-1] <= *NUMPTR);
+       else if (op == TOK_MUL)
+               NUMPTR[-1] *= *NUMPTR;
+       else if (op == TOK_DIV)
+               NUMPTR[-1] /= *NUMPTR;
+       else if (op == TOK_REM)
+               NUMPTR[-1] %= *NUMPTR;
+       else if (op == TOK_ADD)
+               NUMPTR[-1] += *NUMPTR;
+       else if (op == TOK_SUB)
+               NUMPTR[-1] -= *NUMPTR;
+       }
+       return 0;
+err: return(1);
+}
+
+extern long arith (const char *startbuf)
+{
+       register char arithval;
+       const char *expr = startbuf;
+
+       operator lasttok = TOK_MUL, op;
+       size_t datasizes = strlen(startbuf);
+       unsigned char prec;
+
+       long *numstack, *numstackptr;
+
+       operator *stack = alloca(datasizes * sizeof(operator)), *stackptr = stack;
+       numstack = alloca((datasizes/2+1)*sizeof(long)), numstackptr = numstack;
+
+       while ((arithval = *expr)) {
+               if (arithval == ' ' || arithval == '\n' || arithval == '\t')
+                       goto prologue;
+               if ((unsigned)arithval-'0' <= 9) /* isdigit */ {
+                       *numstackptr++ = strtol(expr, (char **) &expr, 10);
+                       lasttok = TOK_NUM;
+                       continue;
+               } if (arithval == '(') {
+                       *stackptr++ = TOK_LPAREN;
+                       lasttok = TOK_LPAREN;
+                       goto prologue;
+               } if (arithval == ')') {
+                       lasttok = TOK_NUM;
+                       while (stackptr != stack) {
+                               op = *--stackptr;
+                               if (op == TOK_LPAREN)
+                                       goto prologue;
+                               if(ARITH_APPLY(op)) goto err;
+                       }
+                       goto err; /* Mismatched parens */
+               } if (arithval == '|') {
+                       if (*++expr == '|')
+                               op = TOK_OR;
+                       else {
+                               --expr;
+                               op = TOK_BOR;
+                       }
+               } else if (arithval == '&') {
+                       if (*++expr == '&')
+                               op = TOK_AND;
+                       else {
+                               --expr;
+                               op = TOK_BAND;
+                       }
+               } else if (arithval == '=') {
+                       if (*++expr != '=') goto err; /* Unknown token */
+                       op = TOK_EQ;
+               } else if (arithval == '!') {
+                       if (*++expr == '=')
+                               op = TOK_NE;
+                       else {
+                               --expr;
+                               op = TOK_NOT;
+                       }
+               } else if (arithval == '>') {
+                       switch (*++expr) {
+                               case '=':
+                                       op = TOK_GE;
+                                       break;
+                               case '>':
+                                       op = TOK_RSHIFT;
+                                       break;
+                               default:
+                                       --expr;
+                                       op = TOK_GT;
+                       }
+               } else if (arithval == '<') {
+                       switch (*++expr) {
+                               case '=':
+                                       op = TOK_LE;
+                                       break;
+                               case '<':
+                                       op = TOK_LSHIFT;
+                                       break;
+                               default:
+                                       --expr;
+                                       op = TOK_LT;
+                       }
+               } else if (arithval == '*')
+                       op = TOK_MUL;
+               else if (arithval == '/')
+                       op = TOK_DIV;
+               else if (arithval == '%')
+                       op = TOK_REM;
+               else if (arithval == '+') {
+                       if (lasttok != TOK_NUM) goto prologue; /* Unary plus */
+                       op = TOK_ADD;
+               } else if (arithval == '-')
+                       op = (lasttok == TOK_NUM) ? TOK_SUB : TOK_UMINUS;
+               else if (arithval == '~')
+                       op = TOK_BNOT;
+               else goto err; /* Unknown token */
+
+               prec = PREC(op);
+               if (prec != UNARYPREC)
+                       while (stackptr != stack && PREC(stackptr[-1]) >= prec)
+                               if(ARITH_APPLY(*--stackptr)) goto err;
+               *stackptr++ = op;
+               lasttok = op;
+prologue: ++expr;
+       } /* yay */
+
+       while (stackptr != stack)
+               if(ARITH_APPLY(*--stackptr)) goto err;
+       if (numstackptr != numstack+1) {
+err: 
+           return -1;
+        /* NOTREACHED */
+       }
+
+       return *numstack;
+}
index 3cf932d..66acc22 100644 (file)
@@ -212,6 +212,8 @@ char *xreadlink(const char *path);
 char *concat_path_file(const char *path, const char *filename);
 char *last_char_is(const char *s, int c);
 
+extern long arith (const char *startbuf);
+
 typedef struct file_headers_s {
        char *name;
        char *link_name;
index bb5bf36..9a5435e 100644 (file)
@@ -51,9 +51,8 @@
 #define ASH_ALIAS
 
 /* If you need ash to act as a full Posix shell, with full math
- * support, enable this.   This option needs some work, since it
- * doesn't compile right now... */
-#undef ASH_MATH_SUPPORT
+ * support, enable this.   This adds a bit over 2k an x86 system. */
+#define ASH_MATH_SUPPORT
 
 /* Getopts is used by shell procedures to parse positional parameters.
  * You probably want to leave this disabled, and use the busybox getopt
@@ -80,6 +79,7 @@
 #undef FNMATCH_BROKEN
 #undef GLOB_BROKEN
 #undef _GNU_SOURCE
+#undef __USE_GNU
 
 #include <assert.h>
 #include <ctype.h>
@@ -1562,8 +1562,10 @@ __lookupalias(const char *name) {
 #endif
 
 #ifdef ASH_MATH_SUPPORT
-/* The generated file arith.c has been snipped.  If you want this
- * stuff back in, feel free to add it to your own copy.  */
+/* The generated file arith.c has been replaced with a custom hand
+ * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.  
+ * This is now part of libbb, so that it can be used by all the shells 
+ * in busybox. */
 #define ARITH_NUM 257
 #define ARITH_LPAREN 258
 #define ARITH_RPAREN 259
@@ -1592,11 +1594,8 @@ __lookupalias(const char *name) {
 
 static void expari (int);
 /* From arith.y */
-static int arith (const char *);
+static long ash_arith(const char *p);
 static int expcmd (int , char **);
-static void arith_lex_reset (void);
-static int yylex (void);
-
 #endif
 
 static char *trap[NSIG];                /* trap handler commands */
@@ -2173,52 +2172,22 @@ exverror(int cond, const char *msg, va_list ap)
 }
 
 
-#ifdef __STDC__
-static void
+static void 
 error(const char *msg, ...)
-#else
-static void
-error(va_alist)
-       va_dcl
-#endif
 {
-#ifndef __STDC__
-       const char *msg;
-#endif
        va_list ap;
-#ifdef __STDC__
        va_start(ap, msg);
-#else
-       va_start(ap);
-       msg = va_arg(ap, const char *);
-#endif
        exverror(EXERROR, msg, ap);
        /* NOTREACHED */
        va_end(ap);
 }
 
 
-#ifdef __STDC__
 static void
 exerror(int cond, const char *msg, ...)
-#else
-static void
-exerror(va_alist)
-       va_dcl
-#endif
 {
-#ifndef __STDC__
-       int cond;
-       const char *msg;
-#endif
        va_list ap;
-#ifdef __STDC__
        va_start(ap, msg);
-#else
-       va_start(ap);
-       cond = va_arg(ap, int);
-       msg = va_arg(ap, const char *);
-#endif
        exverror(cond, msg, ap);
        /* NOTREACHED */
        va_end(ap);
@@ -4914,7 +4883,7 @@ expari(int flag)
        removerecordregions(begoff);
        if (quotes)
                rmescapes(p+2);
-       result = arith(p+2);
+       result = ash_arith(p+2);
        snprintf(p, 12, "%d", result);
 
        while (*p++)
@@ -11952,13 +11921,7 @@ static void
 trace(const char *fmt, ...)
 {
        va_list va;
-#ifdef __STDC__
        va_start(va, fmt);
-#else
-       char *fmt;
-       va_start(va);
-       fmt = va_arg(va, char *);
-#endif
        if (tracefile != NULL) {
                (void) vfprintf(tracefile, fmt, va);
                if (strchr(fmt, '\n'))
@@ -12657,7 +12620,6 @@ found:;
        return 0;
 }
 
-
 /*
  * The "local" command.
  */
@@ -12916,7 +12878,7 @@ findvar(struct var **vpp, const char *name)
 /*
  * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
  * This file contains code for the times builtin.
- * $Id: ash.c,v 1.13 2001/07/26 05:58:40 russ Exp $
+ * $Id: ash.c,v 1.14 2001/07/30 21:41:37 andersen Exp $
  */
 static int timescmd (int argc, char **argv)
 {
@@ -12937,6 +12899,51 @@ static int timescmd (int argc, char **argv)
 }
 
 
+#ifdef ASH_MATH_SUPPORT
+/* The exp(1) builtin.  */
+int expcmd(int argc, char **argv)
+{
+       const char *p;
+       char *concat;
+       char **ap;
+       long i;
+
+       if (argc > 1) {
+               p = argv[1];
+               if (argc > 2) {
+                       /* concatenate arguments */
+                       STARTSTACKSTR(concat);
+                       ap = argv + 2;
+                       for (;;) {
+                               while (*p)
+                                       STPUTC(*p++, concat);
+                               if ((p = *ap++) == NULL)
+                                       break;
+                               STPUTC(' ', concat);
+                       }
+                       STPUTC('\0', concat);
+                       p = grabstackstr(concat);
+               }
+       } else
+               p = "";
+
+       i = ash_arith(p);
+
+       printf("%ld\n", i);
+       return (! i);
+}
+       
+static long ash_arith(const char *p)
+{
+       long i = arith(p);
+       if (i <0)
+           error("arith: syntax error: \"%s\"\n", p);
+       return i;
+}
+#endif
+
+
+
 /*-
  * Copyright (c) 1989, 1991, 1993, 1994
  *      The Regents of the University of California.  All rights reserved.