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.h10
-rw-r--r--Src/Zle/comp1.c79
-rw-r--r--Src/Zle/comp1.export2
-rw-r--r--Src/Zle/compctl.c52
-rw-r--r--Src/Zle/zle_hist.c9
-rw-r--r--Src/Zle/zle_main.c60
-rw-r--r--Src/Zle/zle_misc.c47
-rw-r--r--Src/Zle/zle_thingy.c4
-rw-r--r--Src/Zle/zle_tricky.c347
-rw-r--r--Src/Zle/zle_vi.c4
-rw-r--r--Src/builtin.c85
-rw-r--r--Src/compat.c27
-rw-r--r--Src/exec.c2
-rw-r--r--Src/glob.c5
-rw-r--r--Src/init.c28
-rw-r--r--Src/makepro.awk7
-rw-r--r--Src/options.c1
-rw-r--r--Src/params.c2
-rw-r--r--Src/subst.c37
-rw-r--r--Src/utils.c108
-rw-r--r--Src/zsh.export4
-rw-r--r--Src/zsh.h2
22 files changed, 683 insertions, 239 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 20e3d2cce..5f263557a 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -210,6 +210,7 @@ struct cmatch {
     int flags;			/* see CMF_* below */
     int brpl;			/* the place where to put the brace prefix */
     int brsl;			/* ...and the suffix */
+    char *rems;			/* when to remove the suffix */
 };
 
 #define CMF_FILE     1		/* this is a file */
@@ -272,3 +273,12 @@ struct cline {
 
 #define CFN_FIRST   1
 #define CFN_DEFAULT 2
+
+/* Flags for compadd and addmatches(). */
+
+#define CAF_QUOTE    1
+#define CAF_MENU     2
+#define CAF_NOSORT   4
+#define CAF_ALT      8
+#define CAF_FIGNORE 16
+#define CAF_MATCH   32
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 36588a385..a5e35bc3c 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -49,7 +49,7 @@ void (*makecompparamsptr) _((void));
 /* pointers to functions required by compctl and defined by zle */
 
 /**/
-void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, int, int, int, int, int, char **));
+void (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char **));
 
 /**/
 char *(*comp_strptr) _((int*,int*));
@@ -95,7 +95,8 @@ int incompfunc;
 
 /**/
 long compcurrent,
-     compnmatches;
+     compnmatches,
+     compmatcher;
 
 /**/
 char *compcontext,
@@ -104,14 +105,8 @@ char *compcontext,
      *compsuffix,
      *compiprefix;
 
-/* This variable and the functions rembslash() and quotename() came from     *
- * zle_tricky.c, but are now used in compctl.c, too.                         */
-
-/* 1 if we are completing in a string */
-
-/**/
-int instring;
-
+/* The function rembslash() came from zle_tricky.c, but is now used *
+ * in compctl.c, too.                                               */
 
 /**/
 static void
@@ -395,70 +390,6 @@ rembslash(char *s)
     return t;
 }
 
-/* Quote the string s and return the result.  If e is non-zero, the        *
- * pointer it points to may point to a position in s and in e the position *
- * of the corresponding character in the quoted string is returned.  Like  *
- * e, te may point to a position in the string and pl is used to return    *
- * the position of the character pointed to by te in the quoted string.    *
- * The string is metafied and may contain tokens.                          */
-
-/**/
-char *
-quotename(const char *s, char **e, char *te, int *pl)
-{
-    const char *u, *tt;
-    char *v, buf[PATH_MAX * 2];
-    int sf = 0;
-
-    tt = v = buf;
-    u = s;
-    for (; *u; u++) {
-	if (e && *e == u)
-	    *e = v, sf |= 1;
-	if (te == u)
-	    *pl = v - tt, sf |= 2;
-	if (ispecial(*u) &&
-	    (!instring || (isset(BANGHIST) &&
-			   *u == (char)bangchar) ||
-	     (instring == 2 &&
-	      (*u == '$' || *u == '`' || *u == '\"')) ||
-	     (instring == 1 && *u == '\''))) {
-	    if (*u == '\n' || (instring == 1 && *u == '\'')) {
-		if (unset(RCQUOTES)) {
-		    *v++ = '\'';
-		    if (*u == '\'')
-			*v++ = '\\';
-		    *v++ = *u;
-		    *v++ = '\'';
-		} else if (*u == '\n')
-		    *v++ = '"', *v++ = '\n', *v++ = '"';
-		else
-		    *v++ = '\'', *v++ = '\'';
-		continue;
-	    } else
-		*v++ = '\\';
-	}
-	if(*u == Meta)
-	    *v++ = *u++;
-	*v++ = *u;
-    }
-    *v = '\0';
-    if (strcmp(buf, s))
-	tt = dupstring(buf);
-    else
-	tt = s;
-    v += tt - buf;
-    if (e && (sf & 1))
-	*e += tt - buf;
-
-    if (e && *e == u)
-	*e = v;
-    if (te == u)
-	*pl = v - tt;
-
-    return (char *) tt;
-}
-
 /**/
 int
 setup_comp1(Module m)
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 72581173b..278006b6a 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -14,6 +14,7 @@ compcontext
 compctltab
 compcurrent
 compiprefix
+compmatcher
 compnmatches
 compprefix
 comp_strptr
@@ -30,5 +31,4 @@ makecomplistcallptr
 makecomplistctlptr
 makecompparamsptr
 patcomps
-quotename
 rembslash
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 879042cf3..dcc206c96 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1385,7 +1385,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	    untokenize(p);
 	    quotedzputs(p, stdout);
 	} else
-	    quotedzputs(quotename(s, NULL, NULL, NULL), stdout);
+	    quotedzputs(bslashquote(s, NULL, NULL, NULL, 0), stdout);
     }
 
     /* loop through flags w/o args that are set, printing them if so */
@@ -1515,7 +1515,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 		char *p = dupstring(s);
 
 		untokenize(p);
-		quotedzputs(quotename(p, NULL, NULL, NULL), stdout);
+		quotedzputs(bslashquote(p, NULL, NULL, NULL, 0), stdout);
 	    }
 	}
 	putchar('\n');
@@ -1547,8 +1547,6 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     cclist = 0;
     showmask = 0;
 
-    instring = 0;
-
     /* Parse all the arguments */
     if (*argv) {
 	/* Let's see if this is a global matcher definition. */
@@ -1671,8 +1669,9 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 {
     char *p, **sp, *e;
     char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
-    char *pre = NULL, *suf = NULL, *group = NULL;
-    int f = 0, q = 0, m = 0, ns = 0, a = 0;
+    char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
+    int f = 0, a = 0, dm;
+    Cmatcher match = NULL;
 
     if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
@@ -1681,21 +1680,25 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     for (; *argv && **argv ==  '-'; argv++) {
 	for (p = *argv + 1; *p; p++) {
 	    sp = NULL;
+	    dm = 0;
 	    switch (*p) {
 	    case 'q':
 		f |= CMF_REMOVE;
 		break;
 	    case 'Q':
-		q = 1;
+		a |= CAF_QUOTE;
 		break;
 	    case 'f':
 		f |= CMF_FILE;
 		break;
+	    case 'F':
+		a |= CAF_FIGNORE;
+		break;
 	    case 'n':
 		f |= CMF_NOLIST;
 		break;
 	    case 'U':
-		m = 1;
+		a |= CAF_MENU;
 		break;
 	    case 'P':
 		sp = ⪯
@@ -1711,7 +1714,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		break;
 	    case 'V':
 		if (!group)
-		    ns = 1;
+		    a |= CAF_NOSORT;
 		sp = &group;
 		e = "group name expected after -%c";
 		break;
@@ -1732,7 +1735,19 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		e = "string expected after -%c";
 		break;
 	    case 'a':
-		a = 1;
+		a |= CAF_ALT;
+		break;
+	    case 'm':
+		a |= CAF_MATCH;
+		break;
+	    case 'M':
+		sp = &m;
+		e = "matching specification expected after -%c";
+		dm = 1;
+		break;
+	    case 'r':
+		sp = &rs;
+		e = "string expected after -%c";
 		break;
 	    case '-':
 		argv++;
@@ -1756,6 +1771,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		    zerrnam(name, e, NULL, *p);
 		    return 1;
 		}
+		if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
+		    match = NULL;
+		    return 1;
+		}
 	    }
 	}
     }
