about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-09-22 12:35:20 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-09-22 12:35:20 +0000
commitd139ae56432a82a3dbe11f4be89ba7d886f2c177 (patch)
treef2cd4d874fe13fe923490dd69bd92a0fb3314381 /Src
parentfdb8d5b45550835c00f40591e6ee5b6f6f6fd868 (diff)
downloadzsh-d139ae56432a82a3dbe11f4be89ba7d886f2c177.tar.gz
zsh-d139ae56432a82a3dbe11f4be89ba7d886f2c177.tar.xz
zsh-d139ae56432a82a3dbe11f4be89ba7d886f2c177.zip
zsh-3.1.6-pws-5 zsh-3.1.6-pws-5
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/zle_tricky.c318
-rw-r--r--Src/glob.c4
-rw-r--r--Src/math.c125
-rw-r--r--Src/module.c6
-rw-r--r--Src/subst.c3
-rw-r--r--Src/zsh.export6
6 files changed, 326 insertions, 136 deletions
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 0d6127c6f..dc12ecc89 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -159,6 +159,10 @@ int validlist;
 
 static int ispattern, haspattern;
 
+/* Non-zero if at least one match was added without -U. */
+
+static int hasmatched;
+
 /* Two patterns used when doing glob-completion.  The first one is built *
  * from the whole word we are completing and the second one from that    *
  * part of the word that was identified as a possible filename.          */
@@ -230,6 +234,10 @@ static int unambig_mnum;
 
 static int mflags;
 
+/* Length of longest/shortest match. */
+
+static int maxmlen, minmlen;
+
 /* This holds the explanation strings we have to print in this group and *
  * a pointer to the current cexpl structure. */
 
@@ -287,6 +295,7 @@ struct cline {
     int olen;
     int slen;
     Cline prefix, suffix;
+    int min, max;
 };
 
 #define CLF_MISS  1
@@ -2003,6 +2012,39 @@ cp_cline(Cline l)
     return r;
 }
 
+/* Calculate the length of a cline and its sub-lists. */
+
+static int
+cline_sublen(Cline l)
+{
+    int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
+
+    if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
+	len += l->olen;
+    else {
+	Cline p;
+
+	for (p = l->prefix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+	for (p = l->suffix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+    }
+    return len;
+}
+
+/* Set the lengths in the cline lists. */
+
+static void
+cline_setlens(Cline l, int both)
+{
+    while (l) {
+	l->max = cline_sublen(l);
+	if (both)
+	    l->min = l->max;
+	l = l->next;
+    }
+}
+
 /* This reverts the order of the elements of the given cline list and
  * returns a pointer to the new head. */
 
@@ -2217,8 +2259,8 @@ add_match_part(Cmatcher m, char *l, char *w, int wl,
     if (!strncmp(l, w, wl))
 	l = NULL;
 
-    /* Split the new part into parts and turn the last one into a `suffix'
-     * if we have a left anchor. */
+    /* Split the new part into parts and turn the last one into a
+     * `suffix' if we have a left anchor. */
 
     p = bld_parts(s, sl, osl, &lp);
 
@@ -2234,8 +2276,21 @@ add_match_part(Cmatcher m, char *l, char *w, int wl,
 	p = revert_cline(lp = p);
     /* Now add the sub-clines we already had. */
     if (matchsubs) {
-	matchlastsub->next = p->prefix;
-	p->prefix = matchsubs;
+	if (sfx) {
+	    Cline q;
+
+	    if ((q = lp->prefix)) {
+		while (q->next)
+		    q = q->next;
+		q->next = matchsubs;
+	    } else
+		lp->prefix = matchsubs;
+
+	    matchlastsub->next = NULL;
+	} else {
+	    matchlastsub->next = p->prefix;
+	    p->prefix = matchsubs;
+	}
 	matchsubs = matchlastsub = NULL;
     }
     /* Store the arguments in the last part-cline. */
@@ -2284,13 +2339,15 @@ add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
  * matched prefix or suffix, not including the stuff before or after
  * the last anchor is given. When sfx is non-zero matching is done from
  * the ends of the strings backward, if test is zero, the global variables
- * above are used to build the string for the match and the cline. */
+ * above are used to build the string for the match and the cline. If
+ * part is non-zero, we are satisfied if only a part of the line-string
+ * is used (and return the length used). */
 
 static int
-match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
+match_str(char *l, char *w, int *bp, 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);
+    int il = 0, iw = 0, t, ind, add, bc = (bp ? *bp : 0), he = 0;
     VARARR(unsigned char, ea, ll + 1);
     char *ow;
     Cmlist ms;
@@ -2368,7 +2425,8 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 		    if (ap) {
 			if (!pattern_match(ap, l + aoff, NULL, NULL) ||
 			    (both && (!pattern_match(ap, w + aoff, NULL, NULL) ||
-				      !match_parts(l + aoff, w + aoff, alen))))
+				      !match_parts(l + aoff, w + aoff, alen,
+						   part))))
 				continue;
 		    } else if (!both || il || iw)
 			continue;
@@ -2382,18 +2440,21 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
 			 ict;
 			 tp += add, ct++, ict--) {
-			if (both ||
-			    (pattern_match(ap, tp - moff, NULL, NULL) &&
-			     match_parts(l + aoff , tp - moff, alen))) {
+			if ((both &&
+			     (!ap || !test ||
+			      !pattern_match(ap, tp + aoff, NULL, NULL))) ||
+			    (!both &&
+			     pattern_match(ap, tp - moff, NULL, NULL) &&
+			     match_parts(l + aoff , tp - moff, alen, part))) {
 			    if (sfx) {
 				savw = tp[-zoff];
 				tp[-zoff] = '\0';
 				t = match_str(l - ll, w - lw,
-					      NULL, NULL, 1, 2);
+					      NULL, NULL, 1, 2, part);
 				tp[-zoff] = savw;
 			    } else
 				t = match_str(l + llen + moff, tp + moff,
-					      NULL, NULL, 0, 1);
+					      NULL, NULL, 0, 1, part);
 			    if (t || !both)
 				break;
 			}
