]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - expr.c
Bash-5.0 patch 4: the wait builtin without arguments only waits for known children...
[thirdparty/bash.git] / expr.c
diff --git a/expr.c b/expr.c
index 2177cfad1fd12a6fad54c6d6dcf234da11ede12b..9edc8d9cddbe9f39db2c1d79dfa11a23a9653ac5 100644 (file)
--- a/expr.c
+++ b/expr.c
@@ -1,6 +1,6 @@
 /* expr.c -- arithmetic expression evaluation. */
 
-/* Copyright (C) 1990-2010 Free Software Foundation, Inc.
+/* Copyright (C) 1990-2015 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -26,8 +26,8 @@
  order of decreasing precedence.
 
        "id++", "id--"          [post-increment and post-decrement]
-       "++id", "--id"          [pre-increment and pre-decrement]
        "-", "+"                [(unary operators)]
+       "++id", "--id"          [pre-increment and pre-decrement]
        "!", "~"
        "**"                    [(exponentiation)]
        "*", "/", "%"
@@ -63,7 +63,7 @@
  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 "arrayfunc.h"
+#include "execute_cmd.h"
+#include "flags.h"
+#include "subst.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
@@ -170,6 +175,9 @@ static intmax_t     tokval;         /* current token value */
 static int     noeval;         /* set to 1 if no assignment to be done */
 static procenv_t evalbuf;
 
+/* set to 1 if the expression has already been run through word expansion */
+static int     already_expanded;
+
 static struct lvalue curlval = {0, 0, 0, -1};
 static struct lvalue lastlval = {0, 0, 0, -1};
 
@@ -188,7 +196,9 @@ static void pushexp __P((void));
 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 *));
 
@@ -204,7 +214,7 @@ static intmax_t exp5 __P((void));
 static intmax_t exp4 __P((void));
 static intmax_t expshift __P((void));
 static intmax_t exp3 __P((void));
-static intmax_t exp2 __P((void));
+static intmax_t expmuldiv __P((void));
 static intmax_t        exppower __P((void));
 static intmax_t exp1 __P((void));
 static intmax_t exp0 __P((void));
@@ -214,9 +224,6 @@ static EXPR_CONTEXT **expr_stack;
 static int expr_depth;            /* Location in the stack. */
 static int expr_stack_size;       /* Number of slots already allocated. */
 
-extern char *this_command_name;
-extern int unbound_vars_is_error, last_command_exit_value;
-
 #if defined (ARRAY_VARS)
 extern const char * const bash_badsub_errmsg;
 #endif
@@ -276,8 +283,13 @@ popexp ()
 {
   EXPR_CONTEXT *context;
 
-  if (expr_depth == 0)
-    evalerror (_("recursion stack underflow"));
+  if (expr_depth <= 0)
+    {
+      /* See the comment at the top of evalexp() for an explanation of why
+        this is done. */
+      expression = lasttp = 0;
+      evalerror (_("recursion stack underflow"));
+    }
 
   context = expr_stack[--expr_depth];
 
@@ -300,7 +312,8 @@ expr_unwind ()
 
       free (expr_stack[expr_depth]);
     }
-  free (expr_stack[expr_depth]);       /* free the allocated EXPR_CONTEXT */
+  if (expr_depth == 0)
+    free (expr_stack[expr_depth]);     /* free the allocated EXPR_CONTEXT */
 
   noeval = 0;  /* XXX */
 }
@@ -309,10 +322,45 @@ static void
 expr_bind_variable (lhs, rhs)
      char *lhs, *rhs;
 {
-  (void)bind_int_variable (lhs, rhs);
+  SHELL_VAR *v;
+  int aflags;
+
+  if (lhs == 0 || *lhs == 0)
+    return;            /* XXX */
+
+#if defined (ARRAY_VARS)
+  aflags = (assoc_expand_once && already_expanded) ? ASS_NOEXPAND : 0;
+#else
+  aflags = 0;
+#endif
+  v = bind_int_variable (lhs, rhs, aflags);
+  if (v && (readonly_p (v) || noassign_p (v)))
+    sh_longjmp (evalbuf, 1);   /* variable assignment error */
   stupidly_hack_special_variables (lhs);
 }
 
