aboutsummaryrefslogtreecommitdiffstats
path: root/expr.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>2000-03-17 21:46:59 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:53 +0000
commitbb70624e964126b7ac4ff085ba163a9c35ffa18f (patch)
treeba2dd4add13ada94b1899c6d4aca80195b80b74b /expr.c
parentb72432fdcc59300c6fe7c9d6c8a31ad3447933f5 (diff)
downloadandroid_external_bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.tar.gz
android_external_bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.tar.bz2
android_external_bash-bb70624e964126b7ac4ff085ba163a9c35ffa18f.zip
Imported from ../bash-2.04.tar.gz.
Diffstat (limited to 'expr.c')
-rw-r--r--expr.c188
1 files changed, 135 insertions, 53 deletions
diff --git a/expr.c b/expr.c
index 3b7f80e..14d3aba 100644
--- a/expr.c
+++ b/expr.c
@@ -6,7 +6,7 @@
Bash is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
+ the Free Software Foundation; either version 2, or (at your option)
any later version.
Bash is distributed in the hope that it will be useful, but WITHOUT
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with Bash; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+ Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
/*
All arithmetic is done as long integers with no checking for overflow
@@ -25,6 +25,8 @@
The following operators are handled, grouped into a set of levels in
order of decreasing precedence.
+ "id++", "id--" [post-increment and post-decrement]
+ "++id", "--id" [pre-increment and pre-decrement]
"-", "+" [(unary operators)]
"!", "~"
"**" [(exponentiation)]
@@ -75,6 +77,8 @@
# include <unistd.h>
#endif
+#include <ctype.h>
+
#include "shell.h"
/* Because of the $((...)) construct, expressions may include newlines.
@@ -104,6 +108,10 @@
#define OP_ASSIGN 11 /* op= expassign as in Posix.2 */
#define COND 12 /* exp1 ? exp2 : exp3 */
#define POWER 13 /* exp1**exp2 */
+#define PREINC 14 /* ++var */
+#define PREDEC 15 /* --var */
+#define POSTINC 16 /* var++ */
+#define POSTDEC 17 /* var-- */
#define EQ '='
#define GT '>'
#define LT '<'
@@ -121,6 +129,11 @@
#define BNOT '~' /* Bitwise NOT; Two's complement. */
#define QUES '?'
#define COL ':'
+#define COMMA ','
+
+/* This should be the function corresponding to the operator with the
+ highest precedence. */
+#define EXP_HIGHEST expcomma
static char *expression; /* The current expression */
static char *tp; /* token lexical position */
@@ -136,18 +149,25 @@ static procenv_t evalbuf;
static void readtok (); /* lexical analyzer */
static long subexpr (), expassign (), exp0 (), exp1 (), exp2 (), exp3 (),
exp4 (), exp5 (), expshift (), expland (), explor (),
- expband (), expbor (), expbxor (), expcond (), exppower ();
+ expband (), expbor (), expbxor (), expcond (), exppower (),
+ expcomma ();
static long strlong ();
static void evalerror ();
/* A structure defining a single expression context. */
typedef struct {
int curtok, lasttok;
- char *expression, *tp;
+ char *expression, *tp, *lasttp;
int tokval;
char *tokstr;
+ int noeval;
} EXPR_CONTEXT;
+typedef struct {
+ char *tokstr;
+ int tokval;
+} LVALUE;
+
/* Global var which contains the stack of expression contexts. */
static EXPR_CONTEXT **expr_stack;
static int expr_depth; /* Location in the stack. */
@@ -155,6 +175,28 @@ static int expr_stack_size; /* Number of slots already allocated. */
extern char *this_command_name;
+#define SAVETOK(X) \
+ do { \
+ (X)->curtok = curtok; \
+ (X)->lasttok = lasttok; \
+ (X)->tp = tp; \
+ (X)->lasttp = lasttp; \
+ (X)->tokval = tokval; \
+ (X)->tokstr = tokstr; \
+ (X)->noeval = noeval; \
+ } while (0)
+
+#define RESTORETOK(X) \
+ do { \
+ curtok = (X)->curtok; \
+ lasttok = (X)->lasttok; \
+ tp = (X)->tp; \
+ lasttp = (X)->lasttp; \
+ tokval = (X)->tokval; \
+ tokstr = (X)->tokstr; \
+ noeval = (X)->noeval; \
+ } while (0)
+
/* Push and save away the contents of the globals describing the
current expression context. */
static void
@@ -174,12 +216,9 @@ pushexp ()
context = (EXPR_CONTEXT *)xmalloc (sizeof (EXPR_CONTEXT));
- context->curtok = curtok;
- context->lasttok = lasttok;
context->expression = expression;
- context->tp = tp;
- context->tokval = tokval;
- context->tokstr = tokstr;
+ SAVETOK(context);
+
expr_stack[expr_depth++] = context;
}
@@ -194,12 +233,10 @@ popexp ()
evalerror ("recursion stack underflow");
context = expr_stack[--expr_depth];
- curtok = context->curtok;
- lasttok = context->lasttok;
+
expression = context->expression;
- tp = context->tp;
- tokval = context->tokval;
- tokstr = context->tokstr;
+ RESTORETOK (context);
+
free (context);
}
@@ -294,7 +331,7 @@ subexpr (expr)
readtok ();
- val = expassign ();
+ val = EXP_HIGHEST ();
if (curtok != 0)
evalerror ("syntax error in expression");
@@ -307,35 +344,21 @@ subexpr (expr)
return val;
}
-/* Bind/create a shell variable with the name LHS to the RHS.
- This creates or modifies a variable such that it is an integer.
-
- This should really be in variables.c, but it is here so that all of the
- expression evaluation stuff is localized. Since we don't want any
- recursive evaluation from bind_variable() (possible without this code,
- since bind_variable() calls the evaluator for variables with the integer
- attribute set), we temporarily turn off the integer attribute for each
- variable we set here, then turn it back on after binding as necessary. */
-
-void
-bind_int_variable (lhs, rhs)
- char *lhs, *rhs;
+static long
+expcomma ()
{
- register SHELL_VAR *v;
- int isint = 0;
+ register long value;
- v = find_variable (lhs);
- if (v)
+ value = expassign ();
+ while (curtok == COMMA)
{
- isint = integer_p (v);
- v->attributes &= ~att_integer;
+ readtok ();
+ value = expassign ();
}
- v = bind_variable (lhs, rhs);
- if (isint)
- v->attributes |= att_integer;
+ return value;
}
-
+
static long
expassign ()
{
@@ -404,7 +427,7 @@ expassign ()
rhs = itos (value);
if (noeval == 0)
- bind_int_variable (lhs, rhs);
+ (void)bind_int_variable (lhs, rhs);
free (rhs);
free (lhs);
FREE (tokstr);
@@ -435,7 +458,7 @@ expcond ()
#if 0
val1 = explor ();
#else
- val1 = expassign ();
+ val1 = EXP_HIGHEST ();
#endif
if (set_noeval)
noeval--;
@@ -706,6 +729,8 @@ exppower ()
val2 = exp1 ();
if (val2 == 0)
return (1L);
+ if (val2 < 0)
+ evalerror ("exponent less than 0");
for (c = 1; val2--; c *= val1)
;
val1 = c;
@@ -737,9 +762,31 @@ exp1 ()
static long
exp0 ()
{
- register long val = 0L;
+ register long val = 0L, v2;
+ char *vincdec;
+ int stok;
+
+ /* XXX - might need additional logic here to decide whether or not
+ pre-increment or pre-decrement is legal at this point. */
+ if (curtok == PREINC || curtok == PREDEC)
+ {
+ stok = lasttok = curtok;
+ readtok ();
+ if (curtok != STR)
+ /* readtok() catches this */
+ evalerror ("identifier expected after pre-increment or pre-decrement");
- if (curtok == MINUS)
+ v2 = tokval + ((stok == PREINC) ? 1 : -1);
+ vincdec = itos (v2);
+ if (noeval == 0)
+ (void)bind_int_variable (tokstr, vincdec);
+ free (vincdec);
+ val = v2;
+
+ curtok = NUM; /* make sure --x=7 is flagged as an error */
+ readtok ();
+ }
+ else if (curtok == MINUS)
{
readtok ();
val = - exp0 ();
@@ -752,7 +799,7 @@ exp0 ()
else if (curtok == LPAR)
{
readtok ();
- val = expassign ();
+ val = EXP_HIGHEST ();
if (curtok != RPAR)
evalerror ("missing `)'");
@@ -763,6 +810,19 @@ exp0 ()
else if ((curtok == NUM) || (curtok == STR))
{
val = tokval;
+ if (curtok == STR && (*tp == '+' || *tp == '-') && tp[1] == *tp &&
+ (tp[2] == '\0' || (isalnum (tp[2]) == 0)))
+ {
+ /* post-increment or post-decrement */
+ v2 = val + ((*tp == '+') ? 1 : -1);
+ vincdec = itos (v2);
+ if (noeval == 0)
+ (void)bind_int_variable (tokstr, vincdec);
+ free (vincdec);
+ tp += 2;
+ curtok = NUM; /* make sure x++=7 is flagged as an error */
+ }
+
readtok ();
}
else
@@ -802,9 +862,10 @@ readtok ()
if (legal_variable_starter (c))
{
- /* Semi-bogus ksh compatibility feature -- variable names
- not preceded with a dollar sign are shell variables. */
- char *value;
+ /* variable names not preceded with a dollar sign are shell variables. */
+ char *value, *savecp;
+ EXPR_CONTEXT ec;
+ int peektok;
while (legal_variable_char (c))
c = *cp++;
@@ -827,24 +888,41 @@ readtok ()
#endif /* ARRAY_VARS */
*cp = '\0';
-
FREE (tokstr);
tokstr = savestring (tp);
+ *cp = c;
+ SAVETOK (&ec);
+ tokstr = (char *)NULL; /* keep it from being freed */
+ tp = savecp = cp;
+ noeval = 1;
+ readtok ();
+ peektok = curtok;
+ if (peektok == STR) /* free new tokstr before old one is restored */
+ FREE (tokstr);
+ RESTORETOK (&ec);
+ cp = savecp;
+
+ /* The tests for PREINC and PREDEC aren't strictly correct, but they
+ preserve old behavior if a construct like --x=9 is given. */
+ if (lasttok == PREINC || lasttok == PREDEC || peektok != EQ)
+ {
#if defined (ARRAY_VARS)
- value = (e == ']') ? get_array_value (tokstr, 0) : get_string_value (tokstr);
+ value = (e == ']') ? get_array_value (tokstr, 0) : get_string_value (tokstr);
#else
- value = get_string_value (tokstr);
+ value = get_string_value (tokstr);
#endif
- tokval = (value && *value) ? subexpr (value) : 0;
+ tokval = (value && *value) ? subexpr (value) : 0;
#if defined (ARRAY_VARS)
- if (e == ']')
- FREE (value); /* get_array_value returns newly-allocated memory */
+ if (e == ']')
+ FREE (value); /* get_array_value returns newly-allocated memory */
#endif
+ }
+ else
+ tokval = 0;
- *cp = c;
lasttok = curtok;
curtok = STR;
}
@@ -900,6 +978,10 @@ readtok ()
c = LOR;
else if ((c == '*') && (c1 == '*'))
c = POWER;
+ else if ((c == '-') && (c1 == '-') && legal_variable_starter (*cp))
+ c = PREDEC;
+ else if ((c == '+') && (c1 == '+') && legal_variable_starter (*cp))
+ c = PREINC;
else if (c1 == EQ && member(c, "*/%+-&^|"))
{
assigntok = c; /* a OP= b */