@@ -2409,7 +2470,7 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 
 		    /* Yes, add the strings and clines if this is a 
 		     * top-level call. */
-		    if (!test) {
+		    if (!test && (!he || (llen + alen))) {
 			char *op, *lp, *map, *wap, *wmp;
 			int ol;
 
@@ -2469,16 +2530,21 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 		    lw -= alen; iw += alen;
 		    bc -= llen;
 
-		    if (!test && bc <= 0 && bp) {
+		    if (!test && bp && bc <= 0) {
 			*bp = matchbufadded + bc;
 			bp = NULL;
 		    }
 		    ow = w;
 
-		    if (!llen && !alen)
+		    if (!llen && !alen) {
 			lm = mp;
-		    else
-			lm = NULL;
+			if (he)
+			    mp = NULL;
+			else
+			    he = 1;
+		    } else {
+			lm = NULL; he = 0;
+		    }
 		    break;
 		} else if (ll >= mp->llen && lw >= mp->wlen) {
 		    /* Non-`*'-pattern. */
@@ -2561,12 +2627,13 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 		    ll -= mp->llen; lw -= mp->wlen;
 		    bc -= mp->llen;
 
-		    if (!test && bc <= 0 && bp) {
+		    if (!test && bp && bc <= 0) {
 			*bp = matchbufadded + bc;
 			bp = NULL;
 		    }
 		    ow = w;
 		    lm = NULL;
+		    he = 0;
 		    break;
 		}
 	    }
@@ -2586,6 +2653,7 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 		bp = NULL;
 	    }
 	    lm = NULL;
+	    he = 0;
 	} else {
 	    /* No matcher and different characters: l does not match w. */
 	    if (test)
@@ -2598,7 +2666,10 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
     }
     /* If this is a recursive call, we just return if l matched w or not. */
     if (test)
-	return !ll;
+	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. */
@@ -2643,13 +2714,13 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
 
 /**/
 static int
