about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2015-05-21 10:25:07 +0100
committerPeter Stephenson <pws@zsh.org>2015-05-21 10:25:07 +0100
commitafb78f5d142786169817709e6ec4c48637bcae93 (patch)
tree8401778d995425aa59aaba0bd7136f2db7c57a51
parentaf957f2ed6287f66953742fbca69188cecb98fbf (diff)
downloadzsh-afb78f5d142786169817709e6ec4c48637bcae93.tar.gz
zsh-afb78f5d142786169817709e6ec4c48637bcae93.tar.xz
zsh-afb78f5d142786169817709e6ec4c48637bcae93.zip
35248: treat fully parenthised zsh patterns as complete case patterns again
-rw-r--r--ChangeLog6
-rw-r--r--Src/lex.c2
-rw-r--r--Src/parse.c95
-rw-r--r--Test/A01grammar.ztst36
4 files changed, 124 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index b1b96379c..4c1fe2116 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2015-05-21  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 35248: Src/lex.c, Src/parse.c, Test/A01grammar.ztst:
+	treat fully parenthesised zsh patterns as complete
+	case patterns again.
+
 2015-05-20  Peter Stephenson  <p.stephenson@samsung.com>
 
 	* Ismail: 35232: Completion/Unix/Type/_urls: matching
diff --git a/Src/lex.c b/Src/lex.c
index 87b0cd3af..841fb0b86 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -761,8 +761,6 @@ gettok(void)
 	lexstop = 0;
 	return BAR;
     case LX1_INPAR:
-	if (incasepat == 2)
-	    return INPAR;
 	d = hgetc();
 	if (d == '(') {
 	    if (infor) {
diff --git a/Src/parse.c b/Src/parse.c
index c48669995..053db3fe2 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1152,7 +1152,7 @@ par_case(int *cmplx)
 	YYERRORV(oecused);
     }
     brflag = (tok == INBRACE);
-    incasepat = 2;
+    incasepat = 1;
     incmdpos = 0;
     noaliases = ona;
     nocorrect = onc;
@@ -1165,10 +1165,8 @@ par_case(int *cmplx)
 	    zshlex();
 	if (tok == OUTBRACE)
 	    break;
-	if (tok == INPAR) {
-	    incasepat = 1;
+	if (tok == INPAR)
 	    zshlex();
-	}
 	if (tok != STRING)
 	    YYERRORV(oecused);
 	if (!strcmp(tokstr, "esac"))
@@ -1178,19 +1176,96 @@ par_case(int *cmplx)
 	pp = ecadd(0);
 	palts = ecadd(0);
 	nalts = 0;
+	/*
+	 * Hack here.
+	 *
+	 * [Pause for astonished hubbub to subside.]
+	 *
+	 * The next token we get may be
+	 * - ")" or "|" if we're looking at an honest-to-god
+	 *   "case" patten, either because there's no opening
+	 *   parenthesis, or because SH_GLOB is set and we
+	 *   managed to grab an initial "(" to mark the start
+	 *   of the case pattern.
+	 * - Something else --- we don't care what --- because
+	 *   we're parsing a complete "(...)" as a complete
+	 *   zsh pattern.  In that case, we treat this as a
+	 *   single instance of a case pattern but we pretend
+	 *   we're doing proper case parsing --- in which the
+	 *   parentheses and bar are in different words from
+	 *   the string, so may be separated by whitespace.
+	 *   So we quietly massage the whitespace and hope
+	 *   no one noticed.  This is horrible, but it's
+	 *   unfortunately too difficult to comine traditional
+	 *   zsh patterns with a properly parsed case pattern
+	 *   without generating incompatibilities which aren't
+	 *   all that popular (I've discovered).
+	 * - We can also end up with something other than ")" or "|"
+	 *   just because we're looking at garbage.
+	 *
+	 * Because of the second case, what happens next might
+	 * be the start of the command after the pattern, so we
+	 * need to treat it as in command position.  Luckily
+	 * this doesn't affect our ability to match a | or ) as
+	 * these are valid on command lines.
+	 */
+	incasepat = 0;
+	incmdpos = 1;
 	for (;;) {
-	    ecstr(str);
-	    ecadd(ecnpats++);
-	    nalts++;
-
 	    zshlex();
 	    if (tok == OUTPAR) {
+		ecstr(str);
+		ecadd(ecnpats++);
+		nalts++;
+
 		incasepat = 0;
 		incmdpos = 1;
 		zshlex();
 		break;
-	    } else if (tok != BAR)
+	    } else if (tok == BAR) {
+		ecstr(str);
+		ecadd(ecnpats++);
+		nalts++;
+
+		incasepat = 1;
+		incmdpos = 0;
+	    } else {
+		if (!nalts && str[0] == Inpar) {
+		    int pct = 0, sl;
+		    char *s;
+
+		    for (s = str; *s; s++) {
+			if (*s == Inpar)
+			    pct++;
+			if (!pct)
+			    break;
+			if (pct == 1) {
+			    if (*s == Bar || *s == Inpar)
+				while (iblank(s[1]))
+				    chuck(s+1);
+			    if (*s == Bar || *s == Outpar)
+				while (iblank(s[-1]) &&
+				       (s < str + 1 || s[-2] != Meta))
+				    chuck(--s);
+			}
+			if (*s == Outpar)
+			    pct--;
+		    }
+		    if (*s || pct || s == str)
+			YYERRORV(oecused);
+		    /* Simplify pattern by removing surrounding (...) */
+		    sl = strlen(str);
+		    DPUTS(*str != Inpar || str[sl - 1] != Outpar,
+			  "BUG: strange case pattern");
+		    str[sl - 1] = '\0';
+		    chuck(str);
+		    ecstr(str);
+		    ecadd(ecnpats++);
+		    nalts++;
+		    break;
+		}
 		YYERRORV(oecused);
+	    }
 
 	    zshlex();
 	    if (tok != STRING)
@@ -1208,7 +1283,7 @@ par_case(int *cmplx)
 	    break;
 	if (tok != DSEMI && tok != SEMIAMP && tok != SEMIBAR)
 	    YYERRORV(oecused);
-	incasepat = 2;
+	incasepat = 1;
 	incmdpos = 0;
 	zshlex();
     }
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
index 41fb48688..50058e25d 100644
--- a/Test/A01grammar.ztst
+++ b/Test/A01grammar.ztst
@@ -614,7 +614,8 @@
 >mytrue
 >END
 
-  fn() {
+  (emulate sh -c '
+   fn() {
     case $1 in
       ( one | two | three )
       print Matched $1
@@ -627,6 +628,7 @@
       ;;
     esac
   }
+  '
   which fn
   fn one
   fn two
@@ -635,8 +637,8 @@
   fn five
   fn six
   fn abecedinarian
-  fn xylophone
-0: case word handling
+  fn xylophone)
+0: case word handling in sh emulation (SH_GLOB parentheses)
 >fn () {
 >	case $1 in
 >		(one | two | three) print Matched $1 ;;
@@ -665,3 +667,31 @@
 0: case patterns within words
 >1 OK
 >2 OK
+
+  case horrible in
+    ([a-m])(|[n-z])rr(|ib(um|le|ah)))
+    print It worked
+    ;;
+  esac
+  case "a string with separate words" in
+    (*with separate*))
+    print That worked, too
+    ;;
+  esac
+0:Unbalanced parentheses and spaces with zsh pattern
+>It worked
+>That worked, too
+
+  case horrible in
+    (([a-m])(|[n-z])rr(|ib(um|le|ah)))
+    print It worked
+    ;;
+  esac
+  case "a string with separate words" in
+    (*with separate*)
+    print That worked, too
+    ;;
+  esac
+0:Balanced parentheses and spaces with zsh pattern
+>It worked
+>That worked, too