@@ -1764,7 +1783,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	return 1;
 
     addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
-		  f, q, m, ns, a, argv);
+		  rs, f, a, match, argv);
     return 0;
 }
 
@@ -1794,6 +1813,7 @@ static struct compparam {
     { "SUFFIX", PM_SCALAR, VAR(compsuffix) },
     { "IPREFIX", PM_SCALAR, VAR(compiprefix) },
     { "NMATCHES", PM_INTEGER, VAR(compnmatches) },
+    { "MATCHER", PM_INTEGER, VAR(compmatcher) },
     { NULL, 0, NULL }
 };
 
@@ -2096,6 +2116,15 @@ cond_nmatches(char **a, int id)
     return 0;
 }
 
+/**/
+static int
+cond_matcher(char **a, int id)
+{
+    if (comp_check())
+	return compmatcher == cond_val(a, 0);
+    return 0;
+}
+
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
     BUILTIN("complist", 0, bin_complist, 1, -1, 0, NULL, NULL),
@@ -2119,6 +2148,7 @@ static struct conddef cotab[] = {
     CONDDEF("after", 0, cond_range, 1, 1, 0),
     CONDDEF("mafter", 0, cond_range, 1, 1, 1),
     CONDDEF("nmatches", 0, cond_nmatches, 1, 1, 0),
+    CONDDEF("matcher", 0, cond_matcher, 1, 1, 0),
 };
 
 static struct funcwrap wrapper[] = {
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index fda204222..e6b385652 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -663,6 +663,11 @@ doisearch(int dir)
     static char *previous_search = NULL;
     static int previous_search_len = 0;
 
+    invalidatelist();
+    moveto(0, 0);
+    clearflag = 0;
+    resetneeded = 1; 
+
     strcpy(ibuf, ISEARCH_PROMPT);
     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     remember_edits();
@@ -943,6 +948,10 @@ getvisrchstr(void)
 	zsfree(visrchstr);
 	visrchstr = NULL;
     }
+    invalidatelist();
+    moveto(0, 0);
+    clearflag = 0;
+    resetneeded = 1; 
     statusline = sbuf;
     sbuf[0] = (visrchsense == -1) ? '?' : '/';
     selectkeymap("main", 1);
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 03549b5d0..ca7a7fe19 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -661,10 +661,17 @@ bin_vared(char *name, char **args, char *ops, int func)
 {
     char *s;
     char *t;
-    Param pm;
+    Value v;
+    Param pm = 0;
     int create = 0;
+    int type = PM_SCALAR;
     char *p1 = NULL, *p2 = NULL;
 
+    if (zleactive) {
+	zwarnnam(name, "ZLE cannot be used recursively (yet)", NULL, 0);
+	return 1;
+    }
+
     /* all options are handled as arguments */
     while (*args && **args == '-') {
 	while (*++(*args))
@@ -674,6 +681,12 @@ bin_vared(char *name, char **args, char *ops, int func)
 		yet exist */
 		create = 1;
 		break;
+	    case 'a':
+		type = PM_ARRAY;
+		break;
+	    case 'A':
+		type = PM_HASHED;
+		break;
 	    case 'p':
 		/* -p option -- set main prompt string */
 		if ((*args)[1])
@@ -709,6 +722,9 @@ bin_vared(char *name, char **args, char *ops, int func)
 	    }
 	args++;
     }
+    if (type && !create) {
+	zwarnnam(name, "-%s ignored", type == PM_ARRAY ? "a" : "A", 0);
+    }
 
     /* check we have a parameter name */
     if (!*args) {
@@ -716,17 +732,17 @@ bin_vared(char *name, char **args, char *ops, int func)
 	return 1;
     }
     /* handle non-existent parameter */
-    if (!(s = getsparam(args[0]))) {
-	if (create)
-	    createparam(args[0], PM_SCALAR);
-	else {
-	    zwarnnam(name, "no such variable: %s", args[0], 0);
-	    return 1;
-	}
-    }
-
-    if(zleactive) {
-	zwarnnam(name, "ZLE cannot be used recursively (yet)", NULL, 0);
+    s = args[0];
+    v = fetchvalue(&s, (!create || type == PM_SCALAR),
+		   SCANPM_WANTKEYS|SCANPM_WANTVALS|SCANPM_MATCHMANY);
+    if (!v && !create) {
+	zwarnnam(name, "no such variable: %s", args[0], 0);
+	return 1;
+    } else if (v) {
+	s = getstrvalue(v);
+	pm = v->pm;
+    } else if (*s) {
+	zwarnnam(name, "invalid parameter name: %s", args[0], 0);
 	return 1;
     }
 
@@ -744,14 +760,24 @@ bin_vared(char *name, char **args, char *ops, int func)
     if (t[strlen(t) - 1] == '\n')
 	t[strlen(t) - 1] = '\0';
     /* final assignment of parameter value */
-    pm = (Param) paramtab->getnode(paramtab, args[0]);
-    if (pm && PM_TYPE(pm->flags) == PM_ARRAY) {
+    if (create && (!pm || (type && PM_TYPE(pm->flags) != type))) {
+	if (pm)
+	    unsetparam(args[0]);
+	createparam(args[0], type);
+	pm = 0;
+    }
+    if (!pm)
+	pm = (Param) paramtab->getnode(paramtab, args[0]);
+    if (pm && (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED))) {
 	char **a;
 
 	PERMALLOC {
 	    a = spacesplit(t, 1);
 	} LASTALLOC;
-	setaparam(args[0], a);
+	if (PM_TYPE(pm->flags) == PM_ARRAY)
+	    setaparam(args[0], a);
+	else
+	    sethparam(args[0], a);
     } else
 	setsparam(args[0], t);
     return 0;
@@ -766,6 +792,10 @@ describekeybriefly(void)
 
     if (statusline)
 	return;
+    invalidatelist();
+    moveto(0, 0);
+    clearflag = 0;
+    resetneeded = 1; 
     statusline = "Describe key briefly: _";
     statusll = strlen(statusline);
     zrefresh();
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 836ff09ae..4b5df9cde 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -612,6 +612,10 @@ executenamedcommand(char *prmt)
     char *ptr;
     char *okeymap = curkeymapname;
 
+    invalidatelist();
+    moveto(0, 0);
+    clearflag = 0;
+    resetneeded = 1; 
     cmdbuf = halloc(l + NAMLEN + 2);
     strcpy(cmdbuf, prmt);
     statusline = cmdbuf;
@@ -792,6 +796,49 @@ makeparamsuffix(int br, int n)
     }
 }
 
+/* Set up suffix given a string containing the characters on which to   *
+ * remove the suffix. */
+
+/**/
+void
+makesuffixstr(char *s, int n)
+{
+    if (s) {
+	int inv, i, v, z = 0;
+
+	if (*s == '^' || *s == '!') {
+	    inv = 1;
+	    s++;
+	} else
+	    inv = 0;
+	s = getkeystring(s, &i, 5, &z);
+	s = metafy(s, i, META_USEHEAP);
+
+	if (inv) {
+	    v = 0;
+	    for (i = 0; i < 257; i++)
+		 suffixlen[i] = n;
+	} else
+	    v = n;
+
+	if (z)
+	    suffixlen[256] = v;
+
+	while (*s) {
+	    if (s[1] == '-' && s[2]) {
+		int b = (int) *s, e = (int) s[2];
+
+		while (b <= e)
+		    suffixlen[b++] = v;
+		s += 2;
+	    } else
+		suffixlen[STOUC(*s)] = v;
+	    s++;
+	}
+    } else
+	makesuffix(n);
+}
+
 /* Remove suffix, if there is one, when inserting character c. */
 
 /**/
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 629d5a84e..d055c45c1 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -390,7 +390,7 @@ scanlistwidgets(HashNode hn, int list)
     if(w->flags & WIDGET_INT)
 	return;
     if(list) {
-	fputs("zle -N ", stdout);
+	printf("zle -%c ", (w->flags & WIDGET_NCOMP) ? 'C' : 'N');
 	if(t->nam[0] == '-')
 	    fputs("-- ", stdout);
 	quotedzputs(t->nam, stdout);
@@ -406,7 +406,7 @@ scanlistwidgets(HashNode hn, int list)
     } else {
 	nicezputs(t->nam, stdout);
 	if (w->flags & WIDGET_NCOMP) {
-	    fputs(" -c ", stdout);
+	    fputs(" -C ", stdout);
 	    nicezputs(w->u.comp.wid, stdout);
 	    fputc(' ', stdout);
 	    nicezputs(w->u.comp.func, stdout);
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index df3e11f46..f1285da8c 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -223,7 +223,7 @@ typedef struct aminfo *Aminfo;
 
 struct aminfo {
     int cpl, csl, icpl, icsl;	/* common prefix/suffix lengths           */
-    int prerest;		/* minimum prefix rest                    */
+    int minlen;			/* minimum match length                   */
     int suflen;			/* minimum suffix length                  */
     Cmatch firstm;		/* the first match                        */
     char *pprefix;		/* common part of the -P prefixes         */
@@ -270,6 +270,19 @@ enum { COMP_COMPLETE,
        COMP_LIST_EXPAND };
 #define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
 
+/* Non-zero if the last completion done was ambiguous (used to find   *
+ * out if AUTOMENU should start).  More precisely, it's nonzero after *
+ * successfully doing any completion, unless the completion was       *
+ * unambiguous and did not cause the display of a completion list.    *
+ * From the other point of view, it's nonzero iff AUTOMENU (if set)   *
+ * should kick in on another completion.                              *
+ *                                                                    *
+ * If both AUTOMENU and BASHAUTOLIST are set, then we get a listing   *
+ * on the second tab, a` la bash, and then automenu kicks in when     *
+ * lastambig == 2.                                                    */
+
+static int lastambig;
+
 /**/
 void
 completecall(void)
@@ -287,8 +300,13 @@ completeword(void)
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
 	selfinsert();
-    else
-	docomplete(COMP_COMPLETE);
+    else {
+	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
+	    docomplete(COMP_LIST_COMPLETE);
+	    lastambig = 2;
+	} else
+	    docomplete(COMP_COMPLETE);
+    }
 }
 
 /**/
@@ -358,8 +376,13 @@ expandorcomplete(void)
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
 	selfinsert();
-    else
-	docomplete(COMP_EXPAND_COMPLETE);
+    else {
+	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
+	    docomplete(COMP_LIST_COMPLETE);
+	    lastambig = 2;
+	} else
+	    docomplete(COMP_EXPAND_COMPLETE);
+    }
 }
 
 /**/
@@ -451,15 +474,6 @@ static int lincmd, linredir;
 
 static char *rdstr;
 
-/* Non-zero if the last completion done was ambiguous (used to find   *
- * out if AUTOMENU should start).  More precisely, it's nonzero after *
- * successfully doing any completion, unless the completion was       *
- * unambiguous and did not cause the display of a completion list.    *
- * From the other point of view, it's nonzero iff AUTOMENU (if set)   *
- * should kick in on another completion.                              */
-
-static int lastambig;
-
 /* This holds the name of the current command (used to find the right *
  * compctl).                                                          */
 
@@ -473,6 +487,16 @@ static char *varname;
 
 static int insubscr;
 
+/* 1 if we are completing in a string */
+
+/**/
+int instring;
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above.                              */
+
+#define quotename(s, e, te, pl) bslashquote(s, e, te, pl, instring)
+
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
 
@@ -569,7 +593,8 @@ docomplete(int lst)
 
     /* Check if we have to start a menu-completion (via automenu). */
 
-    if ((amenu = (isset(AUTOMENU) && lastambig)))
+    if ((amenu = (isset(AUTOMENU) && lastambig &&
+		  (!isset(BASHAUTOLIST) || lastambig == 2))))
 	usemenu = 1;
 
     /* Expand history references before starting completion.  If anything *
@@ -919,7 +944,7 @@ unmetafy_line(void)
 static char *
 get_comp_string(void)
 {
-    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins;
+    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, inarr, ia, parct;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
     zsfree(brbeg);
@@ -982,7 +1007,7 @@ get_comp_string(void)
 	inpush(dupstrspace((char *) linptr), 0, NULL);
 	strinbeg();
 	stophist = 2;
-	i = tt0 = cp = rd = ins = oins = 0;
+	i = tt0 = cp = rd = ins = oins = inarr = parct = ia = 0;
 
 	/* This loop is possibly the wrong way to do this.  It goes through *
 	 * the previously massaged command line using the lexer.  It stores *
@@ -1001,7 +1026,21 @@ get_comp_string(void)
 	    linredir = (inredir && !ins);
 	    oins = ins;
 	    /* Get the next token. */
+	    if (inarr)
+		incmdpos = 0;
 	    ctxtlex();
+	    if (tok == ENVARRAY) {
+		inarr = 1;
+		zsfree(varname);
+		varname = ztrdup(tokstr);
+	    } else if (tok == INPAR)
+		parct++;
+	    else if (tok == OUTPAR) {
+		if (parct)
+		    parct--;
+		else
+		    inarr = 0;
+	    }
 	    if (inredir)
 		rdstr = tokstrings[tok];
 	    if (tok == DINPAR)
@@ -1043,6 +1082,7 @@ get_comp_string(void)
 		clwpos = i;
 		cp = lincmd;
 		rd = linredir;
+		ia = inarr;
 		if (inwhat == IN_NOTHING && incond)
 		    inwhat = IN_COND;
 	    } else if (linredir)
@@ -1084,8 +1124,13 @@ get_comp_string(void)
 	zsfree(clwords[clwnum]);
 	clwords[clwnum] = NULL;
 	t0 = tt0;
-	lincmd = cp;
-	linredir = rd;
+	if (ia) {
+	    lincmd = linredir = 0;
+	    inwhat = IN_ENV;
+	} else {
+	    lincmd = cp;
+	    linredir = rd;
+	}
 	strinend();
 	inpop();
 	errflag = zleparse = 0;
@@ -1789,9 +1834,17 @@ inst_cline(Cline l, int pl, int sl)
     pl += brpl;
 
     i = cs - wb;
+    if (pl >= 0 && i >= pl && brbeg && *brbeg) {
+	inststrlen(brbeg, 1, -1);
+	pl = -1;
+	hb = 1;
+    }
+    if (sl >= 0 && i >= sl && brend && *brend) {
+	inststrlen(brend, 1, -1);
+	sl = -1;
+	hb = 1;
+    }
     while (l) {
-	if (d < 0 && (l->flags & CLF_DIFF))
-	    d = cs;
 	if (m < 0 && (l->flags & (CLF_MISS | CLF_SUF)) == (CLF_MISS | CLF_SUF))
 	    m = cs;
 	if (l->flags & CLF_MID) {
@@ -1805,6 +1858,8 @@ inst_cline(Cline l, int pl, int sl)
 	} else {
 	    inststrlen(l->line, 1, l->llen);
 	}
+	if (d < 0 && (l->flags & CLF_DIFF))
+	    d = cs;
 	if (m < 0 && (l->flags & CLF_MISS))
 	    m = cs;
 	i += l->llen;
@@ -1893,8 +1948,10 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
     while (ll && lw) {
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
-		if (nm == mp || lm == mp)
+		if (nm == mp || lm == mp) {
+		    nm = NULL;
 		    continue;
+		}
 		t = 1;
 		/* Try to match the prefix, if any. */
 		if (mp->flags & CMF_LEFT) {
@@ -1930,7 +1987,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
 						  NULL, NULL, mp))
 					break;
 				}
-				if (k) {
+				if (k && i) {
 				    if (nlp) {
 					nw = addtoword(&rw, &rwlen, nw, mp,
 						       l, w, i, 0);
@@ -2106,8 +2163,10 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
     while (ll && lw) {
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
-		if (nm == mp || lm == mp)
+		if (nm == mp || lm == mp) {
+		    nm = NULL;
 		    continue;
+		}
 		t = 1;
 		if (mp->flags & CMF_RIGHT) {
 		    if (il < mp->ralen || iw < mp->ralen)
@@ -2135,13 +2194,13 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 			    if (t) {
 				int i = 0, j = iw, k = lw;
 				int jj = il + mp->llen, kk = ll - mp->llen;
-				char *p = l - mp->llen, *q = w;
+				char *p = l - mp->llen - 1, *q = w - 1;
 
 				for (; k; i++, j++, k--, q--)
-				    if (match_sfx(p, q, NULL, NULL,
-						  NULL, mp))
+				    if (match_pfx(p, q, NULL, NULL,
+						  NULL, NULL, mp))
 					break;
-				if (k) {
+				if (k && i) {
 				    if (nlp) {
 					nw = addtoword(&rw, &rwlen, nw, mp,
 						       l - mp->llen, w - i,
@@ -2151,10 +2210,10 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 						   ((mp->flags & CMF_LEFT) ?
 						    CLF_SUF : 0));
 				    }
-				    w = q;
+				    w = q + 1;
 				    iw = j;
 				    lw = k;
-				    l = p;
+				    l = p + 1;
 				    il = jj;
 				    ll = kk;
 				    bc -= i;
@@ -2395,20 +2454,45 @@ instmatch(Cmatch m)
 /**/
 void
 addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
-	   char *suf, char *group,
-	   int flags, int quote, int menu, int nosort, int alt, char **argv)
+	   char *suf, char *group, char *rems, 
+	   int flags, int aflags, Cmatcher match, char **argv)
 {
-    char *s, *t;
-    int lpl, lsl, i;
-    Aminfo ai = (alt ? fainfo : ainfo);
+    char *s, *t, *e, *te, *ms, *lipre = NULL, *lpre, *lsuf;
+    int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl;
+    Aminfo ai;
+    Cline lc = NULL;
+    LinkList l;
     Cmatch cm;
+    struct cmlist mst;
+    Cmlist oms = mstack;
 
-    if (menu && isset(AUTOMENU))
+    if (aflags & CAF_ALT) {
+	l = fmatches;
+	ai = fainfo;
+    } else {
+	l = matches;
+	ai = ainfo;
+    }
+    if (match) {
+	mst.next = mstack;
+	mst.matcher = match;
+	mstack = &mst;
+    }
+    if ((aflags & CAF_MENU) && isset(AUTOMENU))
 	usemenu = 1;
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
+	    if (aflags & CAF_MATCH) {
+		ctokenize(lipre = dupstring(compiprefix));
+		remnulargs(lipre);
+		ctokenize(lpre = dupstring(compprefix));
+		remnulargs(lpre);
+		llpl = strlen(lpre);
+		ctokenize(lsuf = dupstring(compsuffix));
+		remnulargs(lsuf);
+	    }
 	    if (ipre)
-		ipre = dupstring(ipre);
+		ipre = (lipre ? dyncat(lipre, ipre) : dupstring(ipre));
 	    if (ppre) {
 		ppre = dupstring(ppre);
 		lpl = strlen(ppre);
@@ -2419,6 +2503,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		lsl = strlen(psuf);
 	    } else
 		lsl = 0;
+	    if (aflags & CAF_MATCH)
+		lsm = (psuf ? !strcmp(psuf, lsuf) : (!lsuf || !*lsuf));
 	    if (pre)
 		pre = dupstring(pre);
 	    if (suf)
@@ -2430,10 +2516,12 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		prpre = dupstring(prpre);
 	    if (group) {
 		endcmgroup(NULL);
-		begcmgroup(group, nosort);
-		if (nosort)
+		begcmgroup(group, (aflags & CAF_NOSORT));
+		if (aflags & CAF_NOSORT)
 		    mgroup->flags |= CGF_NOSORT;
 	    }
+	    if (rems)
+		rems = dupstring(rems);
     	    if (ai->pprefix) {
 		if (pre)
 		    ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
@@ -2442,10 +2530,54 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	    } else
 		ai->pprefix = dupstring(pre ? pre : "");
 
-	    for (; (s = *argv); argv++) {
-		if (ai->firstm) {
-		    if ((i = pfxlen(ai->firstm->str, s)) < ai->prerest)
-			ai->prerest = i;
+	    for (; (s = dupstring(*argv)); argv++) {
+		sl = strlen(s);
+		ms = NULL;
+		bpl = brpl;
+		bsl = brsl;
+		if ((!psuf || !*psuf) && (aflags & CAF_FIGNORE)) {
+		    char **pt = fignore;
+		    int filell;
+
+		    for (test = 1; test && *pt; pt++)
+			if ((filell = strlen(*pt)) < sl
+			    && !strcmp(*pt, s + sl - filell))
+			    test = 0;
+
+		    if (!test) {
+			l = fmatches;
+			ai = fainfo;
+		    } else {
+			l = matches;
+			ai = ainfo;
+		    }
+		}
+		if (aflags & CAF_MATCH) {
+		    t = (ppre ? dyncat(ppre, s) : s);
+		    pl = sl + lpl;
+		    if ((test = (llpl <= pl && !strncmp(t, lpre, pl))))
+			test = lsm;
+		    if (!test && mstack &&
+			(ms = comp_match(lpre, lsuf,
+					 (psuf ? dyncat(t, psuf) : t),
+					 &lc, (aflags & CAF_QUOTE),
+					 &bpl, &bsl)))
+			test = 1;
+		    if (!test)
+			continue;
+		    e = s + sl;
+		} else {
+		    e = s;
+		    pl = lpl;
+		}
+		if (!(aflags & CAF_QUOTE)) {
+		    te = s + pl;
+		    s = quotename(s, &e, te, &pl);
+		    sl = strlen(s);
+		}
+		if (!ms && ai->firstm) {
+		    if (sl < ai->minlen)
+			ai->minlen = sl;
 		    if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen)
 			ai->suflen = i;
 		}
@@ -2453,15 +2585,20 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		if (ppre)
 		    t = dyncat(ppre, t);
 		if (ipre && *ipre) {
+		    Cline tlc = prepend_cline(ipre, lc);
+
 		    ai->noipre = 0;
-		    if (ai->icpl > lpl)
-			ai->icpl = lpl;
-		    if (ai->icsl > lsl)
-			ai->icsl = lsl;
-		    if (ai->iaprefix)
-			ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
-		    else
-			ai->iaprefix = dupstring(t);
+		    if (!ms) {
+			if ((aflags & CAF_MATCH) || ai->icpl > pl)
+			    ai->icpl = pl;
+			if ((aflags & CAF_MATCH) || ai->icsl > lsl)
+			    ai->icsl = lsl;
+			if (ai->iaprefix)
+			    ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
+			else
+			    ai->iaprefix = dupstring(t);
+		    } else
+			ai->ilinecl = join_clines(ai->ilinecl, lc);
 		    if (ai->iprefix) {
 			if (strcmp(ipre, ai->iprefix))
 			    ai->iprefix = "";
@@ -2469,16 +2606,21 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 			ai->iprefix = dupstring(ipre);
 
 		    t = dyncat(ipre, t);
+		    lc = tlc;
 		} else
 		    ai->iprefix = "";
-		if (ai->cpl > lpl)
-		    ai->cpl = lpl;
-		if (ai->csl > lsl)
-		    ai->csl = lsl;
-		if (ai->aprefix)
-		    ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
-		else
-		    ai->aprefix = dupstring(t);
+		if (!ms) {
+		    if ((aflags & CAF_MATCH) || ai->cpl > pl)
+			ai->cpl = pl;
+		    if ((aflags & CAF_MATCH) || ai->csl > lsl)
+			ai->csl = lsl;
+		    if (ai->aprefix)
+			ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
+		    else
+			ai->aprefix = dupstring(t);
+		} else
+		    ai->linecl = join_clines(ai->linecl, lc);
+
 		mnum++;
 		ai->count++;
 
@@ -2486,25 +2628,41 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		cm->ppre = ppre;
 		cm->psuf = psuf;
 		cm->prpre = prpre;
-		if (!quote)
-		    s = quotename(s, NULL, NULL, NULL);
-		cm->str = dupstring(s);
+		cm->str = (ms ? ms : dupstring(s));
 		cm->ipre = cm->ripre = ipre;
 		cm->pre = pre;
 		cm->suf = suf;
 		cm->flags = flags;
-		cm->brpl = brpl;
-		cm->brsl = brsl;
-		addlinknode((alt ? fmatches : matches), cm);
-
-		if (expl)
-		    expl->fcount++;
-		if (!ai->firstm)
-		    ai->firstm = cm;
+		cm->brpl = bpl;
+		cm->brsl = bsl;
+		cm->rems = rems;
+		addlinknode(l, cm);
+
+		if (expl) {
+		    if (l == matches)
+			expl->count++;
+		    else
+			expl->fcount++;
+		}
+		if (!ms) {
+		    if (!ai->firstm)
+			ai->firstm = cm;
+		    if ((aflags & CAF_MATCH) && !(e - (s + pl))) {
+			if (!ai->exact)
+			    ai->exact = 1;
+			else {
+			    ai->exact = 2;
+			    cm = NULL;
+			}
+			ai->exactm = cm;
+		    }
+		}
 	    }
 	    compnmatches = mnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
+
+    mstack = oms;
 }
 
 /* This adds a match to the list of matches.  The string to add is given   *
@@ -2685,8 +2843,8 @@ addmatch(char *s, char *t)
     if (!test)
 	return;
     if (!ms && !ispattern && ai->firstm) {
-	if ((test = sl - pfxlen(ai->firstm->str, s)) < ai->prerest)
-	    ai->prerest = test;
+	if (sl < ai->minlen)
+	    ai->minlen = sl;
 	if ((test = sfxlen(ai->firstm->str, s)) < ai->suflen)
 	    ai->suflen = test;
     }
@@ -2716,8 +2874,7 @@ addmatch(char *s, char *t)
 		ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
 	    else
 		ai->iaprefix = dupstring(t);
-	}
-	else
+	} else
 	    ai->ilinecl = join_clines(ai->ilinecl, lc);
 	if (ai->iprefix) {
 	    if (strcmp(ipre, ai->iprefix))
@@ -2737,8 +2894,7 @@ addmatch(char *s, char *t)
 	    ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
 	else
 	    ai->aprefix = dupstring(t);
-    }
-    else
+    } else
 	ai->linecl = join_clines(ai->linecl, lc);
 
     mnum++;
@@ -2780,6 +2936,7 @@ addmatch(char *s, char *t)
     cm->flags = mflags | isf;
     cm->brpl = bpl;
     cm->brsl = bsl;
+    cm->rems = NULL;
     addlinknode(l, cm);
 
     /* One more match for this explanation. */
@@ -3211,7 +3368,12 @@ callcompfunc(char *s, char *fn)
 	    case IN_ENV:
 		compcontext = "value";
 		compcommand = varname;
-		usea = 0;
+		if (!clwpos) {
+		    clwpos = 1;
+		    zsfree(clwords[1]);
+		    clwords[1] = ztrdup(s);
+		}
+		aadd = 1;
 		break;
 	    case IN_COND:
 		compcontext = "condition";
@@ -3305,17 +3467,19 @@ makecomplist(char *s, int incmd, int lst)
     if (validlist)
 	return !nmatches;
 
+    compmatcher = 1;
     for (;;) {
 	if (m) {
 	    ms.next = NULL;
 	    ms.matcher = m->matcher;
 	    mstack = &ms;
-	}
+	} else
+	    mstack = NULL;
 	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 
-	ainfo->prerest = ainfo->suflen = 
-	    fainfo->prerest = fainfo->suflen = 10000;
+	ainfo->minlen = ainfo->suflen = 
+	    fainfo->minlen = fainfo->suflen = 10000;
 	ainfo->noipre = fainfo->noipre= 1;
 
 	freecl = NULL;
@@ -3358,12 +3522,14 @@ makecomplist(char *s, int incmd, int lst)
 	    break;
 
 	errflag = 0;
+	compmatcher++;
     }
     return 1;
 }
 
 /* This should probably be moved into tokenize(). */
 
+/**/
 static char *
 ctokenize(char *p)
 {
@@ -3564,10 +3730,11 @@ makecomplistcmd(char *os, int incmd, int flags)
     }
     /* Then search the pattern compctls, with the command name and the *
      * full pathname of the command. */
-    makecomplistpc(os, incmd);
-    if (!(ccont & CC_CCCONT))
-	return;
-
+    if (cmdstr) {
+	makecomplistpc(os, incmd);
+	if (!(ccont & CC_CCCONT))
+	    return;
+    }
     /* If the command string starts with `=', try the path name of the *
      * command. */
     if (cmdstr && cmdstr[0] == Equals) {
@@ -5057,6 +5224,7 @@ dupmatch(Cmatch m)
     r->flags = m->flags;
     r->brpl = m->brpl;
     r->brsl = m->brsl;
+    r->rems = ztrdup(m->rems);
 
     return r;
 }
@@ -5168,6 +5336,7 @@ freematch(Cmatch m)
     zsfree(m->ppre);
     zsfree(m->psuf);
     zsfree(m->prpre);
+    zsfree(m->rems);
 
     zfree(m, sizeof(m));
 }
@@ -5325,10 +5494,11 @@ do_ambiguous(void)
 	 * on the next call to completion the inserted string would be     *
 	 * taken as a match and no menu completion would be started.       */
 
-	if (isset(RECEXACT) && !lc && !ainfo->prerest)
+	if (isset(RECEXACT) && !lc && ps && ainfo->minlen == strlen(ps))
 	    am = 1;
 
-	/* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
+	/*
+	 * If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
 	 * if the completion is completely ambiguous') is set, and some    *
 	 * prefix was inserted, return now, bypassing the list-displaying  *
 	 * code.  On the way, invalidate the list and note that we don't   *
@@ -5344,7 +5514,8 @@ do_ambiguous(void)
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
 	feep();
-    if (isset(AUTOLIST) && !amenu && !showinglist && smatches >= 2)
+    if (isset(AUTOLIST) && !isset(BASHAUTOLIST) && !amenu && !showinglist &&
+	smatches >= 2)
 	showinglist = -2;
     if (am)
 	lastambig = 1;
@@ -5420,7 +5591,7 @@ do_single(Cmatch m)
 	if (menuwe) {
 	    menuend += menuinsc;
 	    if (m->flags & CMF_REMOVE) {
-		makesuffix(menuinsc);
+		makesuffixstr(m->rems, menuinsc);
 		if (menuinsc == 1)
 		    suffixlen[STOUC(m->suf[0])] = 1;
 	    }
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index e699e438c..a116921b4 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -816,6 +816,10 @@ viswapcase(void)
 void
 vicapslockpanic(void)
 {
+    invalidatelist();
+    moveto(0, 0);
+    clearflag = 0;
+    resetneeded = 1; 
     zbeep();
     statusline = "press a lowercase key to continue";
     statusll = strlen(statusline);
diff --git a/Src/builtin.c b/Src/builtin.c
index ea1ac8ab9..2b77c57bc 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1036,6 +1036,9 @@ fixdir(char *src)
 {
     char *dest = src;
     char *d0 = dest;
+#ifdef __CYGWIN__
+    char *s0 = src;
+#endif
 
 /*** if have RFS superroot directory ***/
 #ifdef HAVE_SUPERROOT
@@ -1052,6 +1055,11 @@ fixdir(char *src)
     for (;;) {
 	/* compress multiple /es into single */
 	if (*src == '/') {
+#ifdef __CYGWIN__
+	    /* allow leading // under cygwin */
+	    if (src == s0 && src[1] == '/')
+		*dest++ = *src++;
+#endif
 	    *dest++ = *src++;
 	    while (*src == '/')
 		src++;
@@ -1657,7 +1665,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 
     if (on & PM_TIED) {
 	Param apm;
-	char *name1;
+	struct asgment asg0;
+	char *oldval = NULL;
 
 	if (ops['m']) {
 	    zwarnnam(name, "incompatible options for -T", NULL, 0);
@@ -1669,36 +1678,61 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    return 1;
 	}
 
+	if (!(asg = getasg(argv[0])))
+	    return 1;
+	asg0 = *asg;
+	if (!(asg = getasg(argv[1])))
+	    return 1;
+	if (!strcmp(asg0.name, asg->name)) {
+	    zerrnam(name, "can't tie a variable to itself", NULL, 0);
+	    return 1;
+	}
+	/*
+	 * Keep the old value of the scalar.  We need to do this
+	 * here as if it is already tied to the same array it
+	 * will be unset when we retie the array.  This is all
+	 * so that typeset -T is idempotent.
+	 *
+	 * We also need to remember here whether the damn thing is
+	 * exported and pass that along.  Isn't the world complicated?
+	 */
+	if ((pm = (Param) paramtab->getnode(paramtab, asg0.name))
+	    && !(pm->flags & PM_UNSET)
+	    && (locallevel == pm->level || func == BIN_EXPORT)) {
+	    if (!asg0.value && !(PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)))
+		oldval = ztrdup(getsparam(asg0.name));
+	    on |= (pm->flags & PM_EXPORTED);
+	}
 	/*
 	 * Create the tied array; this is normal except that
 	 * it has the PM_TIED flag set.  Do it first because
 	 * we need the address.
 	 */
-	if (!(asg = getasg(argv[1])))
-	    return 1;
-	name1 = ztrdup(asg->name);
 	if (!(apm=typeset_single(name, asg->name,
 				 (Param)paramtab->getnode(paramtab,
 							  asg->name),
-				 func, on | PM_ARRAY, off, roff,
-				 asg->value, NULL)))
+				 func, (on | PM_ARRAY) & ~PM_EXPORTED,
+				 off, roff, asg->value, NULL)))
 	    return 1;
 
 	/*
 	 * Create the tied colonarray.  We make it as a normal scalar
 	 * and fix up the oddities later.
 	 */
-	if (!(asg = getasg(argv[0])) ||
-	    !(pm=typeset_single(name, asg->name,
+	if (!(pm=typeset_single(name, asg0.name,
 				(Param)paramtab->getnode(paramtab,
-							 asg->name),
-				func, on, off, roff, asg->value, apm))) {
+							 asg0.name),
+				func, on, off, roff, asg0.value, apm))) {
+	    if (oldval)
+		zsfree(oldval);
 	    unsetparam_pm(apm, 1, 1);
 	    return 1;
 	}
 
-	pm->ename = name1;
-	apm->ename = ztrdup(asg->name);
+	pm->ename = ztrdup(asg->name);
+	apm->ename = ztrdup(asg0.name);
+	if (oldval)
+	    setsparam(asg0.name, oldval);
 
 	return 0;
     }
@@ -1928,14 +1962,39 @@ bin_unset(char *name, char **argv, char *ops, int func)
 
     /* do not glob -- unset the given parameter */
     while ((s = *argv++)) {
+	char *ss = strchr(s, '[');
+	char *sse = ss;
+	if (ss) {
+	    if (skipparens('[', ']', &sse) || *sse) {
+		zerrnam(name, "%s: invalid parameter name", s, 0);
+		returnval = 1;
+		continue;
+	    }
+	    *ss = 0;
+	}
 	pm = (Param) paramtab->getnode(paramtab, s);
 	if (!pm)
 	    returnval = 1;
 	else if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) {
 	    zerrnam(name, "%s: restricted", pm->nam, 0);
 	    returnval = 1;
+	} else if (ss) {
+	    if (PM_TYPE(pm->flags) == PM_HASHED) {
+		HashTable tht = paramtab;
+		if ((paramtab = pm->gets.hfn(pm))) {
+		    *--sse = 0;
+		    unsetparam(ss+1);
+		    *sse = ']';
+		}
+		paramtab = tht;
+	    } else {
+		zerrnam(name, "%s: invalid element for unset", s, 0);
+		returnval = 1;
+	    }
 	} else
-	    unsetparam(s);
+	    unsetparam_pm(pm, 0, 1);
+	if (ss)
+	    *ss = '[';
     }
     return returnval;
 }
diff --git a/Src/compat.c b/Src/compat.c
index ca9c57aac..b1bcbc21b 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -111,12 +111,17 @@ zgetdir(struct dirsav *d)
 {
     char nbuf[PATH_MAX+3];
     char *buf;
-    int bufsiz, pos, len;
+    int bufsiz, pos;
     struct stat sbuf;
+    ino_t pino;
+    dev_t pdev;
+#ifndef __CYGWIN__
     struct dirent *de;
     DIR *dir;
-    ino_t ino, pino;
-    dev_t dev, pdev;
+    dev_t dev;
+    ino_t ino;
+    int len;
+#endif
 
     buf = halloc(bufsiz = PATH_MAX);
     pos = bufsiz - 1;
@@ -137,6 +142,7 @@ zgetdir(struct dirsav *d)
 #ifdef HAVE_FCHDIR
     else
 #endif
+#ifndef __CYGWIN__
 	holdintr();
 
     for (;;) {
@@ -221,6 +227,21 @@ zgetdir(struct dirsav *d)
     if (*buf)
 	zchdir(buf + pos + 1);
     noholdintr();
+
+#else  /* __CYGWIN__ case */
+
+    if (!getcwd(buf, bufsiz)) {
+	if (d) {
+	    return NULL;
+	}
+    } else {
+	if (d) {
+	    return d->dirname = ztrdup(buf);
+	}
+	return buf;
+    }
+#endif
+
     buf[0] = '.';
     buf[1] = '\0';
     return buf;
diff --git a/Src/exec.c b/Src/exec.c
index e563db6a4..bb331f426 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -148,6 +148,7 @@ parse_string(char *s)
     return l;
 }
 
+/**/
 #ifdef HAVE_GETRLIMIT
 
 /* the resource limits for the shell and its children */
@@ -184,6 +185,7 @@ setlimits(char *nam)
     return ret;
 }
 
+/**/
 #endif /* HAVE_GETRLIMIT */
 
 /* fork and set limits */
diff --git a/Src/glob.c b/Src/glob.c
index 7a3839576..5815f05a8 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1432,7 +1432,7 @@ glob(LinkList list, LinkNode np)
 			    }
 			}
 			break;
-		    case 'o':
+		    case 'f':
 			/* Match modes with chmod-spec. */
 			func = qualmodeflags;
 			data = qgetmodespec(&s);
@@ -1501,6 +1501,7 @@ glob(LinkList list, LinkNode np)
 			data = qgetnum(&s);
 			break;
 
+		    case 'o':
 		    case 'O':
 			{
 			    int t;
@@ -1524,7 +1525,7 @@ glob(LinkList list, LinkNode np)
 			    }
 			    gf_sorts |= t;
 			    gf_sortlist[gf_nsorts++] = t |
-				((sense & 1) ? GS_DESC : 0);
+				(((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0);
 			    s++;
 			    break;
 			}
diff --git a/Src/init.c b/Src/init.c
index 5e0a550dd..10013c52b 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -28,11 +28,12 @@
  */
 
 #include "zsh.mdh"
-#include "init.pro"
 
 #include "zshpaths.h"
 #include "zshxmods.h"
 
+#include "init.pro"
+
 /**/
 int noexitct = 0;
 
@@ -300,22 +301,19 @@ init_io(void)
 
     /* Make sure the tty is opened read/write. */
     if (isatty(0)) {
+	zsfree(ttystrname);
+	if ((ttystrname = ztrdup(ttyname(0)))) {
+	    SHTTY = movefd(open(ttystrname, O_RDWR | O_NOCTTY));
 #ifdef TIOCNXCL
-	/*
-	 * See if the terminal claims to be busy.  If so, and fd 0
-	 * is a terminal, try and set non-exclusive use for that.
-	 * This is something to do with Solaris over-cleverness.
-	 */
-	int tmpfd;
-	if ((tmpfd = open("/dev/tty", O_RDWR | O_NOCTTY)) < 0) {
-	    if (errno == EBUSY)
+	    /*
+	     * See if the terminal claims to be busy.  If so, and fd 0
+	     * is a terminal, try and set non-exclusive use for that.
+	     * This is something to do with Solaris over-cleverness.
+	     */
+	    if (SHTTY == -1 && errno == EBUSY)
 		ioctl(0, TIOCNXCL, 0);
-	} else
-	    close(tmpfd);
 #endif
-	zsfree(ttystrname);
-	if ((ttystrname = ztrdup(ttyname(0))))
-	    SHTTY = movefd(open(ttystrname, O_RDWR | O_NOCTTY));
+	}
 	/*
 	 * xterm, rxvt and probably all terminal emulators except
 	 * dtterm on Solaris 2.6 & 7 have a bug. Applications are
@@ -936,6 +934,7 @@ noop_function_int(int nothing)
     /* do nothing */
 }
 
+/**/
 # ifdef UNLINKED_XMOD_zle
 
 /**/
@@ -947,6 +946,7 @@ autoload_zleread(char *lp, char *rp, int ha)
     return zleread(lp, rp, ha);
 }
 
+/**/
 # endif /* UNLINKED_XMOD_zle */
 
 /**/
diff --git a/Src/makepro.awk b/Src/makepro.awk
index 86117fcc1..3306d41d4 100644
--- a/Src/makepro.awk
+++ b/Src/makepro.awk
@@ -40,6 +40,13 @@ BEGIN {
 	    aborting = 1
 	    exit 1
 	}
+	if (line == "" && $0 ~ /^[ \t]*#/) {
+            # Directly after the /**/ was a preprocessor line.
+            # Spit it out and re-start the outer loop.
+	    printf "%s\n", $0
+	    locals = locals $0 "\n"
+	    next
+	}
 	gsub(/\t/, " ")
 	line = line " " $0
 	gsub(/\/\*([^*]|\*+[^*\/])*\*+\//, " ", line)
diff --git a/Src/options.c b/Src/options.c
index 745a6627c..693132494 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -90,6 +90,7 @@ static struct optname optns[] = {
 {NULL, "badpattern",	      OPT_EMULATE|OPT_NONBOURNE, BADPATTERN},
 {NULL, "banghist",	      OPT_EMULATE|OPT_NONBOURNE, BANGHIST},
 {NULL, "bareglobqual",        OPT_EMULATE|OPT_ZSH,       BAREGLOBQUAL},
+{NULL, "bashautolist",	      0,                         BASHAUTOLIST},
 {NULL, "beep",		      OPT_ALL,			 BEEP},
 {NULL, "bgnice",	      OPT_EMULATE|OPT_NONBOURNE, BGNICE},
 {NULL, "braceccl",	      0,			 BRACECCL},
diff --git a/Src/params.c b/Src/params.c
index f57413a2e..f91a448a1 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -1500,7 +1500,7 @@ setsparam(char *s, char *val)
     } else {
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_SCALAR);
-	else if (PM_TYPE(v->pm->flags) == PM_ARRAY &&
+	else if ((PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
 		 !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) {
 	    unsetparam(t);
 	    createparam(t, PM_SCALAR);
diff --git a/Src/subst.c b/Src/subst.c
index b77dbd4a4..422c9c4e9 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -718,10 +718,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     int copied = 0;
     int arrasg = 0;
     int eval = 0;
+    int aspar = 0;
     int nojoin = 0;
     char inbrace = 0;		/* != 0 means ${...}, otherwise $... */
     char hkeys = 0;
     char hvals = 0;
+    int subexp;
 
     *s++ = '\0';
     if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
@@ -813,6 +815,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		case 'e':
 		    eval = 1;
 		    break;
+		case 'P':
+		    aspar = 1;
+		    break;
 
 		case 'c':
 		    whichlen = 1;
@@ -949,7 +954,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    } else
 		globsubst = 1;
 	} else if (*s == '+') {
-	    if (iident(s[1]))
+	    if (iident(s[1]) || (aspar && isstring(s[1]) &&
+				 (s[2] == Inbrace || s[2] == Inpar)))
 		chkset = 1, s++;
 	    else if (!inbrace) {
 		*aptr = '$';
@@ -965,7 +971,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     globsubst = globsubst && !qt;
 
     idbeg = s;
-    if (s[-1] && isstring(*s) && (s[1] == Inbrace || s[1] == Inpar)) {
+    if ((subexp = (s[-1] && isstring(*s) &&
+		   (s[1] == Inbrace || s[1] == Inpar)))) {
 	int sav;
 	int quoted = *s == Qstring;
 
@@ -973,17 +980,28 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
 	sav = *s;
 	*s = 0;
-	if (multsub(&val, &aval, &isarr, NULL) && quoted) {
+	if (multsub(&val, (aspar ? NULL : &aval), &isarr, NULL) && quoted) {
 	    isarr = -1;
 	    aval = alloc(sizeof(char *));
-	}
+	    aspar = 0;
+	} else if (aspar)
+	    idbeg = val;
 	if (isarr)
 	    isarr = -1;
 	copied = 1;
 	*s = sav;
 	v = (Value) NULL;
-    } else {
-	if (!(v = fetchvalue(&s, (wantt ? -1 :
+    } else if (aspar) {
+	if ((v = getvalue(&s, 1))) {
+	    val = idbeg = getstrvalue(v);
+	    subexp = 1;
+	} else
+	    vunset = 1;
+    }
+    if (!subexp || aspar) {
+	char *ov = val;
+
+	if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 :
 				  ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
 			     hkeys|hvals)))
 	    vunset = 1;
@@ -1690,6 +1708,7 @@ modify(char **str, char **ptr)
 	    case 't':
 	    case 'l':
 	    case 'u':
+	    case 'q':
 		c = **ptr;
 		break;
 
@@ -1808,6 +1827,9 @@ modify(char **str, char **ptr)
 			if (hsubl && hsubr)
 			    subst(&copy, hsubl, hsubr, gbal);
 			break;
+		    case 'q':
+			copy = bslashquote(copy, NULL, NULL, NULL, 0);
+			break;
 		    }
 		    tc = *tt;
 		    *tt = '\0';
@@ -1859,6 +1881,9 @@ modify(char **str, char **ptr)
 			}
 		    }
 		    break;
+		case 'q':
+		    *str = bslashquote(*str, NULL, NULL, NULL, 0);
+		    break;
 		}
 	    }
 	    if (rec < 0) {
diff --git a/Src/utils.c b/Src/utils.c
index 90da15368..18a797948 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -446,7 +446,8 @@ finddir_scan(HashNode hn, int flags)
 {
     Nameddir nd = (Nameddir) hn;
 
-    if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full)) {
+    if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full)
+       && !(nd->flags & ND_NOABBREV)) {
 	finddir_last=nd;
 	finddir_best=nd->diff;
     }
@@ -514,9 +515,8 @@ adduserdir(char *s, char *t, int flags, int always)
      * with always==0.  Unless the AUTO_NAME_DIRS option is set, we  *
      * don't let such assignments actually create directory names.   *
      * Instead, a reference to the parameter as a directory name can *
-     * cause the actual creation of the hash table entry. Never hash *
-     * PWD unless it was explicitly requested (or already hashed).   */
-    if (!always && (unset(AUTONAMEDIRS) || !strcmp(s, "PWD")) &&
+     * cause the actual creation of the hash table entry.            */
+    if (!always && unset(AUTONAMEDIRS) &&
 	    !nameddirtab->getnode2(nameddirtab, s))
 	return;
 
@@ -534,6 +534,9 @@ adduserdir(char *s, char *t, int flags, int always)
     nd = (Nameddir) zcalloc(sizeof *nd);
     nd->flags = flags;
     nd->dir = ztrdup(t);
+    /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */
+    if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD"))
+	nd->flags |= ND_NOABBREV;
     nameddirtab->addnode(nameddirtab, ztrdup(s), nd);
 }
 
@@ -1152,9 +1155,22 @@ checkrmall(char *s)
 
 /**/
 int
+read1char(void)
+{
+    char c;
+
+    while (read(SHTTY, &c, 1) != 1) {
+	if (errno != EINTR)
+	    return -1;
+    }
+    return STOUC(c);
+}
+
+/**/
+int
 getquery(char *valid_chars, int purge)
 {
-    char c, d;
+    int c, d;
     int isem = !strcmp(term, "emacs");
 
 #ifdef FIONREAD
@@ -1177,7 +1193,7 @@ getquery(char *valid_chars, int purge)
 	return 'n';
     }
 #endif
-    while (read(SHTTY, &c, 1) == 1) {
+    while ((c = read1char()) >= 0) {
 	if (c == 'Y' || c == '\t')
 	    c = 'y';
 	else if (c == 'N')
@@ -1199,13 +1215,13 @@ getquery(char *valid_chars, int purge)
     }
     if (isem) {
 	if (c != '\n')
-	    while (read(SHTTY, &d, 1) == 1 && d != '\n');
+	    while ((d = read1char()) >= 0 && d != '\n');
     } else {
 	settyinfo(&shttyinfo);
 	if (c != '\n' && !valid_chars)
 	    write(SHTTY, "\n", 1);
     }
-    return (int)c;
+    return c;
 }
 
 static int d;
@@ -3109,6 +3125,73 @@ hasspecial(char const *s)
     return 0;
 }
 
+/* Quote the string s and return the result.  If e is non-zero, the         *
+ * pointer it points to may point to a position in s and in e the position  *
+ * of the corresponding character in the quoted string is returned.  Like   *
+ * e, te may point to a position in the string and pl is used to return     *
+ * the position of the character pointed to by te in the quoted string.     *
+ * The last argument should be zero if this is to be used outside a string, *
+ * one if it is to be quoted for the inside of a single quoted string, and  *
+ * two if it is for the inside of  double quoted string.                    *
+ * The string may be metafied and contain tokens.                           */
+
+/**/
+char *
+bslashquote(const char *s, char **e, char *te, int *pl, int instring)
+{
+    const char *u, *tt;
+    char *v, buf[PATH_MAX * 2];
+    int sf = 0;
+
+    tt = v = buf;
+    u = s;
+    for (; *u; u++) {
+	if (e && *e == u)
+	    *e = v, sf |= 1;
+	if (te == u)
+	    *pl = v - tt, sf |= 2;
+	if (ispecial(*u) &&
+	    (!instring || (isset(BANGHIST) &&
+			   *u == (char)bangchar) ||
+	     (instring == 2 &&
+	      (*u == '$' || *u == '`' || *u == '\"')) ||
+	     (instring == 1 && *u == '\''))) {
+	    if (*u == '\n' || (instring == 1 && *u == '\'')) {
+		if (unset(RCQUOTES)) {
+		    *v++ = '\'';
+		    if (*u == '\'')
+			*v++ = '\\';
+		    *v++ = *u;
+		    *v++ = '\'';
+		} else if (*u == '\n')
+		    *v++ = '"', *v++ = '\n', *v++ = '"';
+		else
+		    *v++ = '\'', *v++ = '\'';
+		continue;
+	    } else
+		*v++ = '\\';
+	}
+	if(*u == Meta)
+	    *v++ = *u++;
+	*v++ = *u;
+    }
+    *v = '\0';
+    if (strcmp(buf, s))
+	tt = dupstring(buf);
+    else
+	tt = s;
+    v += tt - buf;
+    if (e && (sf & 1))
+	*e += tt - buf;
+
+    if (e && *e == u)
+	*e = v;
+    if (te == u)
+	*pl = v - tt;
+
+    return (char *) tt;
+}
+
 /* Unmetafy and output a string, quoted if it contains special characters. */
 
 /**/
@@ -3349,12 +3432,19 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
 	    case Meta:
 		*t++ = '\\', s--;
 		break;
+	    case '-':
+		if (fromwhere == 5) {
+		    *misc  = 1;
+		    break;
+		}
+		goto def;
 	    case 'c':
 		if (fromwhere < 2) {
 		    *misc = 1;
 		    break;
 		}
 	    default:
+	    def:
 		if ((idigit(*s) && *s < '8') || *s == 'x') {
 		    if (!fromwhere) {
 			if (*s == '0')
@@ -3386,7 +3476,7 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
 	} else if (fromwhere == 4 && *s == Snull) {
 	    for (u = t; (*u++ = *s++););
 	    return t + 1;
-	} else if (*s == '^' && fromwhere == 2) {
+	} else if (*s == '^' && (fromwhere == 2 || fromwhere == 5)) {
 	    control = 1;
 	    continue;
 	} else if (*s == Meta)
diff --git a/Src/zsh.export b/Src/zsh.export
index 32c0e7d3c..bfad7aea2 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -14,6 +14,7 @@ attachtty
 bangchar
 bin_notavail
 breaks
+bslashquote
 bufstack
 builtintab
 chline
@@ -55,6 +56,7 @@ exlast
 expanding
 fallback_compctlread
 fallback_zleread
+fetchvalue
 fignore
 file_type
 filesub
@@ -72,6 +74,7 @@ getkeystring
 getlinknode
 getshfunc
 getsparam
+getstrvalue
 gettempname
 glob_pre
 glob_suf
@@ -228,6 +231,7 @@ ugetnode
 uinsertlinknode
 unmeta
 unmetafy
+unsetparam
 unsetparam_pm
 untokenize
 uremnode
diff --git a/Src/zsh.h b/Src/zsh.h
index e23f9c895..87ec3e0c0 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -963,6 +963,7 @@ struct nameddir {
 /* flags for named directories */
 /* DISABLED is defined (1<<0) */
 #define ND_USERNAME	(1<<1)	/* nam is actually a username       */
+#define ND_NOABBREV	(1<<2)	/* never print as abbrev (PWD or OLDPWD) */
 
 
 /* flags for controlling printing of hash table nodes */
@@ -1056,6 +1057,7 @@ enum {
     BADPATTERN,
     BANGHIST,
     BAREGLOBQUAL,
+    BASHAUTOLIST,
     BEEP,
     BGNICE,
     BRACECCL,