-match_parts(char *l, char *w, int n)
+match_parts(char *l, char *w, int n, int part)
 {
     char lsav = l[n], wsav = w[n];
     int ret;
 
     l[n] = w[n] = '\0';
-    ret = match_str(l, w, NULL, NULL, 0, 1);
+    ret = match_str(l, w, NULL, NULL, 0, 1, part);
     l[n] = lsav;
     w[n] = wsav;
 
@@ -2704,7 +2775,7 @@ 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)
+	if ((mpl = match_str(pfx, w, bpl, &rpl, 0, 0, 0)) < 0)
 	    return NULL;
 
 	if (sfx && *sfx) {
@@ -2729,7 +2800,7 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp,
 
 	    /* The try to match the suffix. */
 	    *bsl = (qu ? qbrsl : brsl);
-	    if ((msl = match_str(sfx, w + mpl, bsl, &rsl, 1, 0)) < 0) {
+	    if ((msl = match_str(sfx, w + mpl, bsl, &rsl, 1, 0, 0)) < 0) {
 		free_cline(pli);
 
 		return NULL;
@@ -3398,6 +3469,7 @@ sub_join(Cline a, Cline b, Cline e, int anew)
 {
     if (!e->suffix && a->prefix) {
 	Cline op = e->prefix, n = NULL, *p = &n, t, ca;
+	int min = 0, max = 0;
 
 	for (; b != e; b = b->next) {
 	    if ((*p = t = b->prefix)) {
@@ -3407,6 +3479,8 @@ sub_join(Cline a, Cline b, Cline e, int anew)
 	    }
 	    b->suffix = b->prefix = NULL;
 	    b->flags &= ~CLF_SUF;
+	    min += b->min;
+	    max += b->max;
 	    *p = b;
 	    p = &(b->next);
 	}
@@ -3419,13 +3493,22 @@ sub_join(Cline a, Cline b, Cline e, int anew)
 
 	    if (anew) {
 		join_psfx(e, a, NULL, NULL, 0);
-		if (e->prefix)
+		if (e->prefix) {
+		    e->min += min;
+		    e->max += max;
 		    break;
+		}
 	    } else {
 		join_psfx(e, a, NULL, NULL, 0);
-		if (a->prefix)
+		if (a->prefix) {
+		    a->min += min;
+		    a->max += max;
 		    break;
+		}
 	    }
+	    min -= n->min;
+	    max -= n->max;
+
 	    n = n->next;
 	}
     }
@@ -3437,6 +3520,8 @@ sub_join(Cline a, Cline b, Cline e, int anew)
 static Cline
 join_clines(Cline o, Cline n)
 {
+    cline_setlens(n, 1);
+
     /* First time called, just return the new list. On further invocations
      * we will get it as the first argument. */
     if (!o)
@@ -3562,7 +3647,17 @@ join_clines(Cline o, Cline n)
 		    }
 		}
 	    }
-	    /* Ok, they are equal, now join the sub-lists. */
+	    /* Ok, they are equal, now copy the information about the
+             * original string if needed, calculate minimum and maximum
+	     * lengths, and join the sub-lists. */
+	    if (!o->orig && !o->olen) {
+		o->orig = n->orig;
+		o->olen = n->olen;
+	    }
+	    if (n->min < o->min)
+		o->min = n->min;
+	    if (n->max > o->max)
+		o->max = n->max;
 	    if (o->flags & CLF_MID)
 		join_mid(o, n);
 	    else
@@ -3599,6 +3694,7 @@ add_match_data(int alt, char *str, Cline line,
     Cmatch cm;
     Aminfo ai = (alt ? fainfo : ainfo);
     int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
+    int sl, lpl, lsl, ml;
 
     palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
 
@@ -3791,6 +3887,16 @@ add_match_data(int alt, char *str, Cline line,
     if (!ai->firstm)
 	ai->firstm = cm;
 
+    sl = strlen(str);
+    lpl = (cm->ppre ? strlen(cm->ppre) : 0);
+    lsl = (cm->psuf ? strlen(cm->psuf) : 0);
+    ml = sl + lpl + lsl;
+
+    if (ml < minmlen)
+	minmlen = ml;
+    if (ml > maxmlen)
+	maxmlen = ml;
+
     /* Do we have an exact match? More than one? */
     if (exact) {
 	if (!ai->exact) {
@@ -3799,13 +3905,10 @@ add_match_data(int alt, char *str, Cline line,
 		/* If a completion widget is active, we make the exact
 		 * string available in `compstate'. */
 
-		int sl = strlen(str);
-		int lpl = (cm->ppre ? strlen(cm->ppre) : 0);
-		int lsl = (cm->psuf ? strlen(cm->psuf) : 0);
 		char *e;
 
 		zsfree(compexactstr);
-		compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
+		compexactstr = e = (char *) zalloc(ml + 1);
 		if (cm->ppre) {
 		    strcpy(e, cm->ppre);
 		    e += lpl;
@@ -3854,7 +3957,8 @@ 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, llpl = 0, llsl = 0, nm = mnum;
+    int lpl, lsl, pl, sl, bpl, bsl, bppl = -1, bssl = -1;
+    int llpl = 0, llsl = 0, nm = mnum;
     int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
     Cline lc = NULL;
     Cmatch cm;
@@ -3886,7 +3990,9 @@ addmatches(Cadata dat, char **argv)
      * was invoked. */
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
-	    doadd = (!dat->apar && !dat->opar && !dat->dpar);
+	    if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) &&
+		(dat->aflags & CAF_MATCH))
+		hasmatched = 1;
 	    if (dat->apar)
 		aparl = newlinklist();
 	    if (dat->opar)
@@ -3993,20 +4099,35 @@ addmatches(Cadata dat, char **argv)
 	    } else
 		lsl = 0;
 	    if (dat->aflags & CAF_MATCH) {
+		int ml;
+
 		s = dat->ppre ? dat->ppre : "";
-		if (llpl <= lpl && strpfx(lpre, s))
-		    lpre = "";
-		else if (llpl > lpl && strpfx(s, lpre))
-		    lpre += lpl;
-		else
-		    *argv = NULL;
+		bppl = brpl;
+		if ((ml = match_str(lpre, s, &bppl, NULL, 0, 0, 1)) >= 0)
+		    lpre += ml;
+		else {
+		    bppl = -1;
+		    if (llpl <= lpl && strpfx(lpre, s))
+			lpre = "";
+		    else if (llpl > lpl && strpfx(s, lpre))
+			lpre += lpl;
+		    else
+			*argv = NULL;
+		}
+
 		s = dat->psuf ? dat->psuf : "";
-		if (llsl <= lsl && strsfx(lsuf, s))
-		    lsuf = "";
-		else if (llsl > lsl && strsfx(s, lsuf))
-		    lsuf[llsl - lsl] = '\0';
-		else
-		    *argv = NULL;
+		bssl = brsl;
+		if ((ml = match_str(lsuf, s, &bssl, NULL, 0, 0, 1)) >= 0)
+		    lsuf[llsl - ml] = '\0';
+		else {
+		    bssl = -1;
+		    if (llsl <= lsl && strsfx(lsuf, s))
+			lsuf = "";
+		    else if (llsl > lsl && strsfx(s, lsuf))
+			lsuf[llsl - lsl] = '\0';
+		    else
+			*argv = NULL;
+		}
 	    }
 	    if (*argv) {
 		if (dat->pre)
@@ -4090,7 +4211,9 @@ addmatches(Cadata dat, char **argv)
 		    cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
 					dat->isuf, dat->pre, dat->prpre,
 					dat->ppre, dat->psuf, dat->suf,
-					bpl, bsl, dat->flags, isexact);
+					(bppl >= 0 ? bppl : bpl),
+					(bssl >= 0 ? bssl : bsl),
+					dat->flags, isexact);
 		    cm->rems = dat->rems;
 		    cm->remf = dat->remf;
 		    if (disp)
@@ -4587,6 +4710,9 @@ docompletion(char *s, int lst, int incmd)
 				"yes" : "");
 	movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
 	showinglist = 0;
+	hasmatched = 0;
+	minmlen = 1000000;
+	maxmlen = -1;
 
 	/* Make sure we have the completion list and compctl. */
 	if (makecomplist(s, incmd, lst)) {
@@ -6255,6 +6381,9 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     untokenize(lpre);
     untokenize(lsuf);
 
+    if (!(cc->mask & CC_DELETE))
+	hasmatched = 1;
+
     /* Handle completion of files specially (of course). */
 
     if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
@@ -7458,6 +7587,86 @@ inststrlen(char *str, int move, int len)
     return len;
 }
 
+/* This cuts the cline list before the stuff that isn't worth
+ * inserting in the line. */
+
+static Cline
+cut_cline(Cline l)
+{
+    Cline p, e = NULL, maxp = NULL;
+    int sum = 0, max = 0, tmp, ls = 0;
+
+    /* If no match was added with matching, we don't really know
+     * which parts of the unambiguous string are worth keeping,
+     * so for now we keep everything (in the hope that this
+     * produces a string containing at least everything that was 
+     * originally on the line). */
+
+    if (!hasmatched) {
+	cline_setlens(l, 0);
+	return l;
+    }
+    e = l = cp_cline(l);
+
+    /* First, search the last struct for which we have something on
+     * the line. Anything before that is kept. */
+
+    for (p = l; p; p = p->next)
+	if (p->orig || p->olen)
+	    e = p->next;
+
+    /* Then keep all structs without missing characters. */
+
+    while (e && !(e->flags & CLF_MISS))
+	e = e->next;
+
+    if (e) {
+	/* Then we see if there is another struct with missing
+	 * characters. If not, we keep the whole list. */
+
+	for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
+
+	if (p) {
+	    for (p = e; p; p = p->next) {
+		if (!(p->flags & CLF_MISS))
+		    sum += p->max;
+		else {
+		    tmp = cline_sublen(p);
+		    if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
+			sum += tmp - (p->max - tmp);
+		    else if (tmp < p->min)
+			sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
+		}
+		if (sum > max) {
+		    max = sum;
+		    maxp = p;
+		}
+	    }
+	    if (max)
+		e = maxp;
+	    else {
+		int len = 0;
+
+		cline_setlens(l, 0);
+		ls = 1;
+
+		for (p = e; p; p = p->next)
+		    len += p->max;
+
+		if (len > ((minmlen << 1) / 3))
+		    return l;
+	    }
+	    e->line = e->word = NULL;
+	    e->llen = e->wlen = e->olen = 0;
+	    e->next = NULL;
+	}
+    }
+    if (!ls)
+	cline_setlens(l, 0);
+
+    return l;
+}
+
 /* This builds the unambiguous string. If ins is non-zero, it is
  * immediatly inserted in the line. Otherwise csp is used to return
  * the relative cursor position in the string returned. */
@@ -7466,11 +7675,13 @@ static char *
 cline_str(Cline l, int ins, int *csp)
 {
     Cline s;
-    int ocs = cs, ncs, pcs, pm, sm, d, b, i, j, li = 0;
+    int ocs = cs, ncs, pcs, pm, pmax, sm, smax, d, b, i, j, li = 0;
     int pl, sl, hasp, hass, ppos, spos, plen, slen;
 
+    l = cut_cline(l);
+
     ppos = spos = plen = slen = hasp = hass = 0;
-    pm = sm = d = b = pl = sl = -1;
+    pm = pmax = sm = smax = d = b = pl = sl = -1;
 
     /* Get the information about the brace beginning and end we have
      * to re-insert. */
@@ -7532,8 +7743,10 @@ cline_str(Cline l, int ins, int *csp)
 	}
 	/* Remember the position if this is the first prefix with
 	 * missing characters. */
-	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF))
-	    pm = cs;
+	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
+	    (pmax < (l->min - l->max))) {
+	    pm = cs; pmax = l->min - l->max;
+	}
 	pcs = cs;
 	/* Insert the anchor. */
 	if (l->flags & CLF_LINE)