+#if defined (ARRAY_VARS)
+/* This is similar to the logic in arrayfunc.c:valid_array_subscript when
+   you pass VA_NOEXPAND. */
+static int
+expr_skipsubscript (vp, cp)
+     char *vp, *cp;
+{
+  int flags, isassoc;
+  SHELL_VAR *entry;
+
+  isassoc = 0;
+  entry = 0;
+  if (assoc_expand_once & already_expanded)
+    {
+      *cp = '\0';
+      isassoc = legal_identifier (vp) && (entry = find_variable (vp)) && assoc_p (entry);
+      *cp = '[';       /* ] */
+    }
+  flags = (isassoc && assoc_expand_once && already_expanded) ? VA_NOEXPAND : 0;
+  return (skipsubscript (cp, 0, flags));
+}
+
 /* Rewrite tok, which is of the form vname[expression], to vname[ind], where
    IND is the already-calculated value of expression. */
 static void
@@ -326,18 +374,19 @@ expr_bind_array_element (tok, ind, rhs)
   char ibuf[INT_STRLEN_BOUND (arrayind_t) + 1], *istr;
 
   istr = fmtumax (ind, 10, ibuf, sizeof (ibuf), 0);
-  vname = array_variable_name (tok, (char **)NULL, (int *)NULL);
+  vname = array_variable_name (tok, 0, (char **)NULL, (int *)NULL);
 
   llen = strlen (vname) + sizeof (ibuf) + 3;
   lhs = xmalloc (llen);
 
   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
@@ -353,8 +402,9 @@ expr_bind_array_element (tok, ind, rhs)
    safe to let the loop terminate when expr_depth == 0, without freeing up
    any of the expr_depth[0] stuff. */
 intmax_t
-evalexp (expr, validp)
+evalexp (expr, flags, validp)
      char *expr;
+     int flags;
      int *validp;
 {
   intmax_t val;
@@ -363,10 +413,11 @@ evalexp (expr, validp)
 
   val = 0;
   noeval = 0;
+  already_expanded = (flags&EXP_EXPANDED);
 
   FASTCOPY (evalbuf, oevalbuf, sizeof (evalbuf));
 
-  c = setjmp (evalbuf);
+  c = setjmp_nosigs (evalbuf);
 
   if (c)
     {
@@ -375,6 +426,10 @@ evalexp (expr, validp)
       tokstr = expression = (char *)NULL;
 
       expr_unwind ();
+      expr_depth = 0;  /* XXX - make sure */
+
+      /* We copy in case we've called evalexp recursively */
+      FASTCOPY (oevalbuf, evalbuf, sizeof (evalbuf));
 
       if (validp)
        *validp = 0;
@@ -450,6 +505,9 @@ expassign ()
   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)
@@ -468,6 +526,10 @@ expassign ()
          lvalue = value;
        }
 
+      if (tokstr == 0)
+       evalerror (_("syntax error in variable assignment"));
+
+      /* 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;
@@ -476,20 +538,32 @@ expassign ()
 
       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;
@@ -523,16 +597,22 @@ expassign ()
       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);
 }
 
@@ -547,24 +627,23 @@ expcond ()
   rval = cval = explor ();
   if (curtok == QUES)          /* found conditional expr */
     {
-      readtok ();
-      if (curtok == 0 || curtok == COL)
-       evalerror (_("expression expected"));
       if (cval == 0)
        {
          set_noeval = 1;
          noeval++;
        }
 
+      readtok ();
+      if (curtok == 0 || curtok == COL)
+       evalerror (_("expression expected"));
+
       val1 = EXP_HIGHEST ();
 
       if (set_noeval)
        noeval--;
       if (curtok != COL)
        evalerror (_("`:' expected for conditional expression"));
-      readtok ();
-      if (curtok == 0)
-       evalerror (_("expression expected"));
+
       set_noeval = 0;
       if (cval)
        {
@@ -572,7 +651,11 @@ expcond ()
          noeval++;
        }
 
+      readtok ();
+      if (curtok == 0)
+       evalerror (_("expression expected"));
       val2 = expcond ();
+
       if (set_noeval)
        noeval--;
       rval = cval ? val1 : val2;
@@ -650,6 +733,7 @@ expbor ()
       readtok ();
       val2 = expbxor ();
       val1 = val1 | val2;
+      lasttok = NUM;
     }
 
   return (val1);
@@ -668,6 +752,7 @@ expbxor ()
       readtok ();
       val2 = expband ();
       val1 = val1 ^ val2;
