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
This commit is contained in:
Eric Andersen 2001-07-30 21:41:37 +00:00
parent dc6647201d
commit 74bcd16425
6 changed files with 367 additions and 99 deletions

View File

@ -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)),)

105
ash.c
View 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.

View 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;

250
libbb/arith.c Normal file
View File

@ -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;
}

View 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;

View 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.