@@ -7553,8 +7766,9 @@ cline_str(Cline l, int ins, int *csp)
 	if (l->flags & CLF_MISS) {
 	    if (l->flags & CLF_MID)
 		b = cs;
-	    else if (l->flags & CLF_SUF)
-		sm = cs;
+	    else if ((l->flags & CLF_SUF) && smax < (l->min - l->max)) {
+		sm = cs; smax = l->min - l->max;
+	    }
 	}
 	/* And now insert the suffix or the original string. */
 	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
diff --git a/Src/glob.c b/Src/glob.c
index 55cc96a52..07336986d 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -2001,7 +2001,7 @@ getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr)
 static int
 igetmatch(char **sp, Patprog p, int fl, int n, char *replstr)
 {
-    char *s = *sp, *t, *start, sav;
+    char *s = *sp, *t, sav;
     int i, l = strlen(*sp), ml = ztrlen(*sp), matched = 1;
 
     MUSTUSEHEAP("igetmatch");	/* presumably covered by prefork() test */
@@ -2210,7 +2210,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr)
 	LinkNode nd;
 	Repldata rd;
 	int lleft = 0;		/* size of returned string */
-	char *ptr;
+	char *ptr, *start;
 
 	i = 0;			/* start of last chunk we got from *sp */
 	for (nd = firstnode(repllist); nd; incnode(nd)) {
diff --git a/Src/math.c b/Src/math.c
index e044cbd9f..f91a58ea8 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -57,37 +57,24 @@ static int unary = 1;
  * RL = right-to-left associativity *
  * BOOL = short-circuiting boolean   */
 
-#define LR   0x0000
-#define RL   0x0001
-#define BOOL 0x0002
+#define LR 0
+#define RL 1
+#define BOOL 2
 
 #define MTYPE(x)  ((x) & 3)
 
 /*
- * OP_A2    2 arguments
- * OP_A2IR  2 arguments, return integer
- * OP_A2IO  2 arguments, must be integer, return integer
- * OP_E2    2 arguments with assignment
- * OP_E2IO  2 arguments with assignment, must be integer, return integer
- * OP_OP    None of the above, but occurs where we are expecting an operator
- *          rather than an operand.
- * OP_OPF   Followed by an operator, not an operand.
- *
- * OP_A2*, OP_E2*, OP_OP*:
- *   Occur when we need an operator; the next object must be an operand,
- *   unless OP_OPF is also supplied.
- *
- * Others:
- *   Occur when we need an operand; the next object must also be an operand,
- *   unless OP_OPF is also supplied.
+ * OP_A2 2 argument
+ * OP_A2IR 2 argument with return type integer
+ * OP_A2IO 2 arguments, must be integer, returning integer
+ * OP_E2 2 argument with assignment
+ * OP_E2IO 2 arguments with assignment, must be integer, return integer
  */
-#define OP_A2   0x0004
-#define OP_A2IR 0x0008
-#define OP_A2IO 0x0010
-#define OP_E2   0x0020
-#define OP_E2IO 0x0040
-#define OP_OP   0x0080
-#define OP_OPF  0x0100
+#define OP_A2   4
+#define OP_A2IR 8
+#define OP_A2IO 16
+#define OP_E2   32
+#define OP_E2IO 64
 
 #define M_INPAR 0
 #define M_OUTPAR 1
@@ -165,17 +152,17 @@ static int prec[TOKCOUNT] =
 
 static int type[TOKCOUNT] =
 {
-/*  0 */  LR, LR|OP_OP|OP_OPF, RL, RL, RL|OP_OP|OP_OPF,
-/*  5 */  RL|OP_OP|OP_OPF, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
-/* 10 */  LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
-/* 15 */  LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
-/* 20 */  LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
-/* 25 */  BOOL|OP_A2IO, LR|OP_A2IO, RL|OP_OP, RL|OP_OP, RL|OP_E2,
-/* 30 */  RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
-/* 35 */  RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
-/* 40 */  BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL|OP_OP,
-/* 45 */  RL, RL, LR|OP_OPF, LR|OP_OPF, RL|OP_A2,
-/* 50 */  LR|OP_OPF, RL|OP_E2
+/*  0 */    LR, LR, RL, RL, RL,
+/*  5 */    RL, RL, RL, LR|OP_A2IO, LR|OP_A2IO,
+/* 10 */    LR|OP_A2IO, LR|OP_A2, LR|OP_A2, LR|OP_A2IO, LR|OP_A2,
+/* 15 */    LR|OP_A2, LR|OP_A2IO, LR|OP_A2IO, LR|OP_A2IR, LR|OP_A2IR,
+/* 20 */    LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, LR|OP_A2IR, BOOL|OP_A2IO,
+/* 25 */    BOOL|OP_A2IO, LR|OP_A2IO, RL, RL, RL|OP_E2,
+/* 30 */    RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2, RL|OP_E2IO,
+/* 35 */    RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO, RL|OP_E2IO,
+/* 40 */    BOOL|OP_E2IO, BOOL|OP_E2IO, RL|OP_A2IO, RL|OP_A2, RL,
+/* 45 */    RL, RL, LR, LR, RL|OP_A2,
+/* 50 */    LR, RL|OP_E2
 };
 
 #define LVCOUNT 32
@@ -201,6 +188,7 @@ zzlex(void)
 		return (unary) ? PREPLUS : POSTPLUS;
 	    }
 	    if (*ptr == '=') {
+		unary = 1;
 		ptr++;
 		return PLUSEQ;
 	    }