+      lasttok = NUM;
     }
 
   return (val1);
@@ -686,6 +771,7 @@ expband ()
       readtok ();
       val2 = exp5 ();
       val1 = val1 & val2;
+      lasttok = NUM;
     }
 
   return (val1);
@@ -708,6 +794,7 @@ exp5 ()
        val1 = (val1 == val2);
       else if (op == NEQ)
        val1 = (val1 != val2);
+      lasttok = NUM;
     }
   return (val1);
 }
@@ -736,6 +823,7 @@ exp4 ()
        val1 = val1 < val2;
       else                     /* (op == GT) */
        val1 = val1 > val2;
+      lasttok = NUM;
     }
   return (val1);
 }
@@ -759,6 +847,7 @@ expshift ()
        val1 = val1 << val2;
       else
        val1 = val1 >> val2;
+      lasttok = NUM;
     }
 
   return (val1);
@@ -769,27 +858,31 @@ exp3 ()
 {
   register intmax_t val1, val2;
 
-  val1 = exp2 ();
+  val1 = expmuldiv ();
 
   while ((curtok == PLUS) || (curtok == MINUS))
     {
       int op = curtok;
 
       readtok ();
-      val2 = exp2 ();
+      val2 = expmuldiv ();
 
       if (op == PLUS)
        val1 += val2;
       else if (op == MINUS)
        val1 -= val2;
+      lasttok = NUM;
     }
   return (val1);
 }
 
 static intmax_t
-exp2 ()
+expmuldiv ()
 {
   register intmax_t val1, val2;
+#if defined (HAVE_IMAXDIV)
+  imaxdiv_t idiv;
+#endif
 
   val1 = exppower ();
 
@@ -798,24 +891,69 @@ exp2 ()
         (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 ()
 {
@@ -826,13 +964,12 @@ exppower ()
     {
       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);
 }
@@ -846,21 +983,25 @@ exp1 ()
     {
       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 ();
@@ -890,10 +1031,13 @@ 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
-           expr_bind_variable (tokstr, vincdec);
+#endif
+           if (tokstr)
+             expr_bind_variable (tokstr, vincdec);
        }
       free (vincdec);
       val = v2;
@@ -903,6 +1047,7 @@ exp0 ()
     }
   else if (curtok == LPAR)
     {
+      /* XXX - save curlval here?  Or entire expression context? */
       readtok ();
       val = EXP_HIGHEST ();
 
@@ -936,9 +1081,11 @@ 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);
@@ -946,11 +1093,11 @@ exp0 ()
            }
          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 ();
@@ -996,13 +1143,28 @@ expr_streval (tok, e, lvalue)
   SHELL_VAR *v;
   char *value;
   intmax_t tval;
+  int initial_depth;
 #if defined (ARRAY_VARS)
   arrayind_t ind;
+  int tflag, aflag;
+#endif
+
+/*itrace("expr_streval: %s: noeval = %d expanded=%d", tok, noeval, already_expanded);*/
+  /* If we are suppressing evaluation, just short-circuit here instead of
+     going through the rest of the evaluator. */
+  if (noeval)
+    return (0);
+
+  initial_depth = expr_depth;
+
+#if defined (ARRAY_VARS)
+  tflag = assoc_expand_once && already_expanded;       /* for a start */
 #endif
 
   /* [[[[[ */
 #if defined (ARRAY_VARS)
-  v = (e == ']') ? array_variable_part (tok, (char **)0, (int *)0) : find_variable (tok);
+  aflag = (tflag) ? AV_NOEXPAND : 0;
+  v = (e == ']') ? array_variable_part (tok, tflag, (char **)0, (int *)0) : find_variable (tok);
 #else
   v = find_variable (tok);
 #endif
