about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/comp.h19
-rw-r--r--Src/Zle/complist.c29
-rw-r--r--Src/Zle/zle_tricky.c1051
3 files changed, 759 insertions, 340 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 091b04476..8b31d97fa 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -197,6 +197,8 @@ struct cmgroup {
     LinkList lfmatches;		/* list of matches without fignore */
     LinkList lallccs;		/* list of used compctls */
     int num;			/* number of this group */
+    int nbrbeg;			/* number of opened braces */
+    int nbrend;			/* number of closed braces */
     /* The following is collected/used during listing. */
     int dcount;			/* number of matches to list in columns */
     int cols;			/* number of columns */
@@ -229,8 +231,8 @@ struct cmatch {
     char *disp;			/* string to display (compadd -d) */
     char autoq;			/* closing quote to add automatically */
     int flags;			/* see CMF_* below */
-    int brpl;			/* the place where to put the brace prefix */
-    int brsl;			/* ...and the suffix */
+    int *brpl;			/* places where to put the brace prefixes */
+    int *brsl;			/* ...and the suffixes */
     char *rems;			/* when to remove the suffix */
     char *remf;			/* shell function to call for suffix-removal */
     int qipl;			/* length of quote-prefix */
@@ -356,6 +358,19 @@ struct cldata {
 typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int,
 			    char *, struct stat *);
 
+/* Information about one brace run. */
+
+typedef struct brinfo *Brinfo;
+
+struct brinfo {
+    Brinfo next;		/* next in list */
+    Brinfo prev;		/* previous (only for closing braces) */
+    char *str;			/* the string to insert */
+    int pos;			/* original position */
+    int qpos;			/* original position, with quoting */
+    int curpos;			/* position for current match */
+};
+
 /* Data given to hooks. */
 
 typedef struct chdata *Chdata;
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index b9b0ca79d..731c0ba72 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -523,8 +523,9 @@ typedef struct menustack *Menustack;
 struct menustack {
     Menustack prev;
     char *line;
-    char *brbeg;
-    char *brend;
+    Brinfo brbeg;
+    Brinfo brend;
+    int nbrbeg, nbrend;
     int cs, acc, nmatches;
     struct menuinfo info;
     Cmgroup amatches, pmatches, lastmatches, lastlmatches;
@@ -605,8 +606,10 @@ domenuselect(Hookdef dummy, Chdata dat)
 		s->lastmatches = lastmatches;
 		s->lastlmatches = lastlmatches;
 		s->acc = menuacc;
-		s->brbeg = dupstring(brbeg);
-		s->brend = dupstring(brend);
+		s->brbeg = dupbrinfo(brbeg, NULL);
+		s->brend = dupbrinfo(brend, NULL);
+		s->nbrbeg = nbrbeg;
+		s->nbrend = nbrend;
 		s->nmatches = nmatches;
 		menucmp = menuacc = 0;
 		fixsuffix();
@@ -638,8 +641,10 @@ domenuselect(Hookdef dummy, Chdata dat)
 		s->amatches = s->pmatches =
 		    s->lastmatches = s->lastlmatches = NULL;
 		s->acc = menuacc;
-		s->brbeg = dupstring(brbeg);
-		s->brend = dupstring(brend);
+		s->brbeg = dupbrinfo(brbeg, NULL);
+		s->brend = dupbrinfo(brend, NULL);
+		s->nbrbeg = nbrbeg;
+		s->nbrend = nbrend;
 		s->nmatches = nmatches;
 		acceptlast();
 		do_menucmp(0);
@@ -670,10 +675,14 @@ domenuselect(Hookdef dummy, Chdata dat)
 		    nmatches = u->nmatches;
 		    hasoldlist = 1;
 		}
-		zsfree(brbeg);
-		zsfree(brend);
-		brbeg = ztrdup(u->brbeg);
-		brend = ztrdup(u->brend);
+		PERMALLOC {
+		    freebrinfo(brbeg);
+		    freebrinfo(brend);
+		    brbeg = dupbrinfo(u->brbeg, &lastbrbeg);
+		    brend = dupbrinfo(u->brend, &lastbrend);
+		    nbrbeg = u->nbrbeg;
+		    nbrend = u->nbrend;
+		} LASTALLOC;
 		u = u->prev;
 		clearlist = 1;
 		setwish = 1;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 85e592045..bd96486a7 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -112,24 +112,21 @@ static int movetoend;
 /**/
 int menucmp, menuacc;
 
-/**/
-static char *lastprebr, *lastpostbr;
-
 /* Information about menucompletion. */
 
 /**/
 struct menuinfo minfo;
 
-/* This is for completion inside a brace expansion. brbeg and brend hold  *
- * strings that were temporarily removed from the string to complete.     *
- * brpl and brsl, hold the offset of these strings.                 *
- * brpcs and brscs hold the positions of the re-inserted string in the    *
- * line.                                                                  */
+/* Lists of brace-infos before/after cursor (first and last for each). */
 
 /**/
-char *brbeg = NULL, *brend = NULL;
+Brinfo brbeg, lastbrbeg, brend, lastbrend;
 
-static int brpl, brsl, brpcs, brscs, qbrpl, qbrsl, hasunqu;
+/**/
+int nbrbeg, nbrend;
+
+static char *lastprebr, *lastpostbr;
+static int hasunqu, useqbr, brpcs, brscs;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
@@ -606,18 +603,18 @@ acceptlast(void)
     }
     menuacc++;
 
-    if (brbeg && *brbeg) {
+    if (brbeg) {
 	int l;
 
 	iremovesuffix(',', 1);
 
 	l = (brscs >= 0 ? brscs : cs) - brpcs;
 
-	zsfree(brbeg);
-	brbeg = (char *) zalloc(l + 2);
-	memcpy(brbeg, line + brpcs, l);
-	brbeg[l] = ',';
-	brbeg[l + 1] = '\0';
+	zsfree(lastbrbeg->str);
+	lastbrbeg->str = (char *) zalloc(l + 2);
+	memcpy(lastbrbeg->str, line + brpcs, l);
+	lastbrbeg->str[l] = ',';
+	lastbrbeg->str[l + 1] = '\0';
     } else {
 	int l;
 
@@ -1289,6 +1286,49 @@ unmetafy_line(void)
     (void) unmetafy((char *) line, &ll);
 }
 
+/* Free a brinfo list. */
+
+/**/
+void
+freebrinfo(Brinfo p)
+{
+    Brinfo n;
+
+    while (p) {
+	n = p->next;
+	zsfree(p->str);
+	zfree(p, sizeof(*p));
+
+	p = n;
+    }
+}
+
+/* Duplicate a brinfo list. */
+
+/**/
+Brinfo
+dupbrinfo(Brinfo p, Brinfo *last)
+{
+    Brinfo ret = NULL, *q = &ret, n = NULL;
+
+    while (p) {
+	n = *q = (Brinfo) alloc(sizeof(*n));
+	q = &(n->next);
+
+	n->next = NULL;
+	n->str = dupstring(p->str);
+	n->pos = p->pos;
+	n->qpos = p->qpos;
+	n->curpos = p->curpos;
+
+	p = p->next;
+    }
+    if (last)
+	*last = n;
+
+    return ret;
+}
+
 /* Lasciate ogni speranza.                                                  *
  * This function is a nightmare.  It works, but I'm sure that nobody really *
  * understands why.  The problem is: to make it cleaner we would need       *
@@ -1301,9 +1341,14 @@ get_comp_string(void)
     int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
-    zsfree(brbeg);
-    zsfree(brend);
-    brbeg = brend = NULL;
+    freebrinfo(brbeg);
+    freebrinfo(brend);
+    brbeg = lastbrbeg = brend = lastbrend = NULL;
+    nbrbeg = nbrend = 0;
+    zsfree(lastprebr);
+    zsfree(lastpostbr);
+    lastprebr = lastpostbr = NULL;
+
     /* This global flag is used to signal the lexer code if it should *
      * expand aliases or not.                                         */
     noaliases = isset(COMPLETEALIASES);
@@ -1704,16 +1749,14 @@ get_comp_string(void)
 	    }
 
 	if (!isset(IGNOREBRACES)) {
-	    /* Try and deal with foo{xxx etc.; only simple cases
-	     * (only one inbrace, completion after inbrace and before outbrace
-	     * if present).
-	     */
-	    int myoffs = isset(COMPLETEINWORD) ? offs : strlen(s);
-	    tt = NULL;
-	    /* First check the conditions mentioned above
-	     * and locate opening brace
-	     */
-	    for (i = 0, p = s; *p; p++, i++) {
+	    /* Try and deal with foo{xxx etc. */
+	    char *curs = s + (isset(COMPLETEINWORD) ? offs : strlen(s));
+	    char *predup = dupstring(s), *dp = predup;
+	    char *bbeg = NULL, *bend = NULL, *dbeg;
+	    char *lastp = NULL, *firsts = NULL;
+	    int cant = 0, begi, boffs = offs, hascom = 0;
+
+	    for (i = 0, p = s; *p; p++, dp++, i++) {
 		/* careful, ${... is not a brace expansion...
 		 * we try to get braces after a parameter expansion right,
 		 * but this may fail sometimes. sorry.
@@ -1721,6 +1764,7 @@ get_comp_string(void)
 		if (*p == String || *p == Qstring) {
 		    if (p[1] == Inbrace || p[1] == Inpar || p[1] == Inbrack) {
 			char *tp = p + 1;
+
 			if (skipparens(*tp, (*tp == Inbrace ? Outbrace :
 					     (*tp == Inpar ? Outpar : Outbrack)),
 				       &tp)) {
@@ -1729,6 +1773,7 @@ get_comp_string(void)
 			}
 			i += tp - p;
 			p = tp;
+			dp += tp - p;
 		    } else {
 			char *tp = p + 1;
 
@@ -1754,80 +1799,186 @@ get_comp_string(void)
 				break;
 			    }
 			    if (*tp == Inbrace) {
-				tt = NULL;
+				cant = 1;
 				break;
 			    }
 			    tp--;
 			    i += tp - p;
 			    p = tp;
+			    dp += tp - p;
 			}
 		    }
-		} else if (*p == Inbrace) {
-		    if (tt) {
-			/* too many inbraces */
-			tt = NULL;
+		} else if (p < curs) {
+		    if (*p == Outbrace) {
+			cant = 1;
 			break;
 		    }
-		    tt = p;
-		} else if (*p == Outbrace && i < myoffs) {
-		    /* outbrace is before cursor pos, so nothing to complete */
-		    tt = NULL;
-		    break;
+		    if (*p == Inbrace) {
+			if (bbeg) {
+			    Brinfo new;
+			    int len = bend - bbeg;
+
+			    new = (Brinfo) zalloc(sizeof(*new));
+			    nbrbeg++;
+
+			    new->next = NULL;
+			    if (lastbrbeg)
+				lastbrbeg->next = new;
+			    else
+				brbeg = new;
+			    lastbrbeg = new;
+
+			    new->next = NULL;
+			    PERMALLOC {
+				new->str = dupstrpfx(bbeg, len);
+				untokenize(new->str);
+			    } LASTALLOC;
+			    new->pos = begi;
+			    *dbeg = '\0';
+			    new->qpos = strlen(quotename(predup, NULL));
+			    *dbeg = '{';
+			    i -= len;
+			    boffs -= len;
+			    strcpy(dbeg, dbeg + len);
+			    dp -= len;
+			}
+			bbeg = lastp = p;
+			dbeg = dp;
+			bend = p + 1;
+			begi = i;
+		    } else if (*p == Comma && bbeg) {
+			bend = p + 1;
+			hascom = 1;
+		    }
+		} else {
+		    if (*p == Inbrace) {
+			cant = 1;
+			break;
+		    }
+		    if (p == curs) {
+			if (bbeg) {
+			    Brinfo new;
+			    int len = bend - bbeg;
+
+			    new = (Brinfo) zalloc(sizeof(*new));
+			    nbrbeg++;
+
+			    new->next = NULL;
+			    if (lastbrbeg)
+				lastbrbeg->next = new;
+			    else
+				brbeg = new;
+			    lastbrbeg = new;
+
+			    PERMALLOC {
+				new->str = dupstrpfx(bbeg, len);
+				untokenize(new->str);
+			    } LASTALLOC;
+			    new->pos = begi;
+			    *dbeg = '\0';
+			    new->qpos = strlen(quotename(predup, NULL));
+			    *dbeg = '{';
+			    i -= len;
+			    boffs -= len;
+			    strcpy(dbeg, dbeg + len);
+			    dp -= len;
+			}
+			bbeg = NULL;
+		    }
+		    if (*p == Comma) {
+			if (!bbeg)
+			    bbeg = p;
+			hascom = 1;
+		    } else if (*p == Outbrace) {
+			Brinfo new;
+			int len;
+
+			if (!bbeg)
+			    bbeg = p;
+			len = p + 1 - bbeg;
+			if (!firsts)
+			    firsts = p + 1;
+			
+			new = (Brinfo) zalloc(sizeof(*new));
+			nbrend++;
+
+			if (!lastbrend)
+			    lastbrend = new;
+
+			new->next = brend;
+			brend = new;
+
+			PERMALLOC {
+			    new->str = dupstrpfx(bbeg, len);
+			    untokenize(new->str);
+			} LASTALLOC;
+			new->pos = dp - predup - len + 1;
+			new->qpos = len;
+			bbeg = NULL;
+		    }
 		}
 	    }
+	    if (cant) {
+		freebrinfo(brbeg);
+		freebrinfo(brend);
+		brbeg = lastbrbeg = brend = lastbrend = NULL;
+		nbrbeg = nbrend = 0;
+	    } else {
+		if (p == curs && bbeg) {
+		    Brinfo new;
+		    int len = bend - bbeg;
+
+		    new = (Brinfo) zalloc(sizeof(*new));
+		    nbrbeg++;
 
-	    if (tt && tt < s + myoffs) {
-		/* Braces are go:  delete opening brace */
-		char *com = NULL, *tmp;
-		int pl, sl;
-
-		brbeg = dupstring(tt);
-		brpl = tt - s;
-		tmp = dupstrpfx(s, tt - s);
-		qbrpl = strlen(quotename(tmp, NULL));
-		pl = 1;
-		sl = 0;
-		chuck(tt);
-		offs--;
-		myoffs--;
-
-		/* Look for text up to comma before cursor and delete it */
-		for (i = tt - s, p = tt; *p && i < myoffs; p++, i++)
-		    if (*p == Comma)
-			com = p;
-		if (com) {
-		    i = com - tt + 1;
-		    offs -= i;
-		    myoffs -= i;
-		    strcpy(tt, tt + i);
-		    pl += i;
+		    new->next = NULL;
+		    if (lastbrbeg)
+			lastbrbeg->next = new;
+		    else
+			brbeg = new;
+		    lastbrbeg = new;
+
+		    PERMALLOC {
+			new->str = dupstrpfx(bbeg, len);
+			untokenize(new->str);
+		    } LASTALLOC;
+		    new->pos = begi;
+		    *dbeg = '\0';
+		    new->qpos = strlen(quotename(predup, NULL));
+		    *dbeg = '{';
+		    boffs -= len;
+		    strcpy(dbeg, dbeg + len);
+		}
+		if (brend) {
+		    Brinfo bp, prev = NULL;
+		    int p, l;
+
+		    for (bp = brend; bp; bp = bp->next) {
+			bp->prev = prev;
+			prev = bp;
+			p = bp->pos;
+			l = bp->qpos;
+			bp->pos = strlen(predup + p + l);
+			bp->qpos = strlen(quotename(predup + p + l, NULL));
+			strcpy(predup + p, predup + p + l);
+		    }
 		}
-		brbeg[pl] = '\0';
+		if (hascom) {
+		    if (lastp) {
+			char sav = *lastp;
 
-		/* Look for text between subsequent comma
-		 * and closing brace or end of string and delete it
-		 */
-		for (p = s + myoffs; *p && *p != Outbrace && *p != Comma; p++);
-		if (*p == Comma || *p == Outbrace) {
-		    brend = dupstring(p);
-		    sl = 1;
-		    while (*p && *p != Outbrace) {
-			chuck(p); sl++;
+			*lastp = '\0';
+			untokenize(lastprebr = ztrdup(s));
+			*lastp = sav;
 		    }
-		    if (*p == Outbrace)
-			chuck(p);
-		    brsl = strlen(s) - (p - s);
-		    brend[sl] = '\0';
-		    qbrsl = strlen(quotename(p, NULL));
+		    if ((lastpostbr = ztrdup(firsts)))
+			untokenize(lastpostbr);
 		}
-		/* we are still waiting for an outbrace and maybe commas */
-		if (brbeg)
-		    untokenize(brbeg = ztrdup(brbeg));
-		if (brend)
-		    untokenize(brend = ztrdup(brend));
+		zsfree(s);
+		s = ztrdup(predup);
+		offs = boffs;
 	    }
 	}
-
     } LASTALLOC;
     lexrestore();
 
@@ -2043,9 +2194,9 @@ free_cline(Cline l)
 /* Copy a cline list. */
 
 static Cline
-cp_cline(Cline l)
+cp_cline(Cline l, int deep)
 {
-    Cline r = NULL, *p = &r, t;
+    Cline r = NULL, *p = &r, t, lp = NULL;
 
     while (l) {
 	if ((t = freecl))
@@ -2053,7 +2204,13 @@ cp_cline(Cline l)
 	else
 	    t = (Cline) zhalloc(sizeof(*t));
 	memcpy(t, l, sizeof(*t));
-	*p = t;
+	if (deep) {
+	    if (t->prefix)
+		t->prefix = cp_cline(t->prefix, 0);
+	    if (t->suffix)
+		t->suffix = cp_cline(t->suffix, 0);
+	}
+	*p = lp = t;
 	p = &(t->next);
 	l = l->next;
     }
@@ -2408,18 +2565,21 @@ add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
  * is used (and return the length used). */
 
 static int
-match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
+match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
+	  int sfx, int test, int part)
 {
     int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
-    int il = 0, iw = 0, t, ind, add, bc = (bp ? *bp : 0), he = 0;
+    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc;
     VARARR(unsigned char, ea, ll + 1);
     char *ow;
     Cmlist ms;
     Cmatcher mp, lm = NULL;
+    Brinfo bp = NULL;
 
-    if (!test)
+    if (!test) {
 	start_match();
-
+	bp = *bpp;
+    }
     /* Adjust the pointers and get the values for subscripting and
      * incrementing. */
 
@@ -2436,9 +2596,9 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
 
     /* If the brace is at the beginning, we have to treat it now. */
 
-    if (!test && !bc && bp) {
-	*bp = 0;
-	bp = NULL;
+    if (!test && bp && bc >= bp->pos) {
+	bp->curpos = bc;
+	bp = bp->next;
     }
     while (ll && lw) {
 	/* First try the matchers. */
@@ -2514,11 +2674,11 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
 				savw = tp[-zoff];
 				tp[-zoff] = '\0';
 				t = match_str(l - ll, w - lw,
-					      NULL, NULL, 1, 2, part);
+					      NULL, 0, NULL, 1, 2, part);
 				tp[-zoff] = savw;
 			    } else
 				t = match_str(l + llen + moff, tp + moff,
-					      NULL, NULL, 0, 1, part);
+					      NULL, 0, NULL, 0, 1, part);
 			    if (t || !both)
 				break;
 			}
@@ -2592,12 +2752,14 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
 		    }
 		    ll -= llen; il += llen;
 		    lw -= alen; iw += alen;
-		    bc -= llen;
+		    bc += llen;
 
-		    if (!test && bp && bc <= 0) {
-			*bp = matchbufadded + bc;
-			bp = NULL;
-		    }
+		    if (!test)
+			while (bp &&
+			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+			    bp->curpos = matchbufadded + bpc - bc + obc;
+			    bp = bp->next;
+			}
 		    ow = w;
 
 		    if (!llen && !alen) {
@@ -2689,12 +2851,14 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
 		    }
 		    il += mp->llen; iw += mp->wlen;
 		    ll -= mp->llen; lw -= mp->wlen;
-		    bc -= mp->llen;
+		    bc += mp->llen;
 
-		    if (!test && bp && bc <= 0) {
-			*bp = matchbufadded + bc;
-			bp = NULL;
-		    }
+		    if (!test)
+			while (bp &&
+			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+			    bp->curpos = matchbufadded + bpc - bc + obc;
+			    bp = bp->next;
+			}
 		    ow = w;
 		    lm = NULL;
 		    he = 0;
@@ -2711,11 +2875,12 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
 	    l += add; w += add;
 	    il++; iw++;
 	    ll--; lw--;
-	    bc--;
-	    if (!test && bc <= 0 && bp) {
-		*bp = matchbufadded + (sfx ? (ow - w) : (w - ow));
-		bp = NULL;
-	    }
+	    bc++;
+	    if (!test)
+		while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
+		    bp->curpos = matchbufadded + (sfx ? (ow - w) : (w - ow)) + obc;
+		    bp = bp->next;
+		}
 	    lm = NULL;
 	    he = 0;
 	} else {
@@ -2732,12 +2897,9 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
     if (test)
 	return (part || !ll);
 
-    if (part)
-	return il;
-
     /* In top-level calls, if ll is non-zero (unmatched portion in l),
      * we have to free the collected clines. */
-    if (ll) {
+    if (!part && ll) {
 	abort_match();
 
 	return -1;
@@ -2770,7 +2932,8 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test, int part)
     }
     /* Finally, return the number of matched characters. */
 
-    return iw;
+    *bpp = bp;
+    return (part ? il : iw);
 }
 
 /* Wrapper for match_str(), only for a certain length and only doing
@@ -2784,7 +2947,7 @@ match_parts(char *l, char *w, int n, int part)
     int ret;
 
     l[n] = w[n] = '\0';
-    ret = match_str(l, w, NULL, NULL, 0, 1, part);
+    ret = match_str(l, w, NULL, 0, NULL, 0, 1, part);
     l[n] = lsav;
     w[n] = wsav;
 
@@ -2802,8 +2965,8 @@ match_parts(char *l, char *w, int n, int part)
  * and the suffix don't match the word w. */
 
 static char *
