diff options
author | Eric Andersen | 2001-07-30 21:41:37 +0000 |
---|---|---|
committer | Eric Andersen | 2001-07-30 21:41:37 +0000 |
commit | 74bcd1642597109661543d25304c551a2e39acbc (patch) | |
tree | f54a28df9cd460c1da85e13e6fe1b387199f92df | |
parent | dc6647201da6d5cf044f4b54fe9826c89068b782 (diff) | |
download | busybox-74bcd1642597109661543d25304c551a2e39acbc.zip busybox-74bcd1642597109661543d25304c551a2e39acbc.tar.gz |
This incorporates Posix math support into ash. The Posix math support
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
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | ash.c | 105 | ||||
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/arith.c | 250 | ||||
-rw-r--r-- | libbb/libbb.h | 2 | ||||
-rw-r--r-- | shell/ash.c | 105 |
6 files changed, 367 insertions, 99 deletions
@@ -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)),) @@ -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. diff --git a/include/libbb.h b/include/libbb.h index 3cf932d..66acc22 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -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 index 0000000..c7a3cf9 --- /dev/null +++ b/libbb/arith.c @@ -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; +} diff --git a/libbb/libbb.h b/libbb/libbb.h index 3cf932d..66acc22 100644 --- a/libbb/libbb.h +++ b/libbb/libbb.h @@ -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/shell/ash.c b/shell/ash.c index bb5bf36..9a5435e 100644 --- a/shell/ash.c +++ b/shell/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. |