sh: Fix some bugs with exit status from case containing ;&.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 5 Feb 2012 20:04:53 +0000 (12:04 -0800)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 5 Feb 2012 20:48:20 +0000 (12:48 -0800)
Also, rework evalcase() to not evaluate any tree. Instead, return the
NCLISTFALLTHRU node and handle it in evaltree().

Fixed bugs:

* If a ;& list with non-zero exit status is followed by an empty ;; or final
  list, the exit status of the case command should be equal to the exit
  status of the ;& list, not 0.

* An empty ;& case should not reset $?.

Obtained-from:  FreeBSD 230161

bin/sh/eval.c
tools/regression/bin/sh/builtins/case17.0 [new file with mode: 0644]
tools/regression/bin/sh/builtins/case18.0 [new file with mode: 0644]
tools/regression/bin/sh/builtins/case19.0 [new file with mode: 0644]

index dc1385b..513af93 100644 (file)
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)eval.c  8.9 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/eval.c,v 1.116 2012/01/15 20:04:05 jilles Exp $
+ * $FreeBSD: src/bin/sh/eval.c,v 1.117 2012/01/15 21:39:38 jilles Exp $
  */
 
 #include <sys/time.h>
@@ -90,7 +90,7 @@ int oexitstatus;              /* saved exit status */
 
 static void evalloop(union node *, int);
 static void evalfor(union node *, int);
-static union node *evalcase(union node *, int);
+static union node *evalcase(union node *);
 static void evalsubshell(union node *, int);
 static void evalredir(union node *, int);
 static void expredir(union node *);
@@ -257,7 +257,18 @@ evaltree(union node *n, int flags)
                        evalfor(n, flags & ~EV_EXIT);
                        break;
                case NCASE:
-                       next = evalcase(n, flags);
+                       next = evalcase(n);
+                       break;
+               case NCLIST:
+                       next = n->nclist.body;
+                       break;
+               case NCLISTFALLTHRU:
+                       if (n->nclist.body) {
+                               evaltree(n->nclist.body, flags & ~EV_EXIT);
+                               if (evalskip)
+                                       goto out;
+                       }
+                       next = n->nclist.next;
                        break;
                case NDEFUN:
                        defun(n->narg.text, n->narg.next);
@@ -367,9 +378,14 @@ evalfor(union node *n, int flags)
 }
 
 
+/*
+ * Evaluate a case statement, returning the selected tree.
+ *
+ * The exit status needs care to get right.
+ */
 
 static union node *
-evalcase(union node *n, int flags)
+evalcase(union node *n)
 {
        union node *cp;
        union node *patp;
@@ -385,13 +401,12 @@ evalcase(union node *n, int flags)
                        if (casematch(patp, arglist.list->text)) {
                                popstackmark(&smark);
                                while (cp->nclist.next &&
-                                   cp->type == NCLISTFALLTHRU) {
-                                       evaltree(cp->nclist.body,
-                                           flags & ~EV_EXIT);
-                                       if (evalskip != 0)
-                                               return (NULL);
+                                   cp->type == NCLISTFALLTHRU &&
+                                   cp->nclist.body == NULL)
                                        cp = cp->nclist.next;
-                               }
+                               if (cp->nclist.next &&
+                                   cp->type == NCLISTFALLTHRU)
+                                       return (cp);
                                if (cp->nclist.body == NULL)
                                        exitstatus = 0;
                                return (cp->nclist.body);
diff --git a/tools/regression/bin/sh/builtins/case17.0 b/tools/regression/bin/sh/builtins/case17.0
new file mode 100644 (file)
index 0000000..cb65455
--- /dev/null
@@ -0,0 +1,3 @@
+# $FreeBSD: src/tools/regression/bin/sh/builtins/case17.0,v 1.1 2012/01/15 21:39:38 jilles Exp $
+
+! case x in x) false ;& y) esac
diff --git a/tools/regression/bin/sh/builtins/case18.0 b/tools/regression/bin/sh/builtins/case18.0
new file mode 100644 (file)
index 0000000..263d619
--- /dev/null
@@ -0,0 +1,7 @@
+# $FreeBSD: src/tools/regression/bin/sh/builtins/case18.0,v 1.1 2012/01/15 21:39:38 jilles Exp $
+
+case x$(false) in
+x)     ;&
+y)     [ $? != 0 ] ;;
+z)     false ;;
+esac
diff --git a/tools/regression/bin/sh/builtins/case19.0 b/tools/regression/bin/sh/builtins/case19.0
new file mode 100644 (file)
index 0000000..eb366dd
--- /dev/null
@@ -0,0 +1,7 @@
+# $FreeBSD: src/tools/regression/bin/sh/builtins/case19.0,v 1.1 2012/01/15 21:39:38 jilles Exp $
+
+[ "`case x in
+x)     false ;&
+y)     ;&
+z)     echo $? ;;
+esac`" != 0 ]