-comp_match(char *pfx, char *sfx, char *w, Patprog cp,
-	   Cline *clp, int qu, int *bpl, int *bsl, int *exact)
+comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
+	   Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
 {
     char *r = NULL;
 
@@ -2824,8 +2987,6 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp,
 	wl = strlen(w);
 	*clp = bld_parts(w, wl, wl, NULL);
 	*exact = 0;
-	*bpl = (qu ? qbrpl : brpl);
-	*bsl = (qu ? qbrsl : brsl);
     } else {
 	Cline pli, plil;
 	int mpl, rpl, wl;
@@ -2838,8 +2999,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp,
 
 	/* Always try to match the prefix. */
 
-	*bpl = (qu ? qbrpl : brpl);
-	if ((mpl = match_str(pfx, w, bpl, &rpl, 0, 0, 0)) < 0)
+	useqbr = qu;
+	if ((mpl = match_str(pfx, w, bpl, bcp, &rpl, 0, 0, 0)) < 0)
 	    return NULL;
 
 	if (sfx && *sfx) {
@@ -2863,8 +3024,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp,
 	    plil = matchlastpart;
 
 	    /* The try to match the suffix. */
-	    *bsl = (qu ? qbrsl : brsl);
-	    if ((msl = match_str(sfx, w + mpl, bsl, &rsl, 1, 0, 0)) < 0) {
+
+	    if ((msl = match_str(sfx, w + mpl, bsl, bcs, &rsl, 1, 0, 0)) < 0) {
 		free_cline(pli);
 
 		return NULL;
@@ -3552,8 +3713,8 @@ sub_join(Cline a, Cline b, Cline e, int anew)
 	ca = a->prefix;
 
 	while (n != op) {
-	    e->prefix = cp_cline(n);
-	    a->prefix = cp_cline(ca);
+	    e->prefix = cp_cline(n, 0);
+	    a->prefix = cp_cline(ca, 0);
 
 	    if (anew) {
 		join_psfx(e, a, NULL, NULL, 0);
@@ -3752,8 +3913,10 @@ join_clines(Cline o, Cline n)
 static Cmatch
 add_match_data(int alt, char *str, Cline line,
 	       char *ipre, char *ripre, char *isuf,
-	       char *pre, char *prpre, char *ppre, char *psuf, char *suf,
-	       int bpl, int bsl, int flags, int exact)
+	       char *pre, char *prpre,
+	       char *ppre, Cline pline,
+	       char *psuf, Cline sline,
+	       char *suf, int flags, int exact)
 {
     Cmatch cm;
     Aminfo ai = (alt ? fainfo : ainfo);
@@ -3765,10 +3928,14 @@ add_match_data(int alt, char *str, Cline line,
     DPUTS(!line, "BUG: add_match_data() without cline");
 
     cline_matched(line);
+    if (pline)
+	cline_matched(pline);
+    if (sline)
+	cline_matched(sline);
 
     /* If there is a path suffix, we build a cline list for it and
      * append it to the list for the match itself. */
-    if (psuf)
+    if (!sline && psuf)
 	salen = (psl = strlen(psuf));
     if (isuf)
 	salen += (isl = strlen(isuf));
@@ -3778,6 +3945,7 @@ add_match_data(int alt, char *str, Cline line,
     if (salen) {
 	char *asuf = (char *) zhalloc(salen);
 	Cline pp, p, s, sl = NULL;
+	
 
 	if (psl)
 	    memcpy(asuf, psuf, psl);
@@ -3791,6 +3959,15 @@ add_match_data(int alt, char *str, Cline line,
 	if (salen > qisl) {
 	    s = bld_parts(asuf, salen - qisl, salen - qisl, &sl);
 
+	    if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = s;
+		s = sline;
+	    }
 	    if (!(p->flags & (CLF_SUF | CLF_MID)) &&
 		!p->llen && !p->wlen && !p->olen) {
 		if (p->prefix) {
@@ -3818,9 +3995,22 @@ add_match_data(int alt, char *str, Cline line,
 	    qsl->prefix = NULL;
 	    if (sl)
 		sl->next = qsl;
-	    else
+	    else if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = qsl;
+		p->next = sline;
+	    } else
 		p->next = qsl;
 	}
+    } else if (sline) {
+	Cline p;
+
+	for (p = line; p->next; p = p->next);
+	p->next = cp_cline(sline, 1);
     }
     /* The prefix is handled differently because the completion code
      * is much more eager to insert the -P prefix than it is to insert
@@ -3831,15 +4021,25 @@ add_match_data(int alt, char *str, Cline line,
 	palen += (ipl = strlen(ipre));
     if (pre)
 	palen += (pl = strlen(pre));
-    if (ppre)
+    if (!pline && ppre)
 	palen += (ppl = strlen(ppre));
 
     if (pl) {
-	if (ppl) {
-	    Cline lp, p = bld_parts(ppre, ppl, ppl, &lp);
+	if (ppl || pline) {
+	    Cline lp, p;
+
+	    if (pline)
+		for (p = cp_cline(pline, 1), lp = p; lp->next; lp = lp->next);
+	    else
+		p = bld_parts(ppre, ppl, ppl, &lp);
+
+	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
+		!p->llen && !p->wlen && !p->olen) {
+		Cline lpp;
+
+		for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
 
-	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID))) {
-		lp->prefix->next = line->prefix;
+		lpp->next = line->prefix;
 		line->prefix = lp->prefix;
 		lp->prefix = NULL;
 
@@ -3876,22 +4076,35 @@ add_match_data(int alt, char *str, Cline line,
 	    lp->next = line;
 	    line = p;
 	}
-    } else if (palen) {
-	char *apre = (char *) zhalloc(palen);
+    } else if (palen || pline) {
 	Cline p, lp;
 
-	if (qipl)
-	    memcpy(apre, qipre, qipl);
-	if (ipl)
-	    memcpy(apre + qipl, ipre, ipl);
-	if (pl)
-	    memcpy(apre + qipl + ipl, pre, pl);
-	if (ppl)
-	    memcpy(apre + qipl + ipl + pl, ppre, ppl);
-
-	p = bld_parts(apre, palen, palen, &lp);
-	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID))) {
-	    lp->prefix->next = line->prefix;
+	if (palen) {
+	    char *apre = (char *) zhalloc(palen);
+
+	    if (qipl)
+		memcpy(apre, qipre, qipl);
+	    if (ipl)
+		memcpy(apre + qipl, ipre, ipl);
+	    if (pl)
+		memcpy(apre + qipl + ipl, pre, pl);
+	    if (ppl)
+		memcpy(apre + qipl + ipl + pl, ppre, ppl);
+
+	    p = bld_parts(apre, palen, palen, &lp);
+
+	    if (pline)
+		for (lp->next = cp_cline(pline, 1); lp->next; lp = lp->next);
+	} else
+	    for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next);
+
+	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
+	    !p->llen && !p->wlen && !p->olen) {
+	    Cline lpp;
+
+	    for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
+
+	    lpp->next = line->prefix;
 	    line->prefix = lp->prefix;
 	    lp->prefix = NULL;
 
@@ -3910,12 +4123,6 @@ add_match_data(int alt, char *str, Cline line,
 	    line = p;
 	}
     }
-    /* Then build the unambiguous cline list. */
-    ai->line = join_clines(ai->line, line);
-
-    mnum++;
-    ai->count++;
-    
     /* Allocate and fill the match structure. */
     cm = (Cmatch) zhalloc(sizeof(struct cmatch));
     cm->str = str;
@@ -3934,12 +4141,40 @@ add_match_data(int alt, char *str, Cline line,
     cm->pre = pre;
     cm->suf = suf;
     cm->flags = flags;
-    cm->brpl = bpl;
-    cm->brsl = bsl;
+    if (nbrbeg) {
+	int *p;
+	Brinfo bp;
+
+	cm->brpl = (int *) zhalloc(nbrbeg * sizeof(int));
+
+	for (p = cm->brpl, bp = brbeg; bp; p++, bp = bp->next)
+	    *p = bp->curpos;
+    } else
+	cm->brpl = NULL;
+    if (nbrend) {
+	int *p;
+	Brinfo bp;
+
+	cm->brsl = (int *) zhalloc(nbrend * sizeof(int));
+
+	for (p = cm->brsl, bp = brend; bp; p++, bp = bp->next)
+	    *p = bp->curpos;
+    } else
+	cm->brsl = NULL;
     cm->qipl = qipl;
     cm->qisl = qisl;
     cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
     cm->rems = cm->remf = cm->disp = NULL;
+
+    if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
+	return NULL;
+
+    /* Then build the unambiguous cline list. */
+    ai->line = join_clines(ai->line, line);
+
+    mnum++;
+    ai->count++;
+
     addlinknode((alt ? fmatches : matches), cm);
 
     newmatches = 1;
@@ -4025,15 +4260,21 @@ addmatches(Cadata dat, char **argv)
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
     char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre;
     char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL;
-    int lpl, lsl, pl, sl, bpl, bsl, bppl = -1, bssl = -1;
+    int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
     int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
-    Cline lc = NULL;
+    Cline lc = NULL, pline = NULL, sline = NULL;
     Cmatch cm;
     struct cmlist mst;
     Cmlist oms = mstack;
     Patprog cp = NULL;
     LinkList aparl = NULL, oparl = NULL, dparl = NULL;
+    Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
+
+    for (bp = brbeg; bp; bp = bp->next)
+	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
+    for (bp = brend; bp; bp = bp->next)
+	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
 
     if (dat->flags & CMF_ISPAR)
 	dat->flags |= parflags;
@@ -4153,31 +4394,53 @@ addmatches(Cadata dat, char **argv)
 		int ml;
 
 		s = dat->ppre ? dat->ppre : "";
-		bppl = brpl;
-		if ((ml = match_str(lpre, s, &bppl, NULL, 0, 0, 1)) >= 0)
+		if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) {
+		    if (matchsubs) {
+			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+			tmp->prefix = matchsubs;
+			if (matchlastpart)
+			    matchlastpart->next = tmp;
+			else
+			    matchparts = tmp;
+		    }
+		    pline = matchparts;
 		    lpre += ml;
-		else {
-		    bppl = -1;
+		    bcp = ml;
+		    bpadd = strlen(s) - ml;
+		} else {
 		    if (llpl <= lpl && strpfx(lpre, s))
 			lpre = "";
 		    else if (llpl > lpl && strpfx(s, lpre))
 			lpre += lpl;
 		    else
 			*argv = NULL;
+		    bcp = lpl;
 		}
 
 		s = dat->psuf ? dat->psuf : "";
-		bssl = brsl;
-		if ((ml = match_str(lsuf, s, &bssl, NULL, 0, 0, 1)) >= 0)
+		if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) {
+		    if (matchsubs) {
+			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
+
+			tmp->suffix = matchsubs;
+			if (matchlastpart)
+			    matchlastpart->next = tmp;
+			else
+			    matchparts = tmp;
+		    }
+		    sline = revert_cline(matchparts);
 		    lsuf[llsl - ml] = '\0';
-		else {
-		    bssl = -1;
+		    bcs = ml;
+		    bsadd = strlen(s) - ml;
+		} else {
 		    if (llsl <= lsl && strsfx(lsuf, s))
 			lsuf = "";
 		    else if (llsl > lsl && strsfx(s, lsuf))
 			lsuf[llsl - lsl] = '\0';
 		    else
 			*argv = NULL;
+		    bcs = lsl;
 		}
 		if (comppatmatch && *comppatmatch) {
 		    int is = (*comppatmatch == '*');
@@ -4232,14 +4495,16 @@ addmatches(Cadata dat, char **argv)
 		    dat->rems = dupstring(dat->rems);
 	    }
 	    /* Walk through the matches given. */
+	    obpl = bpl;
+	    obsl = bsl;
 	    for (; (s = *argv); argv++) {
+		bpl = obpl;
+		bsl = obsl;
 		if (disp) {
 		    if (!*++disp)
 			disp = NULL;
 		}
 		sl = strlen(s);
-		bpl = brpl;
-		bsl = brsl;
 		isalt = oisalt;
 		if ((!dat->psuf || !*(dat->psuf)) && aign) {
 		    /* Do the suffix-test. If the match has one of the
@@ -4269,22 +4534,30 @@ addmatches(Cadata dat, char **argv)
 					     (!(dat->aflags & CAF_QUOTE) ?
 					      ((dat->ppre && dat->ppre) ||
 					       !(dat->flags & CMF_FILE) ? 1 : 2) : 0),
-					     &bpl, &bsl, &isexact))) {
+					     &bpl, bcp, &bsl, bcs,
+					     &isexact))) {
 		    if (dparr && !*++dparr)
 			dparr = NULL;
 		    continue;
 		}
 		if (doadd) {
-		    cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
-					dat->isuf, dat->pre, dat->prpre,
-					dat->ppre, dat->psuf, dat->suf,
-					(bppl >= 0 ? bppl : bpl),
-					(bssl >= 0 ? bssl : bsl),
-					dat->flags, isexact);
-		    cm->rems = dat->rems;
-		    cm->remf = dat->remf;
-		    if (disp)
-			cm->disp = dupstring(*disp);
+		    Brinfo bp;
+
+		    for (bp = obpl; bp; bp = bp->next)
+			bp->curpos += bpadd;
+		    for (bp = obsl; bp; bp = bp->next)
+			bp->curpos += bsadd;
+
+		    if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
+					     dat->isuf, dat->pre, dat->prpre,
+					     dat->ppre, pline,
+					     dat->psuf, sline,
+					     dat->suf, dat->flags, isexact))) {
+			cm->rems = dat->rems;
+			cm->remf = dat->remf;
+			if (disp)
+			    cm->disp = dupstring(*disp);
+		    }
 		} else {
 		    if (dat->apar)
 			addlinknode(aparl, ms);
@@ -4351,24 +4624,30 @@ addmatches(Cadata dat, char **argv)
 static void
 addmatch(char *s, char *t)
 {
-    int isfile = 0, isalt = 0, isexact, bpl = brpl, bsl = brsl;
+    int isfile = 0, isalt = 0, isexact;
     char *ms = NULL, *tt;
     HashNode hn;
     Param pm;
     Cline lc = NULL;
-
-/*
- * addwhat: -5 is for files,
- *          -6 is for glob expansions,
- *          -8 is for executable files (e.g. command paths),
- *          -9 is for parameters
- *          -7 is for command names (from cmdnamtab)
- *          -4 is for a cdable parameter
- *          -3 is for executable command names.
- *          -2 is for anything unquoted
- *          -1 is for other file specifications
- *          (things with `~' or `=' at the beginning, ...).
- */
+    Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
+
+    for (bp = brbeg; bp; bp = bp->next)
+	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+    for (bp = brend; bp; bp = bp->next)
+	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+
+    /*
+     * addwhat: -5 is for files,
+     *          -6 is for glob expansions,
+     *          -8 is for executable files (e.g. command paths),
+     *          -9 is for parameters
+     *          -7 is for command names (from cmdnamtab)
+     *          -4 is for a cdable parameter
+     *          -3 is for executable command names.
+     *          -2 is for anything unquoted
+     *          -1 is for other file specifications
+     *          (things with `~' or `=' at the beginning, ...).
+     */
 
     /* Just to make the code cleaner */
     hn = (HashNode) t;
@@ -4376,6 +4655,13 @@ addmatch(char *s, char *t)
 
     if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
 	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
+	int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
+
+	while (bpl && bpl->curpos < ppl)
+	    bpl = bpl->next;
+	while (bsl && bsl->curpos < psl)
+	    bsl = bsl->next;
+
 	if ((addwhat == CC_FILES ||
 	     addwhat == -5) && !*psuf) {
 	    /* If this is a filename, do the fignore check. */
@@ -4390,9 +4676,9 @@ addmatch(char *s, char *t)
 	ms = ((addwhat == CC_FILES || addwhat == -6 ||
 	       addwhat == -5 || addwhat == -8) ? 
 	      comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
-			 &bpl, &bsl, &isexact) :
+			 &bpl, ppl ,&bsl, psl, &isexact) :
 	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
-			 &bpl, &bsl, &isexact));
+			 &bpl, ppl, &bsl, psl, &isexact));
 	if (!ms)
 	    return;
 
@@ -4432,23 +4718,31 @@ addmatch(char *s, char *t)
 	    p1 = qlpre; s1 = qlsuf;
 	    p2 = lpre;  s2 = lsuf;
 	}
+	bpt = bpl;
+	bst = bsl;
+
 	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
 			      (addwhat == CC_QUOTEFLAG),
-			      &bpl, &bsl, &isexact)) &&
-	    !(ms = comp_match(p2, s2, s, NULL, &lc,
-			      (addwhat == CC_QUOTEFLAG),
-			      &bpl, &bsl, &isexact)))
-	    return;
+			      &bpl, strlen(p1), &bsl, strlen(s1),
+			      &isexact))) {
+	    bpl = bpt;
+	    bsl = bst;
+	    if (!(ms = comp_match(p2, s2, s, NULL, &lc,
+				  (addwhat == CC_QUOTEFLAG),
+				  &bpl, strlen(p2), &bsl, strlen(s2),
+				  &isexact)))
+		return;
+	}
     }
     if (!ms)
 	return;
     add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
 		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
 		   prpre, 
-		   (isfile ? lppre : NULL),
-		   (isfile ? lpsuf : NULL),
+		   (isfile ? lppre : NULL), NULL,
+		   (isfile ? lpsuf : NULL), NULL,
 		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
-		   bpl, bsl, (mflags | isfile), isexact);
+		   (mflags | isfile), isexact);
 }
 
 #ifdef HAVE_NIS_PLUS