@@ -211,16 +199,19 @@ zzlex(void)
 		return (unary) ? PREMINUS : POSTMINUS;
 	    }
 	    if (*ptr == '=') {
+		unary = 1;
 		ptr++;
 		return MINUSEQ;
 	    }
 	    return (unary) ? UMINUS : MINUS;
 	case '(':
+	    unary = 1;
 	    return M_INPAR;
 	case ')':
 	    return M_OUTPAR;
 	case '!':
 	    if (*ptr == '=') {
+		unary = 1;
 		ptr++;
 		return NEQ;
 	    }
@@ -228,6 +219,7 @@ zzlex(void)
 	case '~':
 	    return COMP;
 	case '&':
+	    unary = 1;
 	    if (*ptr == '&') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -240,6 +232,7 @@ zzlex(void)
 	    }
 	    return AND;
 	case '|':
+	    unary = 1;
 	    if (*ptr == '|') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -252,6 +245,7 @@ zzlex(void)
 	    }
 	    return OR;
 	case '^':
+	    unary = 1;
 	    if (*ptr == '^') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -264,6 +258,7 @@ zzlex(void)
 	    }
 	    return XOR;
 	case '*':
+	    unary = 1;
 	    if (*ptr == '*') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -277,18 +272,21 @@ zzlex(void)
 	    }
 	    return MUL;
 	case '/':
