+++ /dev/null
-/* 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. The exact expression format should
- * be that which POSIX specifies for shells. */
-
-/* The code uses a simple two-stack algorithm. See
- * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
- * for a detailed explaination of the infix-to-postfix algorithm on which
- * this is based (this code differs in that it applies operators immediately
- * to the stack instead of adding them to a queue to end up with an
- * expression). */
-
-/* To use the routine, call it with an expression string and error return
- * pointer */
-
-/*
- * Aug 24, 2001 Manuel Novoa III
- *
- * Reduced the generated code size by about 30% (i386) and fixed several bugs.
- *
- * 1) In arith_apply():
- * a) Cached values of *numptr and &(numptr[-1]).
- * b) Removed redundant test for zero denominator.
- *
- * 2) In arith():
- * a) Eliminated redundant code for processing operator tokens by moving
- * to a table-based implementation. Also folded handling of parens
- * into the table.
- * b) Combined all 3 loops which called arith_apply to reduce generated
- * code size at the cost of speed.
- *
- * 3) The following expressions were treated as valid by the original code:
- * 1() , 0! , 1 ( *3 ) .
- * These bugs have been fixed by internally enclosing the expression in
- * parens and then checking that all binary ops and right parens are
- * preceded by a valid expression (NUM_TOKEN).
- *
- * Note: It may be desireable to replace Aaron's test for whitespace with
- * ctype's isspace() if it is used by another busybox applet or if additional
- * whitespace chars should be considered. Look below the "#include"s for a
- * precompiler test.
- */
-
-/*
- * Aug 26, 2001 Manuel Novoa III
- *
- * Return 0 for null expressions. Pointed out by vodz.
- *
- * Merge in Aaron's comments previously posted to the busybox list,
- * modified slightly to take account of my changes to the code.
- *
- * TODO: May want to allow access to variables in the arith code.
- * This would:
- * 1) allow us to evaluate $A as 0 if A isn't set (although this
- * would require changes to ash.c too).
- * 2) allow us to write expressions as $(( A + 2 )).
- * This could be done using a callback function passed to the
- * arith() function of by requiring such a function with fixed
- * name as an extern.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include "libbb.h"
-
-/*
- * Use "#if 1" below for Aaron's original test for whitespace.
- * Use "#if 0" for ctype's isspace().
- * */
-#if 1
-#undef isspace
-#define isspace(arithval) \
- (arithval == ' ' || arithval == '\n' || arithval == '\t')
-#endif
-
-typedef char operator;
-
-/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
- * precedence, and high 3 are an ID unique accross operators of that
- * precedence. The ID portion is so that multiple operators can have the
- * same precedence, ensuring that the leftmost one is evaluated first.
- * Consider * and /. */
-
-#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)
-
-/* For now all unary operators have the same precedence, and that's used to
- * identify them as unary operators */
-#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_UPLUS tok_decl(UNARYPREC,3)
-
-#define TOK_NUM tok_decl(15,0)
-#define TOK_RPAREN tok_decl(15,1)
-#define TOK_ERROR tok_decl(15,2) /* just a place-holder really */
-
-#define ARITH_APPLY(op) arith_apply(op, numstack, &numstackptr)
-#define NUMPTR (*numstackptr)
-
-/* "applying" a token means performing it on the top elements on the integer
- * stack. For a unary operator it will only change the top element, but a
- * binary operator will pop two arguments and push a result */
-static short arith_apply(operator op, long *numstack, long **numstackptr)
-{
- long numptr_val;
- long *NUMPTR_M1;
-
- if (NUMPTR == numstack) goto err; /* There is no operator that can work
- without arguments */
- NUMPTR_M1 = NUMPTR - 1;
- if (op == TOK_UMINUS)
- *NUMPTR_M1 *= -1;
- else if (op == TOK_NOT)
- *NUMPTR_M1 = !(*NUMPTR_M1);
- else if (op == TOK_BNOT)
- *NUMPTR_M1 = ~(*NUMPTR_M1);
- else if (op != TOK_UPLUS) {
- /* Binary operators */
- if (NUMPTR_M1 == numstack) goto err; /* ... and binary operators need two
- arguments */
- numptr_val = *--NUMPTR; /* ... and they pop one */
- NUMPTR_M1 = NUMPTR - 1;
- if (op == TOK_BOR)
- *NUMPTR_M1 |= numptr_val;
- else if (op == TOK_OR)
- *NUMPTR_M1 = numptr_val || *NUMPTR_M1;
- else if (op == TOK_BAND)
- *NUMPTR_M1 &= numptr_val;
- else if (op == TOK_AND)
- *NUMPTR_M1 = *NUMPTR_M1 && numptr_val;
- else if (op == TOK_EQ)
- *NUMPTR_M1 = (*NUMPTR_M1 == numptr_val);
- else if (op == TOK_NE)
- *NUMPTR_M1 = (*NUMPTR_M1 != numptr_val);
- else if (op == TOK_GE)
- *NUMPTR_M1 = (*NUMPTR_M1 >= numptr_val);
- else if (op == TOK_RSHIFT)
- *NUMPTR_M1 >>= numptr_val;
- else if (op == TOK_LSHIFT)
- *NUMPTR_M1 <<= numptr_val;
- else if (op == TOK_GT)
- *NUMPTR_M1 = (*NUMPTR_M1 > numptr_val);
- else if (op == TOK_LT)
- *NUMPTR_M1 = (*NUMPTR_M1 < numptr_val);
- else if (op == TOK_LE)
- *NUMPTR_M1 = (*NUMPTR_M1 <= numptr_val);
- else if (op == TOK_MUL)
- *NUMPTR_M1 *= numptr_val;
- else if (op == TOK_ADD)
- *NUMPTR_M1 += numptr_val;
- else if (op == TOK_SUB)
- *NUMPTR_M1 -= numptr_val;
- else if(numptr_val==0) /* zero divisor check */
- return -2;
- else if (op == TOK_DIV)
- *NUMPTR_M1 /= numptr_val;
- else if (op == TOK_REM)
- *NUMPTR_M1 %= numptr_val;
- /* WARNING!!! WARNING!!! WARNING!!! */
- /* Any new operators should be added BEFORE the zero divisor check! */
- }
- return 0;
-err: return(-1);
-}
-
-static const char endexpression[] = ")";
-
-/* + and - (in that order) must be last */
-static const char op_char[] = "!<>=|&*/%~()+-";
-static const char op_token[] = {
- /* paired with equal */
- TOK_NE, TOK_LE, TOK_GE,
- /* paired with self -- note: ! is special-cased below*/
- TOK_ERROR, TOK_LSHIFT, TOK_RSHIFT, TOK_EQ, TOK_OR, TOK_AND,
- /* singles */
- TOK_NOT, TOK_LT, TOK_GT, TOK_ERROR, TOK_BOR, TOK_BAND,
- TOK_MUL, TOK_DIV, TOK_REM, TOK_BNOT, TOK_LPAREN, TOK_RPAREN,
- TOK_ADD, TOK_SUB, TOK_UPLUS, TOK_UMINUS
-};
-
-#define NUM_PAIR_EQUAL 3
-#define NUM_PAIR_SAME 6
-
-extern long arith (const char *expr, int *errcode)
-{
- register char arithval; /* Current character under analysis */
- operator lasttok, op;
- unsigned char prec;
-
- const char *p = endexpression;
-
- size_t datasizes = strlen(expr) + 2;
-
- /* Stack of integers */
- /* The proof that there can be no more than strlen(startbuf)/2+1 integers
- * in any given correct or incorrect expression is left as an excersize to
- * the reader. */
- long *numstack = alloca(((datasizes)/2)*sizeof(long)),
- *numstackptr = numstack;
- /* Stack of operator tokens */
- operator *stack = alloca((datasizes) * sizeof(operator)),
- *stackptr = stack;
-
- *numstack = 0;
- *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
-
- loop:
- if ((arithval = *expr) == 0) {
- if (p == endexpression) { /* Null expression. */
- *errcode = 0;
- return *numstack;
- }
-
- /* This is only reached after all tokens have been extracted from the
- * input stream. If there are still tokens on the operator stack, they
- * are to be applied in order. At the end, there should be a final
- * result on the integer stack */
-
- if (expr != endexpression + 1) { /* If we haven't done so already, */
- expr = endexpression; /* append a closing right paren */
- goto loop; /* and let the loop process it. */
- }
- /* At this point, we're done with the expression. */
- if (numstackptr != numstack+1) {/* ... but if there isn't, it's bad */
- err:
- return (*errcode = -1);
- /* NOTREACHED */
- }
- return *numstack;
- } else {
- /* Continue processing the expression. */
- if (isspace(arithval)) {
- goto prologue; /* Skip whitespace */
- }
- if ((unsigned)arithval-'0' <= 9) /* isdigit */ {
- *numstackptr++ = strtol(expr, (char **) &expr, 10);
- lasttok = TOK_NUM;
- goto loop;
- }
-#if 1
- if ((p = strchr(op_char, arithval)) == NULL) {
- goto err;
- }
-#else
- for ( p=op_char ; *p != arithval ; p++ ) {
- if (!*p) {
- goto err;
- }
- }
-#endif
- p = op_token + (int)(p - op_char);
- ++expr;
- if ((p >= op_token + NUM_PAIR_EQUAL) || (*expr != '=')) {
- p += NUM_PAIR_EQUAL;
- if ((p >= op_token + NUM_PAIR_SAME + NUM_PAIR_EQUAL)
- || (*expr != arithval) || (arithval == '!')) {
- --expr;
- if (arithval == '=') { /* single = */
- goto err;
- }
- p += NUM_PAIR_SAME;
- /* Plus and minus are binary (not unary) _only_ if the last
- * token was as number, or a right paren (which pretends to be
- * a number, since it evaluates to one). Think about it.
- * It makes sense. */
- if ((lasttok != TOK_NUM)
- && (p >= op_token + NUM_PAIR_SAME + NUM_PAIR_EQUAL
- + sizeof(op_char) - 2)) {
- p += 2; /* Unary plus or minus */
- }
- }
- }
- op = *p;
-
- /* We don't want a unary operator to cause recursive descent on the
- * stack, because there can be many in a row and it could cause an
- * operator to be evaluated before its argument is pushed onto the
- * integer stack. */
- /* But for binary operators, "apply" everything on the operator
- * stack until we find an operator with a lesser priority than the
- * one we have just extracted. */
- /* Left paren is given the lowest priority so it will never be
- * "applied" in this way */
- prec = PREC(op);
- if ((prec > 0) && (prec != UNARYPREC)) { /* not left paren or unary */
- if (lasttok != TOK_NUM) { /* binary op must be preceded by a num */
- goto err;
- }
- while (stackptr != stack) {
- if (op == TOK_RPAREN) {
- /* The algorithm employed here is simple: while we don't
- * hit an open paren nor the bottom of the stack, pop
- * tokens and apply them */
- if (stackptr[-1] == TOK_LPAREN) {
- --stackptr;
- lasttok = TOK_NUM; /* Any operator directly after a */
- /* close paren should consider itself binary */
- goto prologue;
- }
- } else if (PREC(stackptr[-1]) < prec) {
- break;
- }
- *errcode = ARITH_APPLY(*--stackptr);
- if(*errcode) return *errcode;
- }
- if (op == TOK_RPAREN) {
- goto err;
- }
- }
-
- /* Push this operator to the stack and remember it. */
- *stackptr++ = lasttok = op;
-
- prologue:
- ++expr;
- goto loop;
- }
-}
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Original BSD copyright notice is retained at the end of this file.
+ */
+
+/*
+ * rewrite arith.y to micro stack based cryptic algorithm by
+ * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
*
- * Modified by Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
- *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2003 to be
+ * used in busybox and size optimizations,
+ * support locale, rewrited arith (see notes to this)
*
- * Original BSD copyright notice is retained at the end of this file.
*/
+
/*
* The follow should be set to reflect the type of system you have:
* JOBS -> 1 if you have Berkeley job control, 0 otherwise.
#include <sysexits.h>
#include <fnmatch.h>
-#include <glob.h>
-
#include "busybox.h"
static int parselleft; /* copy of parsefile->lleft */
/* next character in input buffer */
-static char *parsenextc; /* copy of parsefile->nextc */
+static char *parsenextc; /* copy of parsefile->nextc */
static struct parsefile basepf; /* top level input file */
static char basebuf[IBUFSIZ]; /* buffer for top level input file */
static struct parsefile *parsefile = &basepf; /* current input file */
#define TRACEV(param)
#endif
-#if defined(__GNUC__) && __GNUC__ < 3
-#define va_copy __va_copy
-#endif
-
#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
#define __builtin_expect(x, expected_value) (x)
#endif
#define likely(x) __builtin_expect((x),1)
-#define unlikely(x) __builtin_expect((x),0)
+
#define TEOF 0
#define TNL 1
static int evalcmd(int, char **);
static int execcmd(int, char **);
static int exitcmd(int, char **);
-#ifdef CONFIG_ASH_MATH_SUPPORT
-static int expcmd(int, char **);
-#endif
static int exportcmd(int, char **);
static int falsecmd(int, char **);
#ifdef JOBS
#ifdef JOBS
static int jobscmd(int, char **);
#endif
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static int letcmd(int, char **);
+#endif
static int localcmd(int, char **);
static int pwdcmd(int, char **);
static int readcmd(int, char **);
{ BUILTIN_SPEC_REG "eval", evalcmd },
{ BUILTIN_SPEC_REG "exec", execcmd },
{ BUILTIN_SPEC_REG "exit", exitcmd },
-#ifdef CONFIG_ASH_MATH_SUPPORT
- { BUILTIN_NOSPEC "exp", expcmd },
-#endif
{ BUILTIN_SPEC_REG_ASSG "export", exportcmd },
{ BUILTIN_REGULAR "false", falsecmd },
#ifdef JOBS
{ BUILTIN_REGULAR "kill", killcmd },
#endif
#ifdef CONFIG_ASH_MATH_SUPPORT
- { BUILTIN_NOSPEC "let", expcmd },
+ { BUILTIN_NOSPEC "let", letcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
{ BUILTIN_NOSPEC "pwd", pwdcmd },
static void unsetfunc(const char *);
#ifdef CONFIG_ASH_MATH_SUPPORT
-/* From arith.y */
static int dash_arith(const char *);
#endif
#define VTABSIZE 39
-static const char defpathvar[] =
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
+static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
#ifdef IFS_BROKEN
static const char defifsvar[] = "IFS= \t\n";
#define defifs (defifsvar + 4)
static void ifsbreakup(char *, struct arglist *);
static void ifsfree(void);
static void expandmeta(struct strlist *, int);
-static void addglob(const glob_t *);
-static void addfname(char *);
static int patmatch(char *, const char *);
static int cvtnum(long);
#define pmatch(a, b) !fnmatch((a), (b), 0)
/*
- * Prepare a pattern for a glob(3) call.
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
*
* Returns an stalloced string.
*/
}
-
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
static char *
-scanleft(
- char *startp, char *rmesc, char *rmescend, char *str, int quotes,
- int zero
-) {
+scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+ int zero)
+{
char *loc;
char *loc2;
char c;
static char *
-scanright(
- char *startp, char *rmesc, char *rmescend, char *str, int quotes,
- int zero
-) {
+scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+ int zero)
+{
int esc = 0;
char *loc;
char *loc2;
}
-
/*
* Put a string on the stack.
*/
}
-
/*
* Add the value of a specialized variable to the stack string.
*/
}
-
/*
* Record the fact that we have to scan this region of the
* string for IFS characters.
}
-
/*
* Break the argument string into pieces based upon IFS and add the
* strings to the argument list. The regions of the string to be
INTON;
}
+static void expmeta(char *, char *);
+static struct strlist *expsort(struct strlist *);
+static struct strlist *msort(struct strlist *, int);
+static char *expdir;
-/*
- * Expand shell metacharacters. At this point, the only control characters
- * should be escapes. The results are stored in the list exparg.
- */
static void
-expandmeta(str, flag)
- struct strlist *str;
- int flag;
+expandmeta(struct strlist *str, int flag)
{
+ static const char metachars[] = {
+ '*', '?', '[', 0
+ };
/* TODO - EXP_REDIR */
while (str) {
- const char *p;
- glob_t pglob;
- int i;
+ struct strlist **savelastp;
+ struct strlist *sp;
+ char *p;
if (fflag)
goto nometa;
+ if (!strpbrk(str->text, metachars))
+ goto nometa;
+ savelastp = exparg.lastp;
+
INTOFF;
p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
- i = glob(p, GLOB_NOMAGIC, 0, &pglob);
+ {
+ int i = strlen(str->text);
+ expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+ }
+
+ expmeta(expdir, p);
+ ckfree(expdir);
if (p != str->text)
ckfree(p);
- switch (i) {
- case 0:
- if (!(pglob.gl_flags & GLOB_MAGCHAR))
- goto nometa2;
- addglob(&pglob);
- globfree(&pglob);
- INTON;
- break;
- case GLOB_NOMATCH:
-nometa2:
- globfree(&pglob);
- INTON;
+ INTON;
+ if (exparg.lastp == savelastp) {
+ /*
+ * no matches
+ */
nometa:
*exparg.lastp = str;
rmescapes(str->text);
exparg.lastp = &str->next;
- break;
- default: /* GLOB_NOSPACE */
- error(bb_msg_memory_exhausted);
+ } else {
+ *exparg.lastp = NULL;
+ *savelastp = sp = expsort(*savelastp);
+ while (sp->next != NULL)
+ sp = sp->next;
+ exparg.lastp = &sp->next;
}
str = str->next;
}
}
-
/*
- * Add the result of glob(3) to the list.
+ * Add a file name to the list.
*/
static void
-addglob(pglob)
- const glob_t *pglob;
+addfname(const char *name)
{
- char **p = pglob->gl_pathv;
+ struct strlist *sp;
- do {
- addfname(*p);
- } while (*++p);
+ sp = (struct strlist *)stalloc(sizeof *sp);
+ sp->text = sstrdup(name);
+ *exparg.lastp = sp;
+ exparg.lastp = &sp->next;
}
/*
- * Add a file name to the list.
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
*/
static void
-addfname(char *name)
+expmeta(char *enddir, char *name)
{
+ char *p;
+ const char *cp;
+ char *start;
+ char *endname;
+ int metaflag;
+ struct stat64 statb;
+ DIR *dirp;
+ struct dirent *dp;
+ int atend;
+ int matchdot;
+
+ metaflag = 0;
+ start = name;
+ for (p = name; *p; p++) {
+ if (*p == '*' || *p == '?')
+ metaflag = 1;
+ else if (*p == '[') {
+ char *q = p + 1;
+ if (*q == '!')
+ q++;
+ for (;;) {
+ if (*q == '\\')
+ q++;
+ if (*q == '/' || *q == '\0')
+ break;
+ if (*++q == ']') {
+ metaflag = 1;
+ break;
+ }
+ }
+ } else if (*p == '\\')
+ p++;
+ else if (*p == '/') {
+ if (metaflag)
+ goto out;
+ start = p + 1;
+ }
+ }
+out:
+ if (metaflag == 0) { /* we've reached the end of the file name */
+ if (enddir != expdir)
+ metaflag++;
+ p = name;
+ do {
+ if (*p == '\\')
+ p++;
+ *enddir++ = *p;
+ } while (*p++);
+ if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
+ addfname(expdir);
+ return;
+ }
+ endname = p;
+ if (name < start) {
+ p = name;
+ do {
+ if (*p == '\\')
+ p++;
+ *enddir++ = *p++;
+ } while (p < start);
+ }
+ if (enddir == expdir) {
+ cp = ".";
+ } else if (enddir == expdir + 1 && *expdir == '/') {
+ cp = "/";
+ } else {
+ cp = expdir;
+ enddir[-1] = '\0';
+ }
+ if ((dirp = opendir(cp)) == NULL)
+ return;
+ if (enddir != expdir)
+ enddir[-1] = '/';
+ if (*endname == 0) {
+ atend = 1;
+ } else {
+ atend = 0;
+ *endname++ = '\0';
+ }
+ matchdot = 0;
+ p = start;
+ if (*p == '\\')
+ p++;
+ if (*p == '.')
+ matchdot++;
+ while (! intpending && (dp = readdir(dirp)) != NULL) {
+ if (dp->d_name[0] == '.' && ! matchdot)
+ continue;
+ if (pmatch(start, dp->d_name)) {
+ if (atend) {
+ scopy(dp->d_name, enddir);
+ addfname(expdir);
+ } else {
+ for (p = enddir, cp = dp->d_name;
+ (*p++ = *cp++) != '\0';)
+ continue;
+ p[-1] = '/';
+ expmeta(p, endname);
+ }
+ }
+ }
+ closedir(dirp);
+ if (! atend)
+ endname[-1] = '/';
+}
+
+/*
+ * Sort the results of file name expansion. It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+
+static struct strlist *
+expsort(struct strlist *str)
+{
+ int len;
struct strlist *sp;
- sp = (struct strlist *)stalloc(sizeof *sp);
- sp->text = sstrdup(name);
- *exparg.lastp = sp;
- exparg.lastp = &sp->next;
+ len = 0;
+ for (sp = str ; sp ; sp = sp->next)
+ len++;
+ return msort(str, len);
+}
+
+
+static struct strlist *
+msort(struct strlist *list, int len)
+{
+ struct strlist *p, *q = NULL;
+ struct strlist **lpp;
+ int half;
+ int n;
+
+ if (len <= 1)
+ return list;
+ half = len >> 1;
+ p = list;
+ for (n = half ; --n >= 0 ; ) {
+ q = p;
+ p = p->next;
+ }
+ q->next = NULL; /* terminate first half of list */
+ q = msort(list, half); /* sort first half of list */
+ p = msort(p, len - half); /* sort second half */
+ lpp = &list;
+ for (;;) {
+#ifdef CONFIG_LOCALE_SUPPORT
+ if (strcoll(p->text, q->text) < 0)
+#else
+ if (strcmp(p->text, q->text) < 0)
+#endif
+ {
+ *lpp = p;
+ lpp = &p->next;
+ if ((p = *lpp) == NULL) {
+ *lpp = q;
+ break;
+ }
+ } else {
+ *lpp = q;
+ lpp = &q->next;
+ if ((q = *lpp) == NULL) {
+ *lpp = p;
+ break;
+ }
+ }
+ }
+ return list;
}
}
-
/*
* See if a pattern matches in a case statement.
*/
}
error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
-/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
+/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
/*
- * This file implements the input routines used by the parser.
+ * This implements the input routines used by the parser.
*/
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
}
-
/*
* To handle the "." command, a stack of input files is used. Pushfile
* adds a new entry to the stack and popfile restores the previous level.
}
-
/*
* Close the file(s) that the shell is reading commands from. Called
* after a fork is done.
parsefile->fd = 0;
}
}
-/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
+/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
/* mode flags for set_curjob */
#define CUR_DELETE 2
}
static int
-killcmd(argc, argv)
- int argc;
- char **argv;
+killcmd(int argc, char **argv)
{
int signo = -1;
int list = 0;
col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
start:
- fprintf(
- out, "%s%*c%s",
+ fprintf(out, "%s%*c%s",
s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
);
if (!(mode & SHOW_PID)) {
}
-
/*
* Convert a job name to a job structure.
*/
}
-
/*
* Return a new job structure.
* Called with interrupts off.
}
-
/*
* return 1 if there are stopped jobs, otherwise 0
*/
+
int
stoppedjobs(void)
{
}
}
-
static void
cmdputs(const char *s)
{
if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
exitshell();
- if (e == EXINT ) {
+ if (e == EXINT) {
outcslow('\n', stderr);
}
popstackmark(&smark);
evalstring(minusc, 0);
if (sflag || minusc == NULL) {
-state4: /* XXX ??? - why isn't this before the "if" statement */
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
if ( iflag ) {
const char *hp = lookupvar("HISTFILE");
load_history ( hp );
}
#endif
+state4: /* XXX ??? - why isn't this before the "if" statement */
cmdloop(1);
}
#if PROFILE
}
-
/*
* Read /etc/profile or .profile. Return on error.
*/
}
-
/*
* Read a file containing shell functions.
*/
/* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */
/*
- * Like malloc, but returns an error when out of space.
+ * Same for malloc, realloc, but returns an error when out of space.
*/
static pointer
-ckmalloc(size_t nbytes)
+ckrealloc(pointer p, size_t nbytes)
{
- pointer p;
-
- p = malloc(nbytes);
+ p = realloc(p, nbytes);
if (p == NULL)
error(bb_msg_memory_exhausted);
return p;
}
-
-/*
- * Same for realloc.
- */
-
static pointer
-ckrealloc(pointer p, size_t nbytes)
+ckmalloc(size_t nbytes)
{
- p = realloc(p, nbytes);
- if (p == NULL)
- error(bb_msg_memory_exhausted);
- return p;
+ return ckrealloc(NULL, nbytes);
}
-
/*
* Make a copy of a string in safe storage.
*/
}
-
void
setstackmark(struct stackmark *mark)
{
}
-
/*
* Check for a valid number. This should be elsewhere.
*/
}
-
static void
sizenodelist(struct nodelist *lp)
{
}
-
static union node *
copynode(union node *n)
{
}
-
static char *
nodesavestr(char *s)
{
}
-
/*
* Free a parse tree.
*/
}
-
static void
setoption(int flag, int val)
{
char *
endofname(const char *name)
- {
+{
char *p;
p = (char *) name;
static const char *const *findkwd(const char *s)
{
return bsearch(s, tokname_array + KWDOFFSET,
- (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
+ (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
sizeof(const char *), pstrcmp);
}
}
-
/*
* Called after a function returns.
* Interrupts must be off.
static int
dash_arith(const char *s)
{
- long result = 0;
+ long result;
int errcode = 0;
INTOFF;
result = arith(s, &errcode);
if (errcode < 0) {
- if (errcode == -2)
+ if (errcode == -3)
+ error("exponent less than 0");
+ else if (errcode == -2)
error("divide by zero");
+ else if (errcode == -5)
+ error("expression recursion loop detected");
else
synerror(s);
}
/*
- * The exp(1) builtin.
+ * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
+ * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+ *
+ * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
*/
+
static int
-expcmd(int argc, char **argv)
+letcmd(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 = nullstr;
-
- i = dash_arith(p);
+ ap = argv + 1;
+ if(!*ap)
+ error("expression expected");
+ for (ap = argv + 1; *ap; ap++) {
+ i = dash_arith(*ap);
+ }
- out1fmt("%ld\n", i);
- return (! i);
+ return (!i);
}
#endif /* CONFIG_ASH_MATH_SUPPORT */
return 0;
}
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+
+/* 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. The exact expression format should
+ * be that which POSIX specifies for shells. */
+
+/* The code uses a simple two-stack algorithm. See
+ * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
+ * for a detailed explaination of the infix-to-postfix algorithm on which
+ * this is based (this code differs in that it applies operators immediately
+ * to the stack instead of adding them to a queue to end up with an
+ * expression). */
+
+/* To use the routine, call it with an expression string and error return
+ * pointer */
+
+/*
+ * Aug 24, 2001 Manuel Novoa III
+ *
+ * Reduced the generated code size by about 30% (i386) and fixed several bugs.
+ *
+ * 1) In arith_apply():
+ * a) Cached values of *numptr and &(numptr[-1]).
+ * b) Removed redundant test for zero denominator.
+ *
+ * 2) In arith():
+ * a) Eliminated redundant code for processing operator tokens by moving
+ * to a table-based implementation. Also folded handling of parens
+ * into the table.
+ * b) Combined all 3 loops which called arith_apply to reduce generated
+ * code size at the cost of speed.
+ *
+ * 3) The following expressions were treated as valid by the original code:
+ * 1() , 0! , 1 ( *3 ) .
+ * These bugs have been fixed by internally enclosing the expression in
+ * parens and then checking that all binary ops and right parens are
+ * preceded by a valid expression (NUM_TOKEN).
+ *
+ * Note: It may be desireable to replace Aaron's test for whitespace with
+ * ctype's isspace() if it is used by another busybox applet or if additional
+ * whitespace chars should be considered. Look below the "#include"s for a
+ * precompiler test.
+ */
+
+/*
+ * Aug 26, 2001 Manuel Novoa III
+ *
+ * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
+ *
+ * Merge in Aaron's comments previously posted to the busybox list,
+ * modified slightly to take account of my changes to the code.
+ *
+ */
+
+/*
+ * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * - allow access to variable,
+ * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
+ * - realize assign syntax (VAR=expr, +=, *= etc)
+ * - realize exponentiation (** operator)
+ * - realize comma separated - expr, expr
+ * - realise ++expr --expr expr++ expr--
+ * - realise expr ? expr : expr (but, second expr calculate always)
+ * - allow hexdecimal and octal numbers
+ * - was restored loses XOR operator
+ * - remove one goto label, added three ;-)
+ * - protect $((num num)) as true zero expr (Manuel`s error)
+ * - always use special isspace(), see comment from bash ;-)
+ */
+
+
+#define arith_isspace(arithval) \
+ (arithval == ' ' || arithval == '\n' || arithval == '\t')
+
+
+typedef unsigned char operator;
+
+/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
+ * precedence, and 3 high bits are an ID unique accross operators of that
+ * precedence. The ID portion is so that multiple operators can have the
+ * same precedence, ensuring that the leftmost one is evaluated first.
+ * Consider * and /. */
+
+#define tok_decl(prec,id) (((id)<<5)|(prec))
+#define PREC(op) ((op) & 0x1F)
+
+#define TOK_LPAREN tok_decl(0,0)
+
+#define TOK_COMMA tok_decl(1,0)
+
+#define TOK_ASSIGN tok_decl(2,0)
+#define TOK_AND_ASSIGN tok_decl(2,1)
+#define TOK_OR_ASSIGN tok_decl(2,2)
+#define TOK_XOR_ASSIGN tok_decl(2,3)
+#define TOK_PLUS_ASSIGN tok_decl(2,4)
+#define TOK_MINUS_ASSIGN tok_decl(2,5)
+#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
+#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
+
+#define TOK_MUL_ASSIGN tok_decl(3,0)
+#define TOK_DIV_ASSIGN tok_decl(3,1)
+#define TOK_REM_ASSIGN tok_decl(3,2)
+
+/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
+#define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0)
+
+/* conditional is right associativity too */
+#define TOK_CONDITIONAL tok_decl(4,0)
+#define TOK_CONDITIONAL_SEP tok_decl(4,1)
+
+#define TOK_OR tok_decl(5,0)
+
+#define TOK_AND tok_decl(6,0)
+
+#define TOK_BOR tok_decl(7,0)
+
+#define TOK_BXOR tok_decl(8,0)
+
+#define TOK_BAND tok_decl(9,0)
+
+#define TOK_EQ tok_decl(10,0)
+#define TOK_NE tok_decl(10,1)
+
+#define TOK_LT tok_decl(11,0)
+#define TOK_GT tok_decl(11,1)
+#define TOK_GE tok_decl(11,2)
+#define TOK_LE tok_decl(11,3)
+
+#define TOK_LSHIFT tok_decl(12,0)
+#define TOK_RSHIFT tok_decl(12,1)
+
+#define TOK_ADD tok_decl(13,0)
+#define TOK_SUB tok_decl(13,1)
+
+#define TOK_MUL tok_decl(14,0)
+#define TOK_DIV tok_decl(14,1)
+#define TOK_REM tok_decl(14,2)
+
+/* exponent is right associativity */
+#define TOK_EXPONENT tok_decl(15,1)
+
+/* For now unary operators. */
+#define UNARYPREC 16
+#define TOK_BNOT tok_decl(UNARYPREC,0)
+#define TOK_NOT tok_decl(UNARYPREC,1)
+
+#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
+#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
+
+#define PREC_PRE (UNARYPREC+2)
+
+#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
+#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
+
+#define PREC_POST (UNARYPREC+3)
+
+#define TOK_POST_INC tok_decl(PREC_POST, 0)
+#define TOK_POST_DEC tok_decl(PREC_POST, 1)
+
+#define SPEC_PREC (UNARYPREC+4)
+
+#define TOK_NUM tok_decl(SPEC_PREC, 0)
+#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
+
+#define NUMPTR (*numstackptr)
+
+static inline int tok_have_assign(operator op)
+{
+ operator prec = PREC(op);
+
+ convert_prec_is_assing(prec);
+ return (prec == PREC(TOK_ASSIGN) ||
+ prec == PREC_PRE || prec == PREC_POST);
+}
+
+static inline int is_right_associativity(operator prec)
+{
+ return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) ||
+ prec == PREC(TOK_CONDITIONAL));
+}
+
+
+typedef struct ARITCH_VAR_NUM {
+ long val;
+ long contidional_second_val;
+ char contidional_second_val_initialized;
+ char *var; /* if NULL then is regular number,
+ else is varable name */
+} v_n_t;
+
+
+typedef struct CHK_VAR_RECURSIVE_LOOPED {
+ const char *var;
+ struct CHK_VAR_RECURSIVE_LOOPED *next;
+} chk_var_recursive_looped_t;
+
+static chk_var_recursive_looped_t *prev_chk_var_recursive;
+
+
+static int arith_lookup_val(v_n_t *t)
+{
+ if(t->var) {
+ const char * p = lookupvar(t->var);
+
+ if(p) {
+ int errcode;
+
+ /* recursive try as expression */
+ chk_var_recursive_looped_t *cur;
+ chk_var_recursive_looped_t cur_save;
+
+ for(cur = prev_chk_var_recursive; cur; cur = cur->next) {
+ if(strcmp(cur->var, t->var) == 0) {
+ /* expression recursion loop detected */
+ return -5;
+ }
+ }
+ /* save current lookuped var name */
+ cur = prev_chk_var_recursive;
+ cur_save.var = t->var;
+ cur_save.next = cur;
+ prev_chk_var_recursive = &cur_save;
+
+ t->val = arith (p, &errcode);
+ /* restore previous ptr after recursiving */
+ prev_chk_var_recursive = cur;
+ return errcode;
+ } else {
+ /* allow undefined var as 0 */
+ t->val = 0;
+ }
+ }
+ return 0;
+}
+
+/* "applying" a token means performing it on the top elements on the integer
+ * stack. For a unary operator it will only change the top element, but a
+ * binary operator will pop two arguments and push a result */
+static inline int
+arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
+{
+ long numptr_val;
+ v_n_t *numptr_m1;
+ long rez;
+ int ret_arith_lookup_val;
+
+ if (NUMPTR == numstack) goto err; /* There is no operator that can work
+ without arguments */
+ numptr_m1 = NUMPTR - 1;
+
+ /* check operand is var with noninteger value */
+ ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+ if(ret_arith_lookup_val)
+ return ret_arith_lookup_val;
+
+ rez = numptr_m1->val;
+ if (op == TOK_UMINUS)
+ rez *= -1;
+ else if (op == TOK_NOT)
+ rez = !rez;
+ else if (op == TOK_BNOT)
+ rez = ~rez;
+ else if (op == TOK_POST_INC || op == TOK_PRE_INC)
+ rez++;
+ else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
+ rez--;
+ else if (op != TOK_UPLUS) {
+ /* Binary operators */
+
+ /* check and binary operators need two arguments */
+ if (numptr_m1 == numstack) goto err;
+
+ /* ... and they pop one */
+ --NUMPTR;
+ numptr_val = rez;
+ if (op == TOK_CONDITIONAL) {
+ if(! numptr_m1->contidional_second_val_initialized) {
+ /* protect $((expr1 ? expr2)) without ": expr" */
+ goto err;
+ }
+ rez = numptr_m1->contidional_second_val;
+ } else if(numptr_m1->contidional_second_val_initialized) {
+ /* protect $((expr1 : expr2)) without "expr ? " */
+ goto err;
+ }
+ numptr_m1 = NUMPTR - 1;
+ if(op != TOK_ASSIGN) {
+ /* check operand is var with noninteger value for not '=' */
+ ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+ if(ret_arith_lookup_val)
+ return ret_arith_lookup_val;
+ }
+ if (op == TOK_CONDITIONAL) {
+ numptr_m1->contidional_second_val = rez;
+ }
+ rez = numptr_m1->val;
+ if (op == TOK_BOR || op == TOK_OR_ASSIGN)
+ rez |= numptr_val;
+ else if (op == TOK_OR)
+ rez = numptr_val || rez;
+ else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
+ rez &= numptr_val;
+ else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
+ rez ^= numptr_val;
+ else if (op == TOK_AND)
+ rez = rez && numptr_val;
+ else if (op == TOK_EQ)
+ rez = (rez == numptr_val);
+ else if (op == TOK_NE)
+ rez = (rez != numptr_val);
+ else if (op == TOK_GE)
+ rez = (rez >= numptr_val);
+ else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
+ rez >>= numptr_val;
+ else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
+ rez <<= numptr_val;
+ else if (op == TOK_GT)
+ rez = (rez > numptr_val);
+ else if (op == TOK_LT)
+ rez = (rez < numptr_val);
+ else if (op == TOK_LE)
+ rez = (rez <= numptr_val);
+ else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
+ rez *= numptr_val;
+ else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
+ rez += numptr_val;
+ else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
+ rez -= numptr_val;
+ else if (op == TOK_ASSIGN || op == TOK_COMMA)
+ rez = numptr_val;
+ else if (op == TOK_CONDITIONAL_SEP) {
+ if (numptr_m1 == numstack) {
+ /* protect $((expr : expr)) without "expr ? " */
+ goto err;
+ }
+ numptr_m1->contidional_second_val_initialized = op;
+ numptr_m1->contidional_second_val = numptr_val;
+ }
+ else if (op == TOK_CONDITIONAL) {
+ rez = rez ?
+ numptr_val : numptr_m1->contidional_second_val;
+ }
+ else if(op == TOK_EXPONENT) {
+ if(numptr_val < 0)
+ return -3; /* exponent less than 0 */
+ else {
+ long c = 1;
+
+ if(numptr_val)
+ while(numptr_val--)
+ c *= rez;
+ rez = c;
+ }
+ }
+ else if(numptr_val==0) /* zero divisor check */
+ return -2;
+ else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
+ rez /= numptr_val;
+ else if (op == TOK_REM || op == TOK_REM_ASSIGN)
+ rez %= numptr_val;
+ }
+ if(tok_have_assign(op)) {
+ char buf[32];
+
+ if(numptr_m1->var == NULL) {
+ /* Hmm, 1=2 ? */
+ goto err;
+ }
+ /* save to shell variable */
+ sprintf(buf, "%ld", rez);
+ setvar(numptr_m1->var, buf, 0);
+ /* after saving, make previous value for v++ or v-- */
+ if(op == TOK_POST_INC)
+ rez--;
+ else if(op == TOK_POST_DEC)
+ rez++;
+ }
+ numptr_m1->val = rez;
+ /* protect geting var value, is number now */
+ numptr_m1->var = NULL;
+ return 0;
+err: return(-1);
+}
+
+/* longest must first */
+static const char op_tokens[] = {
+ '<','<','=',0, TOK_LSHIFT_ASSIGN,
+ '>','>','=',0, TOK_RSHIFT_ASSIGN,
+ '<','<', 0, TOK_LSHIFT,
+ '>','>', 0, TOK_RSHIFT,
+ '|','|', 0, TOK_OR,
+ '&','&', 0, TOK_AND,
+ '!','=', 0, TOK_NE,
+ '<','=', 0, TOK_LE,
+ '>','=', 0, TOK_GE,
+ '=','=', 0, TOK_EQ,
+ '|','=', 0, TOK_OR_ASSIGN,
+ '&','=', 0, TOK_AND_ASSIGN,
+ '*','=', 0, TOK_MUL_ASSIGN,
+ '/','=', 0, TOK_DIV_ASSIGN,
+ '%','=', 0, TOK_REM_ASSIGN,
+ '+','=', 0, TOK_PLUS_ASSIGN,
+ '-','=', 0, TOK_MINUS_ASSIGN,
+ '-','-', 0, TOK_POST_DEC,
+ '^','=', 0, TOK_XOR_ASSIGN,
+ '+','+', 0, TOK_POST_INC,
+ '*','*', 0, TOK_EXPONENT,
+ '!', 0, TOK_NOT,
+ '<', 0, TOK_LT,
+ '>', 0, TOK_GT,
+ '=', 0, TOK_ASSIGN,
+ '|', 0, TOK_BOR,
+ '&', 0, TOK_BAND,
+ '*', 0, TOK_MUL,
+ '/', 0, TOK_DIV,
+ '%', 0, TOK_REM,
+ '+', 0, TOK_ADD,
+ '-', 0, TOK_SUB,
+ '^', 0, TOK_BXOR,
+ /* uniq */
+ '~', 0, TOK_BNOT,
+ ',', 0, TOK_COMMA,
+ '?', 0, TOK_CONDITIONAL,
+ ':', 0, TOK_CONDITIONAL_SEP,
+ ')', 0, TOK_RPAREN,
+ '(', 0, TOK_LPAREN,
+ 0
+};
+/* ptr to ")" */
+#define endexpression &op_tokens[sizeof(op_tokens)-7]
+
+
+extern long arith (const char *expr, int *perrcode)
+{
+ register char arithval; /* Current character under analysis */
+ operator lasttok, op;
+ operator prec;
+
+ const char *p = endexpression;
+ int errcode;
+
+ size_t datasizes = strlen(expr) + 2;
+
+ /* Stack of integers */
+ /* The proof that there can be no more than strlen(startbuf)/2+1 integers
+ * in any given correct or incorrect expression is left as an excersize to
+ * the reader. */
+ v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
+ *numstackptr = numstack;
+ /* Stack of operator tokens */
+ operator *stack = alloca((datasizes) * sizeof(operator)),
+ *stackptr = stack;
+
+ *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
+ *perrcode = errcode = 0;
+
+ while(1) {
+ if ((arithval = *expr) == 0) {
+ if (p == endexpression) {
+ /* Null expression. */
+ return 0;
+ }
+
+ /* This is only reached after all tokens have been extracted from the
+ * input stream. If there are still tokens on the operator stack, they
+ * are to be applied in order. At the end, there should be a final
+ * result on the integer stack */
+
+ if (expr != endexpression + 1) {
+ /* If we haven't done so already, */
+ /* append a closing right paren */
+ expr = endexpression;
+ /* and let the loop process it. */
+ continue;
+ }
+ /* At this point, we're done with the expression. */
+ if (numstackptr != numstack+1) {
+ /* ... but if there isn't, it's bad */
+ err:
+ return (*perrcode = -1);
+ }
+ if(numstack->var) {
+ /* expression is $((var)) only, lookup now */
+ errcode = arith_lookup_val(numstack);
+ }
+ ret:
+ *perrcode = errcode;
+ return numstack->val;
+ } else {
+ /* Continue processing the expression. */
+ if (arith_isspace(arithval)) {
+ /* Skip whitespace */
+ goto prologue;
+ }
+ if((p = endofname(expr)) != expr) {
+ int var_name_size = (p-expr) + 1; /* trailing zero */
+
+ numstackptr->var = alloca(var_name_size);
+ safe_strncpy(numstackptr->var, expr, var_name_size);
+ expr = p;
+ num:
+ numstackptr->contidional_second_val_initialized = 0;
+ numstackptr++;
+ lasttok = TOK_NUM;
+ continue;
+ } else if (is_digit(arithval)) {
+ numstackptr->var = NULL;
+ numstackptr->val = strtol(expr, (char **) &expr, 0);
+ goto num;
+ }
+ for(p = op_tokens; ; p++) {
+ const char *o;
+
+ if(*p == 0) {
+ /* strange operator not found */
+ goto err;
+ }
+ for(o = expr; *p && *o == *p; p++)
+ o++;
+ if(! *p) {
+ /* found */
+ expr = o - 1;
+ break;
+ }
+ /* skip tail uncompared token */
+ while(*p)
+ p++;
+ /* skip zero delim */
+ p++;
+ }
+ op = p[1];
+
+ /* post grammar: a++ reduce to num */
+ if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
+ lasttok = TOK_NUM;
+
+ /* Plus and minus are binary (not unary) _only_ if the last
+ * token was as number, or a right paren (which pretends to be
+ * a number, since it evaluates to one). Think about it.
+ * It makes sense. */
+ if (lasttok != TOK_NUM) {
+ switch(op) {
+ case TOK_ADD:
+ op = TOK_UPLUS;
+ break;
+ case TOK_SUB:
+ op = TOK_UMINUS;
+ break;
+ case TOK_POST_INC:
+ op = TOK_PRE_INC;
+ break;
+ case TOK_POST_DEC:
+ op = TOK_PRE_DEC;
+ break;
+ }
+ }
+ /* We don't want a unary operator to cause recursive descent on the
+ * stack, because there can be many in a row and it could cause an
+ * operator to be evaluated before its argument is pushed onto the
+ * integer stack. */
+ /* But for binary operators, "apply" everything on the operator
+ * stack until we find an operator with a lesser priority than the
+ * one we have just extracted. */
+ /* Left paren is given the lowest priority so it will never be
+ * "applied" in this way.
+ * if associativity is right and priority eq, applied also skip
+ */
+ prec = PREC(op);
+ if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
+ /* not left paren or unary */
+ if (lasttok != TOK_NUM) {
+ /* binary op must be preceded by a num */
+ goto err;
+ }
+ while (stackptr != stack) {
+ if (op == TOK_RPAREN) {
+ /* The algorithm employed here is simple: while we don't
+ * hit an open paren nor the bottom of the stack, pop
+ * tokens and apply them */
+ if (stackptr[-1] == TOK_LPAREN) {
+ --stackptr;
+ /* Any operator directly after a */
+ lasttok = TOK_NUM;
+ /* close paren should consider itself binary */
+ goto prologue;
+ }
+ } else {
+ operator prev_prec = PREC(stackptr[-1]);
+
+ convert_prec_is_assing(prec);
+ convert_prec_is_assing(prev_prec);
+ if (prev_prec < prec)
+ break;
+ /* check right assoc */
+ if(prev_prec == prec && is_right_associativity(prec))
+ break;
+ }
+ errcode = arith_apply(*--stackptr, numstack, &numstackptr);
+ if(errcode) goto ret;
+ }
+ if (op == TOK_RPAREN) {
+ goto err;
+ }
+ }
+
+ /* Push this operator to the stack and remember it. */
+ *stackptr++ = lasttok = op;
+
+ prologue:
+ ++expr;
+ }
+ }
+}
+#endif /* CONFIG_ASH_MATH_SUPPORT */
+
+
#ifdef DEBUG
const char *bb_applet_name = "debug stuff usage";
int main(int argc, char **argv)