Imported from ../bash-2.05.tar.gz.
[platform/upstream/bash.git] / expr.c
diff --git a/expr.c b/expr.c
index 0930789..8ce348d 100644 (file)
--- 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
  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)]
        "*", "/", "%"
        "+", "-"
        "<<", ">>"
@@ -38,9 +41,7 @@
        "&&"
        "||"
        "expr ? expr : expr"
-       "=", "*=", "/=", "%=",
-       "+=", "-=", "<<=", ">>=",
-       "&=", "^=", "|="
+       "=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=", "&=", "^=", "|="
 
  (Note that most of these operators have special meaning to bash, and an
  entire expression should be quoted, e.g. "a=$a+1" or "a=a+1" to ensure
 
 #include <stdio.h>
 #include "bashansi.h"
+
 #if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
 #  include <unistd.h>
 #endif
 
+#include <ctype.h>
+
 #include "shell.h"
 
 /* Because of the $((...)) construct, expressions may include newlines.
 #define LSH    9       /* "<<" Left SHift */
 #define RSH    10      /* ">>" Right SHift */
 #define OP_ASSIGN 11   /* op= expassign as in Posix.2 */
-#define COND   12
+#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     '<'
 #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 */
@@ -132,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 ();
+               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. */
@@ -151,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
@@ -170,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;
 }
 
@@ -190,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);
 }
 
@@ -290,7 +331,7 @@ subexpr (expr)
 
   readtok ();
 
-  val = expassign ();
+  val = EXP_HIGHEST ();
 
   if (curtok != 0)
     evalerror ("syntax error in expression");
@@ -303,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 ()
 {
@@ -390,6 +417,9 @@ expassign ()
            case BOR:
              lvalue |= value;
              break;
+           case BXOR:
+             lvalue ^= value;
+             break;
            default:
              free (lhs);
              evalerror ("bug: bad expassign token");
@@ -400,7 +430,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);
@@ -431,12 +461,12 @@ expcond ()
 #if 0
       val1 = explor ();
 #else
-      val1 = expassign ();
+      val1 = EXP_HIGHEST ();
 #endif
       if (set_noeval)
-        noeval--;
+       noeval--;
       if (curtok != COL)
-        evalerror ("`:' expected for conditional expression");
+       evalerror ("`:' expected for conditional expression");
       readtok ();
       if (curtok == 0)
        evalerror ("expression expected");
@@ -448,7 +478,7 @@ expcond ()
        }
       val2 = explor ();
       if (set_noeval)
-        noeval--;
+       noeval--;
       rval = cval ? val1 : val2;
       lasttok = COND;
     }
@@ -665,27 +695,48 @@ exp2 ()
 {
   register long val1, val2;
 
-  val1 = exp1 ();
+  val1 = exppower ();
 
   while ((curtok == MUL) ||
-         (curtok == DIV) ||
-         (curtok == MOD))
+        (curtok == DIV) ||
+        (curtok == MOD))
     {
       int op = curtok;
 
       readtok ();
 
-      val2 = exp1 ();
+      val2 = exppower ();
 
       if (((op == DIV) || (op == MOD)) && (val2 == 0))
        evalerror ("division by 0");
 
       if (op == MUL)
-        val1 *= val2;
+       val1 *= val2;
       else if (op == DIV)
-        val1 /= val2;
+       val1 /= val2;
       else if (op == MOD)
-        val1 %= val2;
+       val1 %= val2;
+    }
+  return (val1);
+}
+
+static long
+exppower ()
+{
+  register long val1, val2, c;
+
+  val1 = exp1 ();
+  if (curtok == POWER)
+    {
+      readtok ();
+      val2 = exp1 ();
+      if (val2 == 0)
+       return (1L);
+      if (val2 < 0)
+       evalerror ("exponent less than 0");
+      for (c = 1; val2--; c *= val1)
+       ;
+      val1 = c;
     }
   return (val1);
 }
@@ -714,9 +765,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");
+
+      v2 = tokval + ((stok == PREINC) ? 1 : -1);
+      vincdec = itos (v2);
+      if (noeval == 0)
+       (void)bind_int_variable (tokstr, vincdec);
+      free (vincdec);
+      val = v2;
 
-  if (curtok == MINUS)
+      curtok = NUM;    /* make sure --x=7 is flagged as an error */
+      readtok ();
+    }
+  else if (curtok == MINUS)
     {
       readtok ();
       val = - exp0 ();
@@ -729,7 +802,7 @@ exp0 ()
   else if (curtok == LPAR)
     {
       readtok ();
-      val = expassign ();
+      val = EXP_HIGHEST ();
 
       if (curtok != RPAR)
        evalerror ("missing `)'");
@@ -740,6 +813,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
@@ -779,9 +865,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++;
@@ -804,24 +891,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;
     }
@@ -875,7 +979,13 @@ readtok ()
        c = LAND;
       else if ((c == BOR) && (c1 == BOR))
        c = LOR;
-      else if (c1 == EQ && member(c, "*/%+-&^|"))
+      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 */
          c = OP_ASSIGN;
@@ -905,7 +1015,7 @@ evalerror (msg)
 
 /* Convert a string to a long integer, with an arbitrary base.
    0nnn -> base 8
-   0xnn -> base 16
+   0[Xx]nn -> base 16
    Anything else: [base#]number (this is implemented to match ksh93)
 
    Base may be >=2 and <=64.  If base is <= 36, the numbers are drawn
@@ -1026,9 +1136,9 @@ main (argc, argv)
     {
       v = evalexp (argv[i], &expok);
       if (expok == 0)
-        fprintf (stderr, "%s: expression error\n", argv[i]);
+       fprintf (stderr, "%s: expression error\n", argv[i]);
       else
-        printf ("'%s' -> %ld\n", argv[i], v);
+       printf ("'%s' -> %ld\n", argv[i], v);
     }
   exit (0);
 }