+	    unary = 1;
 	    if (*ptr == '=') {
 		ptr++;
 		return DIVEQ;
 	    }
 	    return DIV;
 	case '%':
+	    unary = 1;
 	    if (*ptr == '=') {
 		ptr++;
 		return MODEQ;
 	    }
 	    return MOD;
 	case '<':
+	    unary = 1;
 	    if (*ptr == '<') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -301,6 +299,7 @@ zzlex(void)
 	    }
 	    return LES;
 	case '>':
+	    unary = 1;
 	    if (*ptr == '>') {
 		if (*++ptr == '=') {
 		    ptr++;
@@ -313,12 +312,14 @@ zzlex(void)
 	    }
 	    return GRE;
 	case '=':
+	    unary = 1;
 	    if (*ptr == '=') {
 		ptr++;
 		return DEQ;
 	    }
 	    return EQ;
 	case '$':
+	    unary = 0;
 	    yyval.u.l = mypid;
 	    return NUM;
 	case '?':
@@ -327,15 +328,20 @@ zzlex(void)
 		unary = 0;
 		return NUM;
 	    }
+	    unary = 1;
 	    return QUEST;
 	case ':':
+	    unary = 1;
 	    return COLON;
 	case ',':
+	    unary = 1;
 	    return COMMA;
 	case '\0':
