/* expr.c -- arithmetic expression evaluation. */
-/* Copyright (C) 1990-2010 Free Software Foundation, Inc.
+/* Copyright (C) 1990-2013 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Implementation is a recursive-descent parser.
Chet Ramey
- chet@ins.CWRU.Edu
+ chet@po.cwru.edu
*/
#include "config.h"
#include "bashintl.h"
#include "shell.h"
+#include "typemax.h" /* INTMAX_MAX, INTMAX_MIN */
/* Because of the $((...)) construct, expressions may include newlines.
Here is a macro which accepts newlines, tabs and spaces as whitespace. */
#define cr_whitespace(c) (whitespace(c) || ((c) == '\n'))
-/* Size be which the expression stack grows when neccessary. */
+/* Size be which the expression stack grows when necessary. */
#define EXPR_STACK_GROW_SIZE 10
/* Maximum amount of recursion allowed. This prevents a non-integer
static void popexp __P((void));
static void expr_unwind __P((void));
static void expr_bind_variable __P((char *, char *));
+#if defined (ARRAY_VARS)
static void expr_bind_array_element __P((char *, arrayind_t, char *));
+#endif
static intmax_t subexpr __P((char *));
expr_bind_variable (lhs, rhs)
char *lhs, *rhs;
{
- (void)bind_int_variable (lhs, rhs);
+ SHELL_VAR *v;
+
+ v = bind_int_variable (lhs, rhs);
+ if (v && (readonly_p (v) || noassign_p (v)))
+ longjmp (evalbuf, 1); /* variable assignment error */
stupidly_hack_special_variables (lhs);
}
+#if defined (ARRAY_VARS)
/* Rewrite tok, which is of the form vname[expression], to vname[ind], where
IND is the already-calculated value of expression. */
static void
sprintf (lhs, "%s[%s]", vname, istr); /* XXX */
- expr_bind_variable (lhs, rhs);
/*itrace("expr_bind_array_element: %s=%s", lhs, rhs);*/
+ expr_bind_variable (lhs, rhs);
free (vname);
free (lhs);
}
+#endif /* ARRAY_VARS */
/* Evaluate EXPR, and return the arithmetic result. If VALIDP is
non-null, a zero is stored into the location to which it points
FASTCOPY (evalbuf, oevalbuf, sizeof (evalbuf));
- c = setjmp (evalbuf);
+ c = setjmp_nosigs (evalbuf);
if (c)
{
register intmax_t value;
char *lhs, *rhs;
arrayind_t lind;
+#if defined (HAVE_IMAXDIV)
+ imaxdiv_t idiv;
+#endif
value = expcond ();
if (curtok == EQ || curtok == OP_ASSIGN)
lvalue = value;
}
+ /* XXX - watch out for pointer aliasing issues here */
lhs = savestring (tokstr);
/* save ind in case rhs is string var and evaluation overwrites it */
lind = curlval.ind;
if (special)
{
+ if ((op == DIV || op == MOD) && value == 0)
+ {
+ if (noeval == 0)
+ evalerror (_("division by 0"));
+ else
+ value = 1;
+ }
+
switch (op)
{
case MUL:
lvalue *= value;
break;
case DIV:
- if (value == 0)
- evalerror (_("division by 0"));
- lvalue /= value;
- break;
case MOD:
- if (value == 0)
- evalerror (_("division by 0"));
- lvalue %= value;
+ if (lvalue == INTMAX_MIN && value == -1)
+ lvalue = (op == DIV) ? INTMAX_MIN : 0;
+ else
+#if HAVE_IMAXDIV
+ {
+ idiv = imaxdiv (lvalue, value);
+ lvalue = (op == DIV) ? idiv.quot : idiv.rem;
+ }
+#else
+ lvalue = (op == DIV) ? lvalue / value : lvalue % value;
+#endif
break;
case PLUS:
lvalue += value;
rhs = itos (value);
if (noeval == 0)
{
+#if defined (ARRAY_VARS)
if (lind != -1)
expr_bind_array_element (lhs, lind, rhs);
else
+#endif
expr_bind_variable (lhs, rhs);
}
+ if (curlval.tokstr && curlval.tokstr == tokstr)
+ init_lvalue (&curlval);
+
free (rhs);
free (lhs);
FREE (tokstr);
tokstr = (char *)NULL; /* For freeing on errors. */
}
+
return (value);
}
readtok ();
val2 = expbxor ();
val1 = val1 | val2;
+ lasttok = NUM;
}
return (val1);
readtok ();
val2 = expband ();
val1 = val1 ^ val2;
+ lasttok = NUM;
}
return (val1);
readtok ();
val2 = exp5 ();
val1 = val1 & val2;
+ lasttok = NUM;
}
return (val1);
val1 = (val1 == val2);
else if (op == NEQ)
val1 = (val1 != val2);
+ lasttok = NUM;
}
return (val1);
}
val1 = val1 < val2;
else /* (op == GT) */
val1 = val1 > val2;
+ lasttok = NUM;
}
return (val1);
}
val1 = val1 << val2;
else
val1 = val1 >> val2;
+ lasttok = NUM;
}
return (val1);
val1 += val2;
else if (op == MINUS)
val1 -= val2;
+ lasttok = NUM;
}
return (val1);
}
exp2 ()
{
register intmax_t val1, val2;
+#if defined (HAVE_IMAXDIV)
+ imaxdiv_t idiv;
+#endif
val1 = exppower ();
(curtok == MOD))
{
int op = curtok;
+ char *stp, *sltp;
+ stp = tp;
readtok ();
val2 = exppower ();
+ /* Handle division by 0 and twos-complement arithmetic overflow */
if (((op == DIV) || (op == MOD)) && (val2 == 0))
- evalerror (_("division by 0"));
+ {
+ if (noeval == 0)
+ {
+ sltp = lasttp;
+ lasttp = stp;
+ while (lasttp && *lasttp && whitespace (*lasttp))
+ lasttp++;
+ evalerror (_("division by 0"));
+ lasttp = sltp;
+ }
+ else
+ val2 = 1;
+ }
+ else if (op == MOD && val1 == INTMAX_MIN && val2 == -1)
+ {
+ val1 = 0;
+ continue;
+ }
+ else if (op == DIV && val1 == INTMAX_MIN && val2 == -1)
+ val2 = 1;
if (op == MUL)
val1 *= val2;
- else if (op == DIV)
- val1 /= val2;
- else if (op == MOD)
- val1 %= val2;
+ else if (op == DIV || op == MOD)
+#if defined (HAVE_IMAXDIV)
+ {
+ idiv = imaxdiv (val1, val2);
+ val1 = (op == DIV) ? idiv.quot : idiv.rem;
+ }
+#else
+ val1 = (op == DIV) ? val1 / val2 : val1 % val2;
+#endif
+ lasttok = NUM;
}
return (val1);
}
static intmax_t
+ipow (base, exp)
+ intmax_t base, exp;
+{
+ intmax_t result;
+
+ result = 1;
+ while (exp)
+ {
+ if (exp & 1)
+ result *= base;
+ exp >>= 1;
+ base *= base;
+ }
+ return result;
+}
+
+static intmax_t
exppower ()
{
register intmax_t val1, val2, c;
{
readtok ();
val2 = exppower (); /* exponentiation is right-associative */
+ lasttok = NUM;
if (val2 == 0)
return (1);
if (val2 < 0)
evalerror (_("exponent less than 0"));
- for (c = 1; val2--; c *= val1)
- ;
- val1 = c;
+ val1 = ipow (val1, val2);
}
return (val1);
}
{
readtok ();
val = !exp1 ();
+ lasttok = NUM;
}
else if (curtok == BNOT)
{
readtok ();
val = ~exp1 ();
+ lasttok = NUM;
}
else if (curtok == MINUS)
{
readtok ();
val = - exp1 ();
+ lasttok = NUM;
}
else if (curtok == PLUS)
{
readtok ();
val = exp1 ();
+ lasttok = NUM;
}
else
val = exp0 ();
vincdec = itos (v2);
if (noeval == 0)
{
+#if defined (ARRAY_VARS)
if (curlval.ind != -1)
expr_bind_array_element (curlval.tokstr, curlval.ind, vincdec);
else
+#endif
expr_bind_variable (tokstr, vincdec);
}
free (vincdec);
}
else if (curtok == LPAR)
{
+ /* XXX - save curlval here? Or entire expression context? */
readtok ();
val = EXP_HIGHEST ();
vincdec = itos (v2);
if (noeval == 0)
{
+#if defined (ARRAY_VARS)
if (curlval.ind != -1)
expr_bind_array_element (curlval.tokstr, curlval.ind, vincdec);
else
+#endif
expr_bind_variable (tokstr, vincdec);
}
free (vincdec);
}
else
{
+ /* XXX - watch out for pointer aliasing issues here */
if (stok == STR) /* free new tokstr before old one is restored */
FREE (tokstr);
RESTORETOK (&ec);
}
-
}
readtok ();
arrayind_t ind;
#endif
+/*itrace("expr_streval: %s: noeval = %d", tok, noeval);*/
+ /* If we are suppressing evaluation, just short-circuit here instead of
+ going through the rest of the evaluator. */
+ if (noeval)
+ return (0);
+
/* [[[[[ */
#if defined (ARRAY_VARS)
v = (e == ']') ? array_variable_part (tok, (char **)0, (int *)0) : find_variable (tok);
jump_to_top_level (FORCE_EOF);
}
- ind = -1;
#if defined (ARRAY_VARS)
+ ind = -1;
/* Second argument of 0 to get_array_value means that we don't allow
references like array[@]. In this case, get_array_value is just
like get_variable_value in that it does not return newly-allocated
lvalue->tokstr = tok; /* XXX */
lvalue->tokval = tval;
lvalue->tokvar = v; /* XXX */
+#if defined (ARRAY_VARS)
lvalue->ind = ind;
+#else
+ lvalue->ind = -1;
+#endif
}
return (tval);
#endif /* ARRAY_VARS */
*cp = '\0';
+ /* XXX - watch out for pointer aliasing issues here */
+ if (curlval.tokstr && curlval.tokstr == tokstr)
+ init_lvalue (&curlval);
+
FREE (tokstr);
tokstr = savestring (tp);
*cp = c;