@@ -1010,7 +1172,7 @@ expr_streval (tok, e, lvalue)
   if ((v == 0 || invisible_p (v)) && unbound_vars_is_error)
     {
 #if defined (ARRAY_VARS)
-      value = (e == ']') ? array_variable_name (tok, (char **)0, (int *)0) : tok;
+      value = (e == ']') ? array_variable_name (tok, tflag, (char **)0, (int *)0) : tok;
 #else
       value = tok;
 #endif
@@ -1023,6 +1185,9 @@ expr_streval (tok, e, lvalue)
        FREE (value);   /* array_variable_name returns new memory */
 #endif
 
+      if (no_longjmp_on_fatal_error && interactive_shell)
+       sh_longjmp (evalbuf, 1);
+
       if (interactive_shell)
        {
          expr_unwind ();
@@ -1033,17 +1198,25 @@ expr_streval (tok, e, lvalue)
        jump_to_top_level (FORCE_EOF);
     }
 
-  ind = -1;
 #if defined (ARRAY_VARS)
-  /* 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
-     memory or quote the results. */
-  value = (e == ']') ? get_array_value (tok, 0, (int *)NULL, &ind) : get_variable_value (v);
+  ind = -1;
+  /* If the second argument to get_array_value doesn't include AV_ALLOWALL,
+     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
+     memory or quote the results.  AFLAG is set above and is either AV_NOEXPAND
+     or 0. */
+  value = (e == ']') ? get_array_value (tok, aflag, (int *)NULL, &ind) : get_variable_value (v);
 #else
   value = get_variable_value (v);
 #endif
 
+  if (expr_depth < initial_depth)
+    {
+      if (no_longjmp_on_fatal_error && interactive_shell)
+       sh_longjmp (evalbuf, 1);
+      return (0);
+    }
+
   tval = (value && *value) ? subexpr (value) : 0;
 
   if (lvalue)
@@ -1051,7 +1224,11 @@ expr_streval (tok, e, lvalue)
       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);
@@ -1160,7 +1337,7 @@ readtok ()
 #if defined (ARRAY_VARS)
       if (c == '[')
        {
-         e = skipsubscript (cp, 0, 0);
+         e = expr_skipsubscript (tp, cp);              /* XXX - was skipsubscript */
          if (cp[e] == ']')
            {
              cp += e + 1;
@@ -1173,6 +1350,10 @@ readtok ()
 #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;
@@ -1257,6 +1438,14 @@ readtok ()
        c = POWER;
       else if ((c == '-' || c == '+') && c1 == c && curtok == STR)
        c = (c == '-') ? POSTDEC : POSTINC;
+      else if ((c == '-' || c == '+') && c1 == c && curtok == NUM && (lasttok == PREINC || lasttok == PREDEC))
+       {
+         /* This catches something like --FOO++ */
+         if (c == '-')
+           evalerror ("--: assignment requires lvalue");
+         else
+           evalerror ("++: assignment requires lvalue");
+       }
       else if ((c == '-' || c == '+') && c1 == c)
        {
          /* Quickly scan forward to see if this is followed by optional
@@ -1267,7 +1456,20 @@ readtok ()
          if (legal_variable_starter ((unsigned char)*xp))
            c = (c == '-') ? PREDEC : PREINC;
          else
+           /* Could force parsing as preinc or predec and throw an error */
+#if 0
+           {
+             /* Posix says unary plus and minus have higher priority than
+                preinc and predec. */
+             /* This catches something like --4++ */
+             if (c == '-')
+               evalerror ("--: assignment requires lvalue");
+             else
+               evalerror ("++: assignment requires lvalue");
+           }
+#else
            cp--;       /* not preinc or predec, so unget the character */
+#endif
        }
       else if (c1 == EQ && member (c, "*/%+-&^|"))
        {
@@ -1303,12 +1505,12 @@ evalerror (msg)
   char *name, *t;
 
   name = this_command_name;
-  for (t = expression; whitespace (*t); t++)
+  for (t = expression; t && whitespace (*t); t++)
     ;
   internal_error (_("%s%s%s: %s (error token is \"%s\")"),
-                  name ? name : "", name ? ": " : "", t,
-                  msg, (lasttp && *lasttp) ? lasttp : "");
-  longjmp (evalbuf, 1);
+                  name ? name : "", name ? ": " : "",
+                  t ? t : "", msg, (lasttp && *lasttp) ? lasttp : "");
+  sh_longjmp (evalbuf, 1);
 }
 
 /* Convert a string to an intmax_t integer, with an arbitrary base.
@@ -1430,7 +1632,7 @@ main (argc, argv)
 
   for (i = 1; i < argc; i++)
     {
-      v = evalexp (argv[i], &expok);
+      v = evalexp (argv[i], 0, &expok);
       if (expok == 0)
        fprintf (stderr, _("%s: expression error\n"), argv[i]);
       else