@@ -4794,6 +5088,10 @@ docompletion(char *s, int lst, int incmd)
 	    minfo.cur = NULL;
 	    goto compend;
 	}
+	zsfree(lastprebr);
+	zsfree(lastpostbr);
+	lastprebr = lastpostbr = NULL;
+
 	if (comppatmatch && *comppatmatch && comppatmatch != opm)
 	    haspattern = 1;
 	if (!useline && uselist) {
@@ -6512,8 +6810,13 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 			       (strlen(qipre) -
 				(*qipre == '\'' || *qipre == '\"')) : 0));
 	    line[cs] = save;
-	    if (brbeg && *brbeg)
-		strcpy(lppre + qbrpl, lppre + qbrpl + strlen(brbeg));
+	    if (brbeg) {
+		Brinfo bp;
+
+		for (bp = brbeg; bp; bp = bp->next)
+		    strcpy(lppre + bp->qpos,
+			   lppre + bp->qpos + strlen(bp->str));
+	    }
 	    if ((p = strrchr(lppre, '/'))) {
 		p[1] = '\0';
 		lppl = strlen(lppre);
@@ -6540,10 +6843,14 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    line[end] = 0;
 	    lpsuf = dupstring((char *) (line + cs));
 	    line[end] = save;
-	    if (brend && *brend) {
-		char *p = lpsuf + qbrsl - (cs - wb);
+	    if (brend) {
+		Brinfo bp;
+		char *p;
 
-		strcpy(p, p + strlen(brend));
+		for (bp = brend; bp; bp = bp->next) {
+		    p = lpsuf + (we - cs) - bp->qpos;
+		    strcpy(p, p + strlen(bp->str));
+		}
 	    }
 	    if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
 		lpsuf = psuf;
@@ -7518,7 +7825,7 @@ addexpl(void)
 
 /**/
 static Cmatch
-dupmatch(Cmatch m)
+dupmatch(Cmatch m, int nbeg, int nend)
 {
     Cmatch r;
 
@@ -7534,8 +7841,24 @@ dupmatch(Cmatch m)
     r->pre = ztrdup(m->pre);
     r->suf = ztrdup(m->suf);
     r->flags = m->flags;
-    r->brpl = m->brpl;
-    r->brsl = m->brsl;
+    if (nbeg) {
+	int *p, *q, i;
+
+	r->brpl = (int *) zalloc(nbeg * sizeof(int));
+
+	for (p = r->brpl, q = m->brpl, i = nbeg; i--; p++, q++)
+	    *p = *q;
+    } else
+	r->brpl = NULL;
+    if (nend) {
+	int *p, *q, i;
+
+	r->brsl = (int *) zalloc(nend * sizeof(int));
+
+	for (p = r->brsl, q = m->brsl, i = nend; i--; p++, q++)
+	    *p = *q;
+    } else
+	r->brsl = NULL;
     r->rems = ztrdup(m->rems);
     r->remf = ztrdup(m->remf);
     r->autoq = m->autoq;
@@ -7619,7 +7942,7 @@ permmatches(int last)
 	n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) *
 					    sizeof(Cmatch));
 	for (q = g->matches; *q; q++, p++)
-	    *p = dupmatch(*q);
+	    *p = dupmatch(*q, nbrbeg, nbrend);
 	*p = NULL;
 
 	n->lcount = g->lcount;
@@ -7654,6 +7977,8 @@ permmatches(int last)
 	g = g->next;
     }
     for (g = pmatches; g; g = g->next) {
+	g->nbrbeg = nbrbeg;
+	g->nbrend = nbrend;
 	for (rn = 1, q = g->matches; *q; q++) {
 	    (*q)->rnum = rn++;
 	    (*q)->gnum = mn++;
@@ -7722,7 +8047,7 @@ comp_list(char *v)
 
 /**/
 static void
-freematch(Cmatch m)
+freematch(Cmatch m, int nbeg, int nend)
 {
     if (!m) return;
 
@@ -7738,6 +8063,8 @@ freematch(Cmatch m)
     zsfree(m->rems);
     zsfree(m->remf);
     zsfree(m->disp);
+    zfree(m->brpl, nbeg * sizeof(int));
+    zfree(m->brsl, nend * sizeof(int));
 
     zfree(m, sizeof(m));
 }
@@ -7757,7 +8084,7 @@ freematches(Cmgroup g)
 	n = g->next;
 	
 	for (m = g->matches; *m; m++)
-	    freematch(*m);
+	    freematch(*m, g->nbrbeg, g->nbrend);
 
 	if (g->ylist)
 	    freearray(g->ylist);
@@ -7823,7 +8150,7 @@ cut_cline(Cline l)
 	cline_setlens(l, 0);
 	return l;
     }
-    e = l = cp_cline(l);
+    e = l = cp_cline(l, 0);
 
     /* First, search the last struct for which we have something on
      * the line. Anything before that is kept. */
@@ -7900,54 +8227,52 @@ cline_str(Cline l, int ins, int *csp)
 {
     Cline s;
     int ocs = cs, ncs, pcs, pm, pmax, pmm, sm, smax, smm, d, dm, mid;
-    int pl, sl, hasp, hass, ppos, spos, plen, slen, i, j, li = 0;
+    int i, j, li = 0;
+    Brinfo brp, brs;
 
     l = cut_cline(l);
 
-    pmm = smm = dm = ppos = spos = plen = slen = hasp = hass = 0;
-    pm = pmax = sm = smax = d = mid = pl = sl = -1;
+    pmm = smm = dm = 0;
+    pm = pmax = sm = smax = d = mid = -1;
 
     /* Get the information about the brace beginning and end we have
      * to re-insert. */
     if (ins) {
-	if ((hasp = (brbeg && *brbeg))) {
-	    plen = strlen(brbeg); pl = (hasunqu ? brpl : qbrpl);
+	Brinfo bp;
+	int olen = we - wb;
+
+	if ((brp = brbeg)) {
+	    for (bp = brbeg; bp; bp = bp->next) {
+		bp->curpos = (hasunqu ? bp->pos : bp->qpos);
+		olen -= strlen(bp->str);
+	    }
 	}
-	if ((hass = (brend && *brend))) {
-	    slen = strlen(brend);
-	    sl = we - wb - (hasunqu ? brsl : qbrsl) - plen - slen + 1;
+	if ((brs = lastbrend)) {
+	    for (bp = brend; bp; bp = bp->next)
+		olen -= strlen(bp->str);
+
+	    for (bp = brend; bp; bp = bp->next)
+		bp->curpos = olen - (hasunqu ? bp->pos : bp->qpos);
 	}
-	if (!pl) {
-	    inststrlen(brbeg, 1, -1);
-	    pl = -1; hasp = 0;
+	while (brp && !brp->curpos) {
+	    inststrlen(brp->str, 1, -1);
+	    brp = brp->next;
 	}
-	if (!sl) {
-	    inststrlen(brend, 1, -1);
-	    sl = -1; hass = 0;
+	while (brs && !brs->curpos) {
+	    inststrlen(brs->str, 1, -1);
+	    brs = brs->prev;
 	}
     }
     /* Walk through the top-level cline list. */
     while (l) {
-	if (pl >= 0)
-	    ppos = -1;
-	if (sl >= 0)
-	    spos = -1;
 	/* Insert the original string if no prefix. */
 	if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
+	    pcs = cs + l->olen;
 	    inststrlen(l->orig, 1, l->olen);
-	    if (ins) {
-		li += l->olen;
-		if (pl >= 0 && li >= pl) {
-		    ppos = cs - (li - pl); pl = -1;
-		}
-		if (sl >= 0 && li >= sl) {
-		    spos = cs - (li - sl) - 1; sl = -1;
-		}
-	    }
 	} else {
 	    /* Otherwise insert the prefix. */
 	    for (s = l->prefix; s; s = s->next) {
-		pcs = cs;
+		pcs = cs + s->llen;
 		if (s->flags & CLF_LINE)
 		    inststrlen(s->line, 1, s->llen);
 		else
@@ -7955,15 +8280,20 @@ cline_str(Cline l, int ins, int *csp)
 		if ((s->flags & CLF_DIFF) && (!dm || (s->flags & CLF_MATCHED))) {
 		    d = cs; dm = s->flags & CLF_MATCHED;
 		}
-		if (ins) {
-		    li += s->llen;
-		    if (pl >= 0 && li >= pl) {
-			ppos = pcs + s->llen - (li - pl); pl = -1;
-		    }
-		    if (sl >= 0 && li >= sl) {
-			spos = pcs + s->llen - (li - sl) - 1; sl = -1;
-		    }
-		}
+		li += s->llen;
+	    }
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brp && li >= brp->curpos) {
+		ocs = cs;
+		bl = strlen(brp->str);
+		cs = pcs - (li - brp->curpos);
+		inststrlen(brp->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brp = brp->next;
 	    }
 	}
 	/* Remember the position if this is the first prefix with
@@ -7973,6 +8303,19 @@ cline_str(Cline l, int ins, int *csp)
 	     ((l->flags & CLF_MATCHED) && !pmm))) {
 	    pm = cs; pmax = l->min - l->max; pmm = l->flags & CLF_MATCHED;
 	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brs && li > brs->curpos) {
+		ocs = cs;
+		bl = strlen(brs->str);
+		cs = pcs - (li - brs->curpos);
+		inststrlen(brs->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brs = brs->prev;
+	    }
+	}
 	pcs = cs;
 	/* Insert the anchor. */
 	if (l->flags & CLF_LINE)
@@ -7980,12 +8323,18 @@ cline_str(Cline l, int ins, int *csp)
 	else
 	    inststrlen(l->word, 1, l->wlen);
 	if (ins) {
+	    int ocs, bl;
+
 	    li += l->llen;
-	    if (pl >= 0 && li >= pl) {
-		ppos = pcs + l->llen - (li - pl); pl = -1;
-	    }
-	    if (sl >= 0 && li >= sl) {
-		spos = pcs + l->llen - (li - sl) - 1; sl = -1;
+
+	    while (brp && li >= brp->curpos) {
+		ocs = cs;
+		bl = strlen(brp->str);
+		cs = pcs + l->llen - (li - brp->curpos);
+		inststrlen(brp->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brp = brp->next;
 	    }
 	}
 	/* Remember the cursor position for suffixes and mids. */
@@ -7999,26 +8348,54 @@ cline_str(Cline l, int ins, int *csp)
 		sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
 	    }
 	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brs && li >= brs->curpos) {
+		ocs = cs;
+		bl = strlen(brs->str);
+		cs = pcs + l->llen - (li - brs->curpos);
+		inststrlen(brs->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brs = brs->prev;
+	    }
+	}
 	/* And now insert the suffix or the original string. */
 	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
 	    pcs = cs;
 	    inststrlen(l->orig, 1, l->olen);
 	    if (ins) {
+		int ocs, bl;
+
 		li += l->olen;
-		if (pl >= 0 && li >= pl) {
-		    ppos = pcs + l->olen - (li - pl); pl = -1;
+
+		while (brp && li >= brp->curpos) {
+		    ocs = cs;
+		    bl = strlen(brp->str);
+		    cs = pcs + l->olen - (li - brp->curpos);
+		    inststrlen(brp->str, 1, bl);
+		    cs = ocs + bl;
+		    pcs += bl;
+		    brp = brp->next;
 		}
-		if (sl >= 0 && li >= sl) {
-		    spos = pcs + l->olen - (li - sl) - 1; sl = -1;
+		while (brs && li >= brs->curpos) {
+		    ocs = cs;
+		    bl = strlen(brs->str);
+		    cs = pcs + l->olen - (li - brs->curpos);
+		    inststrlen(brs->str, 1, bl);
+		    cs = ocs + bl;
+		    pcs += bl;
+		    brs = brs->prev;
 		}
 	    }
 	} else {
-	    int hp = 0, hs = 0;
 	    Cline js = NULL;
 
 	    for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
 		if (j < 0 && (s->flags & CLF_DIFF))
 		    j = i, js = s;
+		pcs = cs;
 		if (s->flags & CLF_LINE) {
 		    inststrlen(s->line, 0, s->llen);
 		    i += s->llen; pcs = cs + s->llen;
@@ -8027,48 +8404,54 @@ cline_str(Cline l, int ins, int *csp)
 		    i += s->wlen; pcs = cs + s->wlen;
 		}
 		if (ins) {
+		    int ocs, bl;
+
 		    li += s->llen;
-		    if (pl >= 0 && li >= pl) {
-			hp = 1; ppos = pcs - (li - pl) - i; pl = -1;
+
+		    while (brp && li >= brp->curpos) {
+			ocs = cs;
+			bl = strlen(brp->str);
+			cs = pcs - (li - brp->curpos);
+			inststrlen(brp->str, 1, bl);
+			cs = ocs + bl;
+			pcs += bl;
+			brp = brp->next;
 		    }
-		    if (sl >= 0 && li >= sl) {
-			hs = 1; spos = pcs - (li - sl) - i; sl = -1;
+		    while (brs && li >= brs->curpos) {
+			ocs = cs;
+			bl = strlen(brs->str);
+			cs = pcs - (li - brs->curpos);
+			inststrlen(brs->str, 1, bl);
+			cs = ocs + bl;
+			pcs += bl;
+			brs = brs->prev;
 		    }
 		}
 	    }
-	    if (hp)
-		ppos += i;
-	    if (hs)
-		spos += i;
 	    cs += i;
 	    if (j >= 0 && (!dm || (js->flags & CLF_MATCHED))) {
 		d = cs - j; dm = js->flags & CLF_MATCHED;
 	    }
 	}
-	/* If we reached the right positions, re-insert the braces. */
-	if (ins) {
-	    if (hasp && ppos >= 0) {
-		i = cs;
-		cs = ppos;
-		inststrlen(brbeg, 1, plen);
-		cs = i + plen;
-		hasp = 0;
-	    }
-	    if (hass && spos >= 0) {
-		i = cs;
-		cs = spos;
-		inststrlen(brend, 1, slen);
-		cs = i + slen;
-		hass = 0;
-	    }
-	}
 	l = l->next;
     }
-    if (pl >= 0)
-	inststrlen(brbeg, 1, plen);
-    if (sl >= 0)
-	inststrlen(brend, 1, slen);
-
+    if (ins) {
+	int ocs = cs;
+
+	for (; brp; brp = brp->next)
+	    inststrlen(brp->str, 1, -1);
+	for (; brs; brs = brs->prev)
+	    inststrlen(brs->str, 1, -1);
+
+	if (mid >= ocs)
+	    mid += cs - ocs;
+	if (pm >= ocs)
+	    pm += cs - ocs;
+	if (sm >= ocs)
+	    sm += cs - ocs;
+	if (d >= ocs)
+	    d += cs - ocs;
+    }
     /* This calculates the new cursor position. If we had a mid cline
      * with missing characters, we take this, otherwise if we have a
      * prefix with missing characters, we take that, the same for a
@@ -8089,11 +8472,6 @@ cline_str(Cline l, int ins, int *csp)
 
 	return r;
     }
-    if (ncs >= ppos)
-	ncs += plen;
-    if (ncs > spos)
-	ncs += slen;
-
     lastend = cs;
     cs = ncs;
 
@@ -8136,7 +8514,8 @@ unambig_data(int *cp)
 static int
 instmatch(Cmatch m, int *scs)
 {
-    int l, r = 0, ocs, a = cs, brb = 0;
+    int l, r = 0, ocs, a = cs, brb = 0, bradd, *brpos;
+    Brinfo bp;
 
     zsfree(lastprebr);
     zsfree(lastpostbr);
@@ -8163,17 +8542,26 @@ instmatch(Cmatch m, int *scs)
     inststrlen(m->str, 1, (l = strlen(m->str)));
     r += l;
     ocs = cs;
-    /* Re-insert the brace beginning, if any. */
-    if (brbeg && *brbeg) {
-	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
-	lastprebr = (char *) zalloc(cs - a + 1);
-	memcpy(lastprebr, (char *) line + a, cs - a);
-	lastprebr[cs - a] = '\0';
-	l = strlen(brbeg);
-	brpcs = cs;
-	inststrlen(brbeg, 1, l);
-	r += l;
-	ocs += l;
+    /* Re-insert the brace beginnings, if any. */
+    if (brbeg) {
+	int pcs = cs;
+
+	l = 0;
+	for (bp = brbeg, brpos = m->brpl,
+		 bradd = (m->pre ? strlen(m->pre) : 0);
+	     bp; bp = bp->next, brpos++) {
+	    cs = a + *brpos + bradd;
+	    pcs = cs;
+	    l = strlen(bp->str);
+	    bradd += l;
+	    brpcs = cs;
+	    inststrlen(bp->str, 1, l);
+	    r += l;
+	    ocs += l;
+	}
+	lastprebr = (char *) zalloc(pcs - a + 1);
+	memcpy(lastprebr, (char *) line + a, pcs - a);
+	lastprebr[pcs - a] = '\0';
 	cs = ocs;
     }
     /* Path suffix. */
@@ -8182,20 +8570,27 @@ instmatch(Cmatch m, int *scs)
 	r += l;
     }
     /* Re-insert the brace end. */
-    if (brend && *brend) {
+    if (brend) {
 	a = cs;
-	cs -= m->brsl;
-	ocs = brscs = cs;
-	l = strlen(brend);
-	inststrlen(brend, 1, l);
-	brb = cs;
-	r += l;
-	cs = a + l;
-    } else
+	for (bp = brend, brpos = m->brsl, bradd = 0; bp; bp = bp->next, brpos++) {
+	    cs = a - *brpos;
+	    ocs = brscs = cs;
+	    l = strlen(bp->str);
+	    bradd += l;
+	    inststrlen(bp->str, 1, l);
+	    brb = cs;
+	    r += l;
+	}
+	cs = a + bradd;
+	if (scs)
+	    *scs = ocs;
+    } else {
 	brscs = -1;
+
+	if (scs)
+	    *scs = cs;
+    }
     /* -S suffix */
-    if (scs)
-	*scs = cs;
     if (m->suf) {
 	inststrlen(m->suf, 1, (l = strlen(m->suf)));
 	r += l;
@@ -8205,7 +8600,7 @@ instmatch(Cmatch m, int *scs)
 	inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
 	r += l;
     }
-    if (brend && *brend) {
+    if (brend) {
 	lastpostbr = (char *) zalloc(cs - brb + 1);
 	memcpy(lastpostbr, (char *) line + brb, cs - brb);
 	lastpostbr[cs - brb] = '\0';
@@ -8242,10 +8637,10 @@ hasbrpsfx(Cmatch m, char *pre, char *suf)
     brpcs = opcs;
     brscs = oscs;
 
-    ret = (((!op && !lastprebr) ||
-	    (op && lastprebr && !strcmp(op, lastprebr))) &&
-	   ((!os && !lastpostbr) ||
-	    (os && lastpostbr && !strcmp(os, lastpostbr))));
+    ret = (((!pre && !lastprebr) ||
+	    (pre && lastprebr && !strcmp(pre, lastprebr))) &&
+	   ((!suf && !lastpostbr) ||
+	    (suf && lastpostbr && !strcmp(suf, lastpostbr))));
 
     zsfree(lastprebr);
     zsfree(lastpostbr);