+	    unary = 1;
 	    ptr--;
 	    return EOI;
 	case '[':
+	    unary = 0;
 	    {
 		int base = zstrtol(ptr, &ptr, 10);
 
@@ -350,6 +356,7 @@ zzlex(void)
 	    break;
 	case '0':
 	    if (*ptr == 'x' || *ptr == 'X') {
+		unary = 0;
 		/* Should we set lastbase here? */
 		yyval.u.l = zstrtol(++ptr, &ptr, lastbase = 16);
 		return NUM;
@@ -358,6 +365,7 @@ zzlex(void)
 	default:
 	    if (idigit(*--ptr) || *ptr == '.') {
 		char *nptr;
+		unary = 0;
 		for (nptr = ptr; idigit(*nptr); nptr++);
 
 		if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') {
@@ -387,6 +395,7 @@ zzlex(void)
 		    ptr++;
 		    ptr = getkeystring(ptr, NULL, 6, &v);
 		    yyval.u.l = v;
+		    unary = 0;
 		    return NUM;
 		}
 		cct = 1;
@@ -399,6 +408,7 @@ zzlex(void)
 		    zerr("too many identifiers (complain to author)", NULL, 0);
 		    return EOI;
 		}
+		unary = 0;
 		while (iident(*++ptr));
 		if (*ptr == '[') {
 		    int l;
@@ -419,6 +429,7 @@ zzlex(void)
 	    }
 	    else if (cct) {
 		yyval.u.l = poundgetfn(NULL);
+		unary = 0;
 		return NUM;
 	    }
 	    return EOI;
@@ -504,8 +515,6 @@ op(int what)
     LV lv;
     int tp = type[what];
 
-    if (errflag)
-	return;
     if (sp < 0) {
 	zerr("bad math expression: stack empty", NULL, 0);
 	return;
@@ -895,40 +904,6 @@ mathevalarg(char *s, char **ss)
     return (x.type & MN_FLOAT) ? (zlong)x.u.d : x.u.l;
 }
 
-/*
- * Make sure we have an operator or an operand, whatever is expected.
- * For this purpose, unary operators constitute part of an operand.
- */
-
-/**/
-static void
-checkunary(int tp, char *ptr)
-{
-    int errmsg = 0;
-    if (tp & (OP_A2|OP_A2IR|OP_A2IO|OP_E2|OP_E2IO|OP_OP)) {
-	if (unary)
-	    errmsg = 1;
-    } else {
-	if (!unary)
-	    errmsg = 2;
-    }
-    if (errmsg) {
-	char errbuf[40];
-	int len, over = 0;
-	while (inblank(*ptr))
-	    ptr++;
-	len = strlen(ptr);
-	if (len > 10) {
-	    len = 10;
-	    over = 1;
-	}
-	sprintf(errbuf, "bad math expression: %s expected at `%%l%s'",
-		errmsg == 2 ? "operator" : "operand",
-		over ? "..." : ""); 
-	zerr(errbuf, ptr, len);
-    }
-    unary = !(tp & OP_OPF);
-}
 
 /* operator-precedence parse the string and execute */
 
@@ -938,12 +913,10 @@ mathparse(int pc)
 {
     zlong q;
     int otok, onoeval;
-    char *optr = ptr;
 
     if (errflag)
 	return;
     mtok = zzlex();
-    checkunary(type[mtok], optr);
     while (prec[mtok] <= pc) {
 	if (errflag)
 	    return;
@@ -991,8 +964,6 @@ mathparse(int pc)
 	    op(otok);
 	    continue;
 	}
-	optr = ptr;
 	mtok = zzlex();
-	checkunary(type[mtok], optr);
     }
 }
diff --git a/Src/module.c b/Src/module.c
index dab2c0350..d10206155 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -1181,13 +1181,13 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 		nicezputs((char *) getdata(node), stdout);
 		putchar('\n');
 	    }
-	    return 0;
 	} else {
 	    for (; *args; args++)
 		for (node = firstnode(bltinmodules); node; incnode(node))
-		    if (!strcmp(*args, (char *) getdata(node)))
-			return 0;
+		    if (strcmp(*args, (char *) getdata(node)))
+			return 1;
 	}
+	return 0;
     }
     /* Otherwise we return 1 -- different from the dynamic version. */
     return 1;
diff --git a/Src/subst.c b/Src/subst.c
index 5b08d0bfb..b0bb885c9 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -152,8 +152,7 @@ stringsubst(LinkList list, LinkNode node, int ssub)
 	    *str++ = '\0';
 	    if (endchar == Outpar && str2[1] == '(' && str[-2] == ')') {
 		/* Math substitution of the form $((...)) */
-		str[-2] = '\0';
-		str = arithsubst(str2 + 2, &str3, str);
+		str = arithsubst(str2 + 1, &str3, str);
 		setdata(node, (void *) str3);
 		continue;
 	    }
diff --git a/Src/zsh.export b/Src/zsh.export
index 92844d39d..625fef331 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -19,6 +19,7 @@ arrlen
 attachtty
 bangchar
 bin_notavail
+bltinmodules
 breaks
 bslashquote
 bufstack
@@ -33,6 +34,7 @@ compctlreadptr
 cond_match
 cond_str
 cond_val
+condtab
 convbase
 coprocin
 coprocout
@@ -50,6 +52,7 @@ deletehookfunc
 deleteparamdefs
 deleteparamtable
 deletewrapper
+dirstack
 dosetopt
 doshfunc
 down_histent
@@ -78,6 +81,7 @@ findcmd
 freearray
 freeheap
 freelinklist
+freestr
 freestruct
 getaparam
 gethashnode
@@ -149,6 +153,7 @@ metadiffer
 metafy
 metalen
 mode_to_octal
+modules
 movefd
 movehistent
 mypgrp
@@ -195,6 +200,7 @@ pushheap
 putshout
 pwd
 quietgethist
+quietgethistent
 quotedzputs
 readoutput
 realparamtab