summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/expn.yo3
-rw-r--r--Src/glob.c65
3 files changed, 66 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 8dc686dec..39a4ae592 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2001-07-06  Peter Stephenson  <pws@pwstephenson.fsnet.co.uk>
+
+	* 15266: Src/glob.c, Doc/Zsh/expn.yo: ${(S)...%%...} matches
+	were wrong; try desperately to explain that in ${(SI:...:)...%%...}
+	and ${(SI:...:)...%...} indices count matches finishing
+	progressively earlier in the string.
+
 2001-07-05  Peter Stephenson  <pws@csr.com>
 
 	* 15264: Doc/Zsh/grammar.yo: improve description of use of
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 562e9bef2..d75450f7e 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -793,7 +793,8 @@ flag, or with tt(${)...tt(/)...tt(}) (only the var(expr)th match is
 substituted) or tt(${)...tt(//)...tt(}) (all matches from the
 var(expr)th on are substituted).  The var(expr)th match is counted
 such that there is either one or zero matches from each starting
-position in the string, although for global substitution matches
+position in the string when scanning forwards, or to each finishing
+position when scanning backwards, although for global substitution matches
 overlapping previous replacements are ignored.
 )
 item(tt(M))(
diff --git a/Src/glob.c b/Src/glob.c
index 5be79329b..12e911d69 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -2068,7 +2068,7 @@ set_pat_end(Patprog p, char null_me)
 static int
 igetmatch(char **sp, Patprog p, int fl, int n, char *replstr)
 {
-    char *s = *sp, *t, sav;
+    char *s = *sp, *t, sav, *furthestend, *longeststart, *lastend;
     int i, l = strlen(*sp), ml = ztrlen(*sp), matched = 1;
 
     repllist = NULL;
@@ -2243,19 +2243,17 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr)
 		*sp = get_match_ret(*sp, l, l, fl, replstr);
 		patoffset = 0;
 		return 1;
-	    } /* fall through */
-	case (SUB_END|SUB_LONG|SUB_SUBSTR):
-	    /* Longest/shortest at end, matching substrings.       */
+	    }
 	    patoffset--;
 	    for (t = s + l - 1; t >= s; t--, patoffset--) {
 		if (t > s && t[-1] == Meta)
 		    t--;
 		set_pat_start(p, t-s);
 		if (pattry(p, t) && patinput > t && !--n) {
-		    /* Found the longest match */
-		    char *mpos = patinput;
-		    if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
-			char *ptr;
+		    /* Found a match from this point */
+		    char *mpos = patinput, *ptr;
+		    if (!(p->flags & PAT_PURES)) {
+			/* See if there's a shorter to anywhere */
 			for (ptr = t; ptr < mpos; METAINC(ptr)) {
 			    sav = *ptr;
 			    set_pat_end(p, sav);
@@ -2282,6 +2280,57 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr)
 	    }
 	    patoffset = 0;
 	    break;
+
+	case (SUB_END|SUB_LONG|SUB_SUBSTR):
+	    /*
+	     * Longest at end, matching substrings.  Scan up from
+	     * start, remembering the furthest we got.  The
+	     * longest string to reach that point wins.
+	     */
+	    furthestend = longeststart = lastend = NULL;
+	    sav = '\0';
+	    while (n) {
+		int l2 = strlen(s);
+		for (i = 0, t = s; i <= l2; i++, t++, patoffset++) {
+		    set_pat_start(p, t-s);
+		    if (pattry(p, t)) {
+			if (!furthestend || 
+			    patinput - t > furthestend - longeststart) {
+			    furthestend = patinput;
+			    longeststart = t;
+			}
+		    }
+		    if (*t == Meta)
+			t++, i++;
+		}
+		if (furthestend) {
+		    if (lastend) {
+			*lastend = sav;
+			lastend = NULL;
+		    }
+		    if (--n && furthestend > s) {
+			lastend = (furthestend > s+1 && furthestend[-2]
+				   == Meta) ? furthestend-2 : furthestend-1;
+			sav = *lastend;
+			set_pat_end(p, sav);
+			*lastend = '\0';
+			furthestend = NULL;
+			patoffset = 0;
+			continue;
+		    }
+		}
+		break;
+	    }
+	    if (lastend)
+		*lastend = sav;
+	    if (!n) {
+		*sp = get_match_ret(*sp, longeststart-s, furthestend-s, fl,
+				    replstr);
+		patoffset = 0;
+		return 1;
+	    }
+	    patoffset = 0;
+	    break;
 	}
     }