about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/.distfiles7
-rw-r--r--Src/Modules/.distfiles1
-rw-r--r--Src/Modules/mapfile.c1
-rw-r--r--Src/Zle/.distfiles4
-rw-r--r--Src/Zle/comp.h118
-rw-r--r--Src/Zle/comp1.c18
-rw-r--r--Src/Zle/comp1.export5
-rw-r--r--Src/Zle/compctl.c131
-rw-r--r--Src/Zle/zle_hist.c19
-rw-r--r--Src/Zle/zle_main.c5
-rw-r--r--Src/Zle/zle_misc.c5
-rw-r--r--Src/Zle/zle_move.c2
-rw-r--r--Src/Zle/zle_refresh.c3
-rw-r--r--Src/Zle/zle_tricky.c649
-rw-r--r--Src/glob.c3
-rw-r--r--Src/hist.c6
-rw-r--r--Src/init.c42
-rw-r--r--Src/loop.c13
-rw-r--r--Src/main.c24
-rw-r--r--Src/options.c11
-rw-r--r--Src/subst.c15
-rw-r--r--Src/zsh.export2
-rw-r--r--Src/zsh.mdd3
23 files changed, 837 insertions, 250 deletions
diff --git a/Src/.distfiles b/Src/.distfiles
index 727c855cc..ce135a00e 100644
--- a/Src/.distfiles
+++ b/Src/.distfiles
@@ -1,11 +1,12 @@
 DISTFILES_SRC='
     .cvsignore .distfiles .exrc .indent.pro
     Makefile.in Makemod.in.in
-    ansi2knr.c
+    signames1.awk signames2.awk
+    ansi2knr.c modentry.c
     builtin.c compat.c cond.c exec.c glob.c hashtable.c hashtable.h
     hist.c init.c input.c jobs.c lex.c linklist.c loop.c main.c makepro.awk
     math.c mem.c mkbltnmlst.sh mkmakemod.sh mkmodindex.sh
     module.c options.c params.c parse.c prompt.c prototypes.h
-    signals.c signals.h signames.awk subst.c system.h text.c utils.c
-    watch.c xmods.conf zsh.h zsh.mdd ztype.h
+    signals.c signals.h subst.c system.h text.c utils.c
+    watch.c xmods.conf zsh.h zsh.mdd ztype.h zsh.export
 '
diff --git a/Src/Modules/.distfiles b/Src/Modules/.distfiles
index dbc152fa8..dc851bf9b 100644
--- a/Src/Modules/.distfiles
+++ b/Src/Modules/.distfiles
@@ -4,6 +4,7 @@ DISTFILES_SRC='
     clone.mdd clone.c
     example.mdd example.c
     files.mdd files.c
+    mapfile.mdd mapfile.c
     parameter.mdd parameter.c
     stat.mdd stat.c
     zftp.mdd zftp.c
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
index e98da7889..41cae80b8 100644
--- a/Src/Modules/mapfile.c
+++ b/Src/Modules/mapfile.c
@@ -149,6 +149,7 @@ setpmmapfile(Param pm, char *value)
 	 * pages.  Honestly, I tried it without, and you need both.
 	 */
 	ftruncate(fd, len);
+	munmap(mmptr, len);
     }
 #else /* don't USE_MMAP */
     /* can't be bothered to do anything too clever here */
diff --git a/Src/Zle/.distfiles b/Src/Zle/.distfiles
index 42c62efe9..9dbf27758 100644
--- a/Src/Zle/.distfiles
+++ b/Src/Zle/.distfiles
@@ -1,10 +1,10 @@
 DISTFILES_SRC='
     .cvsignore .distfiles .exrc
-    comp1.mdd comp.h comp1.c
+    comp1.mdd comp.h comp1.c comp1.export
     compctl.mdd compctl.c
     deltochar.mdd deltochar.c
     zle.mdd iwidgets.list zle.h zle_bindings.c zle_hist.c
     zle_keymap.c zle_main.c zle_misc.c zle_move.c zle_params.c
     zle_refresh.c zle_things.sed zle_thingy.c zle_tricky.c
-    zle_utils.c zle_vi.c zle_widget.sed zle_word.c
+    zle_utils.c zle_vi.c zle_widget.sed zle_word.c zle.export
 '
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index caeb4d6c3..bf3ea816f 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -91,6 +91,7 @@ struct compcond {
 #define CCT_NUMWORDS  10
 #define CCT_RANGESTR  11
 #define CCT_RANGEPAT  12
+#define CCT_QUOTE     13
 
 /* Contains the real description for compctls */
 
@@ -107,6 +108,7 @@ struct compctl {
     char *ylist;		/* for -y (user-defined desc. for listing) */
     char *prefix, *suffix;	/* for -P and -S (prefix, suffix)          */
     char *subcmd;		/* for -l (command name to use)            */
+    char *substr;		/* for -1 (command name to use)            */
     char *withd;		/* for -w (with directory                  */
     char *hpat;			/* for -H (history pattern)                */
     int hnum;			/* for -H (number of events to search)     */
@@ -209,6 +211,7 @@ struct cmatch {
     char *prpre;		/* path prefix for opendir */
     char *pre;			/* prefix string from -P */
     char *suf;			/* suffix string from -S */
+    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 */
@@ -297,42 +300,79 @@ struct cadata {
 
 /* Flags for special parameters. */
 
-#define CP_WORDS      (1 <<  0)
-#define CP_CURRENT    (1 <<  1)
-#define CP_PREFIX     (1 <<  2)
-#define CP_SUFFIX     (1 <<  3)
-#define CP_IPREFIX    (1 <<  4)
-#define CP_ISUFFIX    (1 <<  5)
-#define CP_COMPSTATE  (1 <<  6)
-
-#define CP_REALPARAMS        7
-
-#define CP_NMATCHES   (1 <<  7)
-#define CP_MATCHER    (1 <<  8)
-#define CP_MATCHERSTR (1 <<  9)
-#define CP_MATCHERTOT (1 << 10)
-#define CP_CONTEXT    (1 << 11)
-#define CP_PARAMETER  (1 << 12)
-#define CP_REDIRECT   (1 << 13)
-#define CP_QUOTE      (1 << 14)
-#define CP_QUOTING    (1 << 15)
-#define CP_RESTORE    (1 << 16)
-#define CP_LIST       (1 << 17)
-#define CP_FORCELIST  (1 << 18)
-#define CP_INSERT     (1 << 19)
-#define CP_EXACT      (1 << 20)
-#define CP_EXACTSTR   (1 << 21)
-#define CP_PATMATCH   (1 << 22)
-#define CP_PATINSERT  (1 << 23)
-#define CP_UNAMBIG    (1 << 24)
-#define CP_UNAMBIGC   (1 << 25)
-#define CP_LISTMAX    (1 << 26)
-#define CP_LASTPROMPT (1 << 27)
-#define CP_TOEND      (1 << 28)
-#define CP_OLDLIST    (1 << 29)
-#define CP_OLDINS     (1 << 30)
-#define CP_VARED      (1 << 31)
-
-#define CP_NUM              32
-
-#define CP_ALLMASK    ((unsigned int) 0xffffffff)
+#define CPN_WORDS      0
+#define CP_WORDS       (1 <<  CPN_WORDS)
+#define CPN_CURRENT    1
+#define CP_CURRENT     (1 <<  CPN_CURRENT)
+#define CPN_PREFIX     2
+#define CP_PREFIX      (1 <<  CPN_PREFIX)
+#define CPN_SUFFIX     3
+#define CP_SUFFIX      (1 <<  CPN_SUFFIX)
+#define CPN_IPREFIX    4
+#define CP_IPREFIX     (1 <<  CPN_IPREFIX)
+#define CPN_ISUFFIX    5
+#define CP_ISUFFIX     (1 <<  CPN_ISUFFIX)
+#define CPN_QIPREFIX   6
+#define CP_QIPREFIX    (1 <<  CPN_QIPREFIX)
+#define CPN_QISUFFIX   7
+#define CP_QISUFFIX    (1 <<  CPN_QISUFFIX)
+#define CPN_COMPSTATE  8
+#define CP_COMPSTATE   (1 <<  CPN_COMPSTATE)
+
+#define CP_REALPARAMS  9
+#define CP_ALLREALS    ((unsigned int) 0x1ff)
+
+
+#define CPN_NMATCHES   0
+#define CP_NMATCHES    (1 <<  CPN_NMATCHES)
+#define CPN_MATCHER    1
+#define CP_MATCHER     (1 <<  CPN_MATCHER)
+#define CPN_MATCHERSTR 2
+#define CP_MATCHERSTR  (1 <<  CPN_MATCHERSTR)
+#define CPN_MATCHERTOT 3
+#define CP_MATCHERTOT  (1 <<  CPN_MATCHERTOT)
+#define CPN_CONTEXT    4
+#define CP_CONTEXT     (1 <<  CPN_CONTEXT)
+#define CPN_PARAMETER  5
+#define CP_PARAMETER   (1 <<  CPN_PARAMETER)
+#define CPN_REDIRECT   6
+#define CP_REDIRECT    (1 <<  CPN_REDIRECT)
+#define CPN_QUOTE      7
+#define CP_QUOTE       (1 <<  CPN_QUOTE)
+#define CPN_QUOTING    8
+#define CP_QUOTING     (1 <<  CPN_QUOTING)
+#define CPN_RESTORE    9
+#define CP_RESTORE     (1 <<  CPN_RESTORE)
+#define CPN_LIST       10
+#define CP_LIST        (1 << CPN_LIST)
+#define CPN_FORCELIST  11
+#define CP_FORCELIST   (1 << CPN_FORCELIST)
+#define CPN_INSERT     12
+#define CP_INSERT      (1 << CPN_INSERT)
+#define CPN_EXACT      13
+#define CP_EXACT       (1 << CPN_EXACT)
+#define CPN_EXACTSTR   14
+#define CP_EXACTSTR    (1 << CPN_EXACTSTR)
+#define CPN_PATMATCH   15
+#define CP_PATMATCH    (1 << CPN_PATMATCH)
+#define CPN_PATINSERT  16
+#define CP_PATINSERT   (1 << CPN_PATINSERT)
+#define CPN_UNAMBIG    17
+#define CP_UNAMBIG     (1 << CPN_UNAMBIG)
+#define CPN_UNAMBIGC   18
+#define CP_UNAMBIGC    (1 << CPN_UNAMBIGC)
+#define CPN_LISTMAX    19
+#define CP_LISTMAX     (1 << CPN_LISTMAX)
+#define CPN_LASTPROMPT 20
+#define CP_LASTPROMPT  (1 << CPN_LASTPROMPT)
+#define CPN_TOEND      21
+#define CP_TOEND       (1 << CPN_TOEND)
+#define CPN_OLDLIST    22
+#define CP_OLDLIST     (1 << CPN_OLDLIST)
+#define CPN_OLDINS     23
+#define CP_OLDINS      (1 << CPN_OLDINS)
+#define CPN_VARED      24
+#define CP_VARED       (1 << CPN_VARED)
+
+#define CP_KEYPARAMS   25
+#define CP_ALLKEYS     ((unsigned int) 0xffffff)
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 071a8e64a..ba8bcc868 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -47,7 +47,7 @@ Cmlist cmatcher;
 void (*makecompparamsptr) _((void));
 
 /**/
-void (*comp_setunsetptr) _((unsigned int, unsigned int));
+void (*comp_setunsetptr) _((int, int, int, int));
 
 /* pointers to functions required by compctl and defined by zle */
 
@@ -69,6 +69,9 @@ int (*makecomplistctlptr) _((int));
 /**/
 char *(*unambig_dataptr) _((int *));
 
+/**/
+int (*set_comp_sepptr) _((void));
+
 /* Hash table for completion info for commands */
  
 /**/
@@ -112,6 +115,8 @@ char **compwords,
      *compsuffix,
      *compiprefix,
      *compisuffix,
+     *compqiprefix,
+     *compqisuffix,
      *compmatcherstr,
      *compcontext,
      *compparameter,
@@ -133,7 +138,7 @@ char **compwords,
      *compvared;
 
 /**/
-Param *comppms;
+Param *comprpms, *compkpms;
 
 /* The function rembslash() came from zle_tricky.c, but is now used *
  * in compctl.c, too.                                               */
@@ -193,6 +198,7 @@ freecompctl(Compctl cc)
     zsfree(cc->hpat);
     zsfree(cc->gname);
     zsfree(cc->subcmd);
+    zsfree(cc->substr);
     if (cc->cond)
 	freecompcond(cc->cond);
     if (cc->ext) {
@@ -440,9 +446,10 @@ setup_comp1(Module m)
     cc_first.refc = 10000;
     cc_first.mask = 0;
     cc_first.mask2 = CC_CCCONT;
-    comppms = NULL;
+    comprpms = compkpms = NULL;
     compwords = NULL;
-    compprefix = compsuffix = compiprefix = compisuffix = compmatcherstr = 
+    compprefix = compsuffix = compiprefix = compisuffix = 
+	compqiprefix = compqisuffix = compmatcherstr = 
 	compcontext = compparameter = compredirect = compquote =
 	compquoting = comprestore = complist = compinsert =
 	compexact = compexactstr = comppatmatch = comppatinsert =
@@ -450,6 +457,7 @@ setup_comp1(Module m)
 	compoldlist = compoldins = compvared = NULL;
     makecompparamsptr = NULL;
     comp_setunsetptr = NULL;
+    set_comp_sepptr = NULL;
     return 0;
 }
 
@@ -481,6 +489,8 @@ finish_comp1(Module m)
     zsfree(compsuffix);
     zsfree(compiprefix);
     zsfree(compisuffix);
+    zsfree(compqiprefix);
+    zsfree(compqisuffix);
     zsfree(compmatcherstr);
     zsfree(compcontext);
     zsfree(compparameter);
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 2dc285d3a..9b738cc78 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -19,6 +19,7 @@ compforcelist
 compinsert
 compiprefix
 compisuffix
+compkpms
 complastprompt
 complist
 complistmax
@@ -34,9 +35,12 @@ comppatmatch
 comppms
 compprefix
 compredirect
+compqiprefix
+compqisuffix
 compquote
 compquoting
 comprestore
+comprpms
 comp_setunsetptr
 comp_strptr
 compsuffix
@@ -56,4 +60,5 @@ makecomplistctlptr
 makecompparamsptr
 patcomps
 rembslash
+set_comp_sepptr
 unambig_dataptr
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index f644dfc38..c5b24714e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -688,6 +688,22 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
 		    *argv = "" - 1;
 		}
 		break;
+	    case 'h':
+		if (cl) {
+		    zerrnam(name, "illegal option -%c", NULL, **argv);
+		    return 1;
+		} else if ((*argv)[1]) {
+		    cct.substr = (*argv) + 1;
+		    *argv = "" - 1;
+		} else if (!argv[1]) {
+		    zwarnnam(name, "command name expected after -%c", NULL,
+			    **argv);
+		    return 1;
+		} else {
+		    cct.substr = *++argv;
+		    *argv = "" - 1;
+		}
+		break;
 	    case 'W':
 		if ((*argv)[1]) {
 		    cct.withd = (*argv) + 1;
@@ -933,6 +949,9 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 		t++;
 	    /* First get the condition code */
 	    switch (*t) {
+	    case 'q':
+		c->type = CCT_QUOTE;
+		break;
 	    case 's':
 		c->type = CCT_CURSUF;
 		break;
@@ -1017,7 +1036,8 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 		c->u.r.a = (int *)zcalloc(n * sizeof(int));
 		c->u.r.b = (int *)zcalloc(n * sizeof(int));
 	    } else if (c->type == CCT_CURSUF ||
-		       c->type == CCT_CURPRE)
+		       c->type == CCT_CURPRE ||
+		       c->type == CCT_QUOTE)
 		c->u.s.s = (char **)zcalloc(n * sizeof(char *));
 
 	    else if (c->type == CCT_RANGESTR ||
@@ -1059,7 +1079,8 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 			c->u.r.b[l] = atoi(tt);
 		    }
 		} else if (c->type == CCT_CURSUF ||
-			   c->type == CCT_CURPRE) {
+			   c->type == CCT_CURPRE ||
+			   c->type == CCT_QUOTE) {
 		    /* -s[..] or -S[..]:  single string expected */
 		    for (; *t && *t != '\200'; t++)
 			if (*t == '\201')
@@ -1218,6 +1239,7 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     zsfree(cc->prefix);
     zsfree(cc->suffix);
     zsfree(cc->subcmd);
+    zsfree(cc->substr);
     zsfree(cc->withd);
     zsfree(cc->hpat);
     zsfree(cc->gname);
@@ -1239,6 +1261,7 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     cc->prefix = ztrdup(cct->prefix);
     cc->suffix = ztrdup(cct->suffix);
     cc->subcmd = ztrdup(cct->subcmd);
+    cc->substr = ztrdup(cct->substr);
     cc->withd = ztrdup(cct->withd);
     cc->gname = ztrdup(cct->gname);
     cc->hpat = ztrdup(cct->hpat);
@@ -1366,7 +1389,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 {
     Compctl cc2;
     char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
-    char *mss = " pcCwWsSnNmrR";
+    char *mss = " pcCwWsSnNmrRq";
     unsigned long t = 0x7fffffff;
     unsigned long flags = cc->mask, flags2 = cc->mask2;
     unsigned long oldshowmask;
@@ -1448,6 +1471,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
     printif(cc->glob, 'g');
     printif(cc->str, 's');
     printif(cc->subcmd, 'l');
+    printif(cc->substr, 'h');
     printif(cc->withd, 'W');
     if (cc->hpat) {
 	printf(" -H %d ", cc->hnum);
@@ -1484,6 +1508,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 			    break;
 			case CCT_CURSUF:
 			case CCT_CURPRE:
+			case CCT_QUOTE:
 			    printqt(c->u.s.s[i]);
 			    break;
 			case CCT_RANGESTR:
@@ -2067,6 +2092,9 @@ bin_compset(char *name, char **argv, char *ops, int func)
     case 'P': test = CVT_PREPAT; break;
     case 's': test = CVT_SUFNUM; break;
     case 'S': test = CVT_SUFPAT; break;
+    case 'q': return !(compquote && *compquote &&
+		       (*compquote == '\'' || *compquote == '"') &&
+		       !set_comp_sepptr());
     default:
 	zerrnam(name, "bad option -%c", NULL, argv[0][1]);
 	return 1;
@@ -2135,19 +2163,25 @@ bin_compcall(char *name, char **argv, char *ops, int func)
  * order of the CP_* bits in comp.h */
 
 #define VAL(X) ((void *) (&(X)))
-static struct compparam {
+struct compparam {
     char *name;
     int type;
     void *var, *set, *get;
-} compparams[] = {
+};
+
+static struct compparam comprparams[] = {
     { "words", PM_ARRAY, VAL(compwords), NULL, NULL },
     { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL },
     { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL },
     { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL },
     { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL },
     { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL },
-    { NULL, 0, NULL, NULL, NULL },
+    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
+    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
+    { NULL, 0, NULL, NULL, NULL }
+};
 
+static struct compparam compkparams[] = {
     { "nmatches", PM_INTEGER, VAL(compnmatches), NULL, NULL },
     { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL },
     { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL },
@@ -2155,8 +2189,8 @@ static struct compparam {
     { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
     { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
     { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
-    { "quote", PM_SCALAR, VAL(compquote), NULL, NULL },
-    { "quoting", PM_SCALAR, VAL(compquoting), NULL, NULL },
+    { "quote", PM_SCALAR | PM_READONLY, VAL(compquote), NULL, NULL },
+    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
     { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
     { "list", PM_SCALAR, VAL(complist), NULL, NULL },
     { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL },
@@ -2179,11 +2213,9 @@ static struct compparam {
 
 #define COMPSTATENAME "compstate"
 
-static struct compparam *
-addcompparams(struct compparam *cp)
+static void
+addcompparams(struct compparam *cp, Param *pp)
 {
-    Param *pp = comppms + (cp - compparams);
-
     for (; cp->name; cp++, pp++) {
 	Param pm = createparam(cp->name, cp->type | PM_SPECIAL | PM_REMOVABLE);
 	if (!pm)
@@ -2214,31 +2246,29 @@ addcompparams(struct compparam *cp)
 	}
 	pm->unsetfn = compunsetfn;
     }
-    return cp;
 }
 
 /**/
 void
 makecompparams(void)
 {
-    struct compparam *cp;
     Param cpm;
     HashTable tht;
 
-    cp = addcompparams(compparams);
+    addcompparams(comprparams, comprpms);
 
     if (!(cpm = createparam(COMPSTATENAME, PM_SPECIAL|PM_REMOVABLE|PM_HASHED)))
 	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
     DPUTS(!cpm, "param not set in makecompparams");
 
-    comppms[cp - compparams] = cpm;
+    comprpms[CPN_COMPSTATE] = cpm;
     tht = paramtab;
     cpm->level = locallevel;
     cpm->gets.hfn = get_compstate;
     cpm->sets.hfn = set_compstate;
     cpm->unsetfn = compunsetfn;
-    cpm->u.hash = paramtab = newparamtable(17, COMPSTATENAME);
-    addcompparams(cp + 1);
+    cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
+    addcompparams(compkparams, compkpms);
     paramtab = tht;
 }
 
@@ -2262,8 +2292,8 @@ set_compstate(Param pm, HashTable ht)
 
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next)
-	    for (cp = compparams + CP_REALPARAMS,
-		 pp = comppms + CP_REALPARAMS; cp->name; cp++, pp++)
+	    for (cp = compkparams,
+		 pp = compkpms; cp->name; cp++, pp++)
 		if (!strcmp(hn->nam, cp->name)) {
 		    v.isarr = v.inv = v.a = 0;
 		    v.b = -1;
@@ -2318,20 +2348,25 @@ compunsetfn(Param pm, int exp)
 
 /**/
 void
-comp_setunset(unsigned int set, unsigned int unset)
+comp_setunset(int rset, int runset, int kset, int kunset)
 {
     Param *p;
 
-    if (!comppms)
-	return;
-
-    set &= CP_ALLMASK;
-    unset &= CP_ALLMASK;
-    for (p = comppms; set || unset; set >>= 1, unset >>= 1, p++) {
-	if (set & 1)
-	    (*p)->flags &= ~PM_UNSET;
-	if (unset & 1)
-	    (*p)->flags |= PM_UNSET;
+    if (comprpms && (rset >= 0 || runset >= 0)) {
+	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
+	    if (rset & 1)
+		(*p)->flags &= ~PM_UNSET;
+	    if (runset & 1)
+		(*p)->flags |= PM_UNSET;
+	}
+    }
+    if (comprpms && (kset >= 0 || kunset >= 0)) {
+	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
+	    if (kset & 1)
+		(*p)->flags &= ~PM_UNSET;
+	    if (kunset & 1)
+		(*p)->flags |= PM_UNSET;
+	}
     }
 }
 
@@ -2343,16 +2378,19 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	return 1;
     else {
 	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
+	char *oqipre, *oqisuf, *oq, *oqi;
 	zlong ocur;
-	unsigned int unset = 0, m, sm;
+	unsigned int runset = 0, kunset = 0, m, sm;
 	Param *pp;
 
 	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
-	    CP_IPREFIX | CP_ISUFFIX | CP_RESTORE;
-	for (pp = comppms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
+	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
+	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
 	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
-		unset |= sm;
+		runset |= sm;
 	}
+	if (compkpms[CPN_RESTORE]->flags & PM_UNSET)
+	    kunset = CP_RESTORE;
 	orest = comprestore;
 	comprestore = ztrdup("auto");
 	ocur = compcurrent;
@@ -2360,6 +2398,10 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	osuf = dupstring(compsuffix);
 	oipre = dupstring(compiprefix);
 	oisuf = dupstring(compisuffix);
+	oqipre = dupstring(compqiprefix);
+	oqisuf = dupstring(compqisuffix);
+	oq = dupstring(compquote);
+	oqi = dupstring(compquoting);
 
 	HEAPALLOC {
 	    owords = arrdup(compwords);
@@ -2377,18 +2419,27 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	    compiprefix = ztrdup(oipre);
 	    zsfree(compisuffix);
 	    compisuffix = ztrdup(oisuf);
+	    zsfree(compqiprefix);
+	    compqiprefix = ztrdup(oqipre);
+	    zsfree(compqisuffix);
+	    compqisuffix = ztrdup(oqisuf);
+	    zsfree(compquote);
+	    compquote = ztrdup(oq);
+	    zsfree(compquoting);
+	    compquoting = ztrdup(oqi);
 	    freearray(compwords);
 	    PERMALLOC {
 		compwords = arrdup(owords);
 	    } LASTALLOC;
 	    comp_setunset(CP_COMPSTATE |
-			  (~unset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
+			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
 				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
-				     CP_RESTORE)),
-			  unset);
+				     CP_QIPREFIX | CP_QISUFFIX)),
+			  (runset & CP_ALLREALS),
+			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
 	} else
-	    comp_setunset(CP_COMPSTATE | (~unset & CP_RESTORE),
-			  (unset & CP_RESTORE));
+	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
+			  (kunset & CP_RESTORE));
 	zsfree(comprestore);
 	comprestore = orest;
 
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index d2b9a3233..2fc55e98c 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -76,7 +76,8 @@ forget_edits(void)
 void
 uphistory(void)
 {
-    if (!zle_goto_hist(histline, -zmult) && isset(HISTBEEP))
+    int nodups = isset(HISTIGNOREDUPS);
+    if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
 	feep();
 }
 
@@ -267,7 +268,8 @@ acceptlineanddownhistory(void)
 void
 downhistory(void)
 {
-    if (!zle_goto_hist(histline, zmult) && isset(HISTBEEP))
+    int nodups = isset(HISTIGNOREDUPS);
+    if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
 	feep();
 }
 
@@ -370,7 +372,7 @@ beginningofbufferorhistory(void)
 void
 beginningofhistory(void)
 {
-    if (!zle_goto_hist(firsthist(), 0) && isset(HISTBEEP))
+    if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
 	feep();
 }
 
@@ -388,7 +390,7 @@ endofbufferorhistory(void)
 void
 endofhistory(void)
 {
-    zle_goto_hist(curhist, 0);
+    zle_goto_hist(curhist, 0, 0);
 }
 
 /**/
@@ -472,9 +474,14 @@ setlocalhistory(void)
 
 /**/
 int
-zle_goto_hist(int ev, int n)
+zle_goto_hist(int ev, int n, int skipdups)
 {
     Histent he = movehistent(quietgethist(ev), n, hist_skip_flags);
+    if (skipdups && n) {
+	n = n < 0? -1 : 1;
+	while (he && !metadiffer(ZLETEXT(he), (char *) line, ll))
+	    he = movehistent(he, n, hist_skip_flags);
+    }
     if (!he)
 	return 0;
     zle_setline(he);
@@ -906,7 +913,7 @@ vifetchhistory(void)
 	    return;
 	}
     }
-    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0) &&
+    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
 	isset(HISTBEEP))
 	feep();
 }
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index dfb437308..13f3cc402 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -640,7 +640,7 @@ execzlefunc(Thingy func)
 	    zsfree(msg);
 	    feep();
 	} else {
-	    int osc = sfcontext;
+	    int osc = sfcontext, osi = movefd(0);
 
 	    startparamscope();
 	    makezleparams(0);
@@ -650,6 +650,7 @@ execzlefunc(Thingy func)
 	    endparamscope();
 	    lastcmd = 0;
 	    r = 1;
+	    redup(osi, 0);
 	}
     }
     if (r) {
@@ -952,6 +953,7 @@ setup_zle(Module m)
     makecomplistcallptr = makecomplistcall;
     makecomplistctlptr = makecomplistctl;
     unambig_dataptr = unambig_data;
+    set_comp_sepptr = set_comp_sep;
 
     /* initialise the thingies */
     init_thingies();
@@ -1027,6 +1029,7 @@ finish_zle(Module m)
     makecomplistcallptr = NULL;
     makecomplistctlptr = NULL;
     unambig_dataptr = NULL;
+    set_comp_sepptr = NULL;
 
     return 0;
 }
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 30c31c358..c12c5894b 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -127,6 +127,7 @@ killwholeline(void)
 	for (i = cs; i != ll && line[i] != '\n'; i++);
 	forekill(i - cs + (i != ll), fg);
     }
+    clearlist = 1;
 }
 
 /**/
@@ -787,7 +788,9 @@ static char *suffixfunc;
 void
 makesuffix(int n)
 {
-    suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] = n;
+    suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] =
+	suffixlen[';'] = suffixlen['|'] = suffixlen['&'] = 
+	suffixlen['<'] = suffixlen['>'] = n;
 }
 
 /* Set up suffix for parameter names: the last n characters are a suffix *
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index 7169f5700..aec1c1767 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -476,7 +476,7 @@ vigotomark(void)
 	feep();
 	return;
     }
-    if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0)) {
+    if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
 	vimarkline[ch] = 0;
 	feep();
 	return;
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 9742ba501..ac18f52f0 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -268,10 +268,13 @@ zrefresh(void)
     if (clearlist && listshown) {
 	if (tccan(TCCLEAREOD)) {
 	    int ovln = vln, ovcs = vcs;
+	    char *nb = nbuf[vln];
 
+	    nbuf[vln] = obuf[vln];
 	    moveto(nlnct, 0);
 	    tcout(TCCLEAREOD);
 	    moveto(ovln, ovcs);
+	    nbuf[vln] = nb;
 	} else {
 	    invalidatelist();
 	    moveto(0, 0);
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 41ea12e77..281fe211d 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -63,6 +63,11 @@ dopestring;
 
 #define inststr(X) inststrlen((X),1,-1)
 
+/* The line before completion was tried. */
+
+static char *origline;
+static int origcs;
+
 /* wb and we hold the beginning/end position of the word we are completing. */
 
 static int wb, we;
@@ -177,6 +182,8 @@ static Comp patcomp, filecomp;
  * prpre       -- ppre in expanded form usable for opendir                 *
  * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
  * isuf        -- the ignored suffix                                       *
+ * qipre, qisuf-- ingnored quoted string                                   *
+ * autoq       -- quotes to automatically insert                           *
  *                                                                         *
  * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,       *
  * fpre, fsuf, lppre, and lpsuf.  noreal is non-zero if we have rpre/rsuf. */
@@ -188,6 +195,7 @@ static char *fpre, *fsuf;
 static char *ipre, *ripre;
 static char *isuf;
 static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
+static char *qipre, *qisuf, autoq;
 static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
 static int noreal;
 
@@ -559,7 +567,7 @@ acceptandmenucomplete(void)
 /* These are flags saying if we are completing in the command *
  * position, in a redirection, or in a parameter expansion.   */
 
-static int lincmd, linredir, ispar, linwhat, linarr;
+static int lincmd, linredir, ispar, parq, eparq, linwhat, linarr;
 
 /* The string for the redirection operator. */
 
@@ -673,21 +681,24 @@ cmphaswilds(char *str)
 /* Check if we have to complete a parameter name. */
 
 static char *
-check_param(char *s, int set)
+check_param(char *s, int set, char **ep)
 {
     char *p;
+    int bq = 0, eq = 0, i;
 
-    ispar = 0;
+    if (!ep)
+	ispar = parq = eparq = 0;
     /* Try to find a `$'. */
-    for (p = s + offs; p > s && *p != String; p--);
-    if (*p == String) {
+    for (p = s + offs; p > s && *p != String && *p != Qstring; p--);
+    if (*p == String || *p == Qstring) {
 	/* Handle $$'s */
-	while (p > s && p[-1] == String)
+	while (p > s && (p[-1] == String || p[-1] == Qstring))
 	    p--;
-	while (p[1] == String && p[2] == String)
+	while ((p[1] == String || p[1] == Qstring) &&
+	       (p[2] == String || p[2] == Qstring))
 	    p += 2;
     }
-    if (*p == String &&	p[1] != Inpar && p[1] != Inbrack) {
+    if ((*p == String || *p == Qstring) && p[1] != Inpar && p[1] != Inbrack) {
 	/* This is really a parameter expression (not $(...) or $[...]). */
 	char *b = p + 1, *e = b;
 	int n = 0, br = 1;
@@ -714,6 +725,12 @@ check_param(char *s, int set)
 	    b++;
 
 	e = b;
+	if (br) {
+	    while (*e == (ep ? Dnull : '"'))
+		e++, parq++, bq++;
+	    if (!ep)
+		b = e;
+	}
 	/* Find the end of the name. */
 	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
 	    *e == '?'   || *e == '*'  || *e == '$'    ||
@@ -730,22 +747,44 @@ check_param(char *s, int set)
 
 	/* Now make sure that the cursor is inside the name. */
 	if (offs <= e - s && offs >= b - s && n <= 0) {
+	    if (br) {
+		p = e;
+		while (*p == (ep ? Dnull : '"'))
+		    p++, parq--, eparq++, eq++;
+	    }
 	    /* It is. */
+	    if (ep) {
+		*ep = e;
+		return b;
+	    }
 	    /* If we were called from makecomplistflags(), we have to set the
 	     * global variables. */
+
 	    if (set) {
 		if (br >= 2)
 		    mflags |= CMF_PARBR;
 
 		/* Get the prefix (anything up to the character before the name). */
-		lpsuf = dupstring(quotename(e, NULL));
+		for (i = eq, p = e; i; i--, p++)
+		    *p = '.';
+		isuf = quotename(e, NULL);
+		for (i = eq, p = isuf; i; i--, p++)
+		    *p = '"';
 		*e = '\0';
-		lpsl = strlen(lpsuf);
 		ripre = dupstring(s);
 		ripre[b - s] = '\0';
-		ipre = dupstring(quotename(ripre, NULL));
+		for (i = bq, p = ripre + (b - s) - 1; i; i--, p--)
+		    *p = '.';
+		ipre = quotename(ripre, NULL);
+		for (i = bq, p = ripre + strlen(ripre) - 1; i; i--, p--)
+		    *p = '"';
+		for (i = bq, p = ipre + strlen(ipre) - 1; i; i--, p--)
+		    *p = '"';
 		untokenize(ipre);
 	    }
+	    else
+		parq = eparq = 0;
+
 	    /* And adjust wb, we, and offs again. */
 	    offs -= b - s;
 	    wb = cs - offs;
@@ -817,8 +856,15 @@ docomplete(int lst)
 	ol = NULL;
     inwhat = IN_NOTHING;
     qword = NULL;
+    zsfree(qipre);
+    qipre = ztrdup("");
+    zsfree(qisuf);
+    qisuf = ztrdup("");
+    autoq = '\0';
     /* Get the word to complete. */
     noerrs = 1;
+    origline = dupstring((char *) line);
+    origcs = cs;
     s = get_comp_string();
     DPUTS(wb < 0 || cs < wb || cs > we,
 	  "BUG: 0 <= wb <= cs <= we is not true!");
@@ -1152,7 +1198,8 @@ get_comp_string(void)
     noaliases = isset(COMPLETEALIASES);
 
     /* Find out if we are somewhere in a `string', i.e. inside '...', *
-     * "...", `...`, or ((...)).                                      */
+     * "...", `...`, or ((...)). Nowadays this is only used to find   *
+     * out if we are inside `...`.                                    */
 
     for (i = j = k = 0, p = (char *)line; p < (char *)line + cs; p++)
 	if (*p == '`' && !(k & 1))
@@ -1163,28 +1210,9 @@ get_comp_string(void)
 	    k++;
 	else if (*p == '\\' && p[1] && !(k & 1))
 	    p++;
-    instring = (j & 1) ? 2 : (k & 1);
     inbackt = (i & 1);
+    instring = 0;
     addx(&tmp);
-    if (instring) {
-	/* Yes, we are in a string. */
-	if (!tmp) {
-	    tmp = (char *)line;
-	    line = (unsigned char *) dupstring((char *) line);
-	}
-	/* Now remove the quotes.                                   *
-	 * What??  Why that??  Well, we want to be able to complete *
-	 * inside strings.  The lexer code gives us no help here,   *
-	 * so we have to cheat.  We remove the quotes, the lexer    *
-	 * will then treat the words in the strings normally and we *
-	 * can complete them.                                       *
-	 * This is completely the wrong thing to do, but it's       *
-	 * occasionally useful, and we can't handle quotes properly *
-	 * yet anyway.                                              */
-	for (p = (char *)line; *p; p++)
-	    if (*p == '"' || *p == '\'')
-		*p = ' ';
-    }
     linptr = (char *)line;
     pushheap();
     HEAPALLOC {
@@ -1227,6 +1255,16 @@ get_comp_string(void)
 	    if (linarr)
 		incmdpos = 0;
 	    ctxtlex();
+
+	    if (tok == LEXERR) {
+		if (!tokstr)
+		    break;
+		for (j = 0, p = tokstr; *p; p++)
+		    if (*p == Snull || *p == Dnull)
+			j++;
+		if (j & 1)
+		    tok = STRING;
+	    }
 	    if (tok == ENVARRAY) {
 		linarr = 1;
 		zsfree(varname);
@@ -1496,8 +1534,28 @@ get_comp_string(void)
 	}
 	/* This variable will hold the current word in quoted form. */
 	qword = ztrdup(s);
-	/* While building the quoted form, we also clean up the command line. */
 	offs = cs - wb;
+	if ((p = check_param(s, 0, &tt))) {
+	    for (; *p == Dnull; p++)
+		*p = '"';
+	    for (; *tt == Dnull; tt++)
+		*tt = '"';
+	}
+	if (*s == Snull || *s == Dnull) {
+	    char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, "");
+	    int sl = strlen(s);
+
+	    instring = (*s == Snull ? 1 : 2);
+	    zsfree(qipre);
+	    qipre = n;
+	    if (sl > 1 && s[sl - 1] == *s) {
+		n = tricat(q, qisuf, "");
+		zsfree(qisuf);
+		qisuf = n;
+	    }
+	    autoq = *q;
+	}
+	/* While building the quoted form, we also clean up the command line. */
 	for (p = s, tt = qword, i = wb; *p; p++, tt++, i++)
 	    if (INULL(*p)) {
 		if (i < cs)
@@ -3367,7 +3425,9 @@ add_match_data(int alt, char *str, Cline line,
 {
     Cmatch cm;
     Aminfo ai = (alt ? fainfo : ainfo);
-    int palen = 0, salen = 0, ipl = 0, pl = 0, ppl = 0, isl = 0, psl = 0;
+    int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
+
+    palen = salen = qipl = ipl = pl = ppl = isl = psl = 0;
 
     DPUTS(!line, "BUG: add_match_data() without cline");
 
@@ -3377,41 +3437,60 @@ add_match_data(int alt, char *str, Cline line,
 	salen = (psl = strlen(psuf));
     if (isuf)
 	salen += (isl = strlen(isuf));
+    if (qisuf)
+	salen += (qisl = strlen(qisuf));
 
     if (salen) {
 	char *asuf = (char *) zhalloc(salen);
-	Cline pp, p, s;
+	Cline pp, p, s, sl = NULL;
 
 	if (psl)
 	    memcpy(asuf, psuf, psl);
 	if (isl)
 	    memcpy(asuf + psl, isuf, isl);
-
-	s = bld_parts(asuf, salen, salen, NULL);
+	if (qisl)
+	    memcpy(asuf + psl + isl, qisuf, qisl);
 
 	for (pp = NULL, p = line; p->next; pp = p, p = p->next);
 
-	if (!(p->flags & (CLF_SUF | CLF_MID)) &&
-	    !p->llen && !p->wlen && !p->olen) {
-	    if (p->prefix) {
-		Cline q;
+	if (salen > qisl) {
+	    s = bld_parts(asuf, salen - qisl, salen - qisl, &sl);
 
-		for (q = p->prefix; q->next; q = q->next);
-		q->next = s->prefix;
-		s->prefix = p->prefix;
-		p->prefix = NULL;
-	    }
-	    free_cline(p);
-	    if (pp)
-		pp->next = s;
+	    if (!(p->flags & (CLF_SUF | CLF_MID)) &&
+		!p->llen && !p->wlen && !p->olen) {
+		if (p->prefix) {
+		    Cline q;
+
+		    for (q = p->prefix; q->next; q = q->next);
+		    q->next = s->prefix;
+		    s->prefix = p->prefix;
+		    p->prefix = NULL;
+		}
+		free_cline(p);
+		if (pp)
+		    pp->next = s;
+		else
+		    line = s;
+	    } else
+		p->next = s;
+	}
+	if (qisl) {
+	    Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL);
+
+	    qsl->flags |= CLF_SUF;
+	    qsl->suffix = qsl->prefix;
+	    qsl->prefix = NULL;
+	    if (sl)
+		sl->next = qsl;
 	    else
-		line = s;
-	} else
-	    p->next = s;
+		p->next = qsl;
+	}
     }
     /* And the same for the prefix. */
+    if (qipre)
+	palen = (qipl = strlen(qipre));
     if (ipre)
-	palen = (ipl = strlen(ipre));
+	palen += (ipl = strlen(ipre));
     if (pre)
 	palen += (pl = strlen(pre));
     if (ppre)
@@ -3421,12 +3500,14 @@ add_match_data(int alt, char *str, Cline line,
 	char *apre = (char *) zhalloc(palen);
 	Cline p, lp;
 
+	if (qipl)
+	    memcpy(apre, qipre, qipl);
 	if (ipl)
-	    memcpy(apre, ipre, ipl);
+	    memcpy(apre + qipl, ipre, ipl);
 	if (pl)
-	    memcpy(apre + ipl, pre, pl);
+	    memcpy(apre + qipl + ipl, pre, pl);
 	if (ppl)
-	    memcpy(apre + ipl + pl, ppre, ppl);
+	    memcpy(apre + qipl + ipl + pl, ppre, ppl);
 
 	p = bld_parts(apre, palen, palen, &lp);
 	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID))) {
@@ -3461,14 +3542,21 @@ add_match_data(int alt, char *str, Cline line,
     cm->ppre = (ppre && *ppre ? ppre : NULL);
     cm->psuf = (psuf && *psuf ? psuf : NULL);
     cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
-    cm->ipre = (ipre && *ipre ? ipre : NULL);
+    if (qipre && *qipre)
+	cm->ipre = (ipre && *ipre ? dyncat(qipre, ipre) : dupstring(qipre));
+    else
+	cm->ipre = (ipre && *ipre ? ipre : NULL);
     cm->ripre = (ripre && *ripre ? ripre : NULL);
-    cm->isuf = (isuf && *isuf ? isuf : NULL);
+    if (qisuf && *qisuf)
+	cm->isuf = (isuf && *isuf ? dyncat(isuf, qisuf) : dupstring(qisuf));
+    else
+	cm->isuf = (isuf && *isuf ? isuf : NULL);
     cm->pre = pre;
     cm->suf = suf;
     cm->flags = flags;
     cm->brpl = bpl;
     cm->brsl = bsl;
+    cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
     cm->rems = cm->remf = NULL;
     addlinknode((alt ? fmatches : matches), cm);
 
@@ -3505,14 +3593,14 @@ add_match_data(int alt, char *str, Cline line,
 		e += sl;
 		if (cm->psuf)
 		    strcpy(e, cm->psuf);
-		comp_setunsetptr(CP_EXACTSTR, 0);
+		comp_setunsetptr(0, 0, CP_EXACTSTR, 0);
 	    }
 	    ai->exactm = cm;
 	} else {
 	    ai->exact = 2;
 	    ai->exactm = NULL;
 	    if (incompfunc)
-		comp_setunsetptr(0, CP_EXACTSTR);
+		comp_setunsetptr(0, 0, 0, CP_EXACTSTR);
 	}
     }
     return cm;
@@ -3543,9 +3631,10 @@ int
 addmatches(Cadata dat, char **argv)
 {
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
-    char **aign = NULL, **dparr = NULL;
+    char **aign = NULL, **dparr = NULL, oaq = autoq;
+    char *oqp = qipre, *oqs = qisuf, qc;
     int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
-    int oisalt = 0, isalt, isexact, doadd;
+    int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
     Cline lc = NULL;
     Cmatch cm;
     struct cmlist mst;
@@ -3553,6 +3642,23 @@ addmatches(Cadata dat, char **argv)
     Comp cp = NULL;
     LinkList aparl = NULL, oparl = NULL, dparl = NULL;
 
+    if (compquote && (qc = *compquote)) {
+	if (qc == '`') {
+	    instring = 0;
+	    inbackt = 0;
+	    autoq = '\0';
+	} else {
+	    instring = (qc == '\'' ? 1 : 2);
+	    inbackt = 0;
+	    autoq = qc;
+	}
+    } else {
+	instring = inbackt = 0;
+	autoq = '\0';
+    }
+    qipre = ztrdup(compqiprefix ? compqiprefix : "");
+    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
+
     /* Switch back to the heap that was used when the completion widget
      * was invoked. */
     SWITCHHEAPS(compheap) {
@@ -3778,6 +3884,14 @@ addmatches(Cadata dat, char **argv)
      * matchers. */
     mstack = oms;
 
+    instring = ois;
+    inbackt = oib;
+    autoq = oaq;
+    zsfree(qipre);
+    zsfree(qisuf);
+    qipre = oqp;
+    qisuf = oqs;
+
     return (mnum == nm);
 }
 
@@ -4222,6 +4336,10 @@ docompletion(char *s, int lst, int incmd)
 	/* Make sure we have the completion list and compctl. */
 	if (makecomplist(s, incmd, lst)) {
 	    /* Error condition: feeeeeeeeeeeeep(). */
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
 	    feep();
 	    clearlist = 1;
 	    goto compend;
@@ -4308,23 +4426,25 @@ callcompfunc(char *s, char *fn)
     if ((list = getshfunc(fn)) != &dummy_list) {
 	char **p, *tmp;
 	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
-	unsigned int set;
-	Param *ocpms = comppms;
+	unsigned int rset, kset;
+	Param *ocrpms = comprpms, *ockpms = compkpms;
 
-	comppms = (Param *) zalloc(CP_NUM * sizeof(Param));
+	comprpms = (Param *) zalloc(CP_REALPARAMS * sizeof(Param));
+	compkpms = (Param *) zalloc(CP_KEYPARAMS * sizeof(Param));
 
-	set = CP_ALLMASK &
+	rset = CP_ALLREALS;
+	kset = CP_ALLKEYS &
 	    ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
 	      CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS |
 	      (useglob ? 0 : CP_PATMATCH));
 	zsfree(compvared);
 	if (varedarg) {
 	    compvared = ztrdup(varedarg);
-	    set |= CP_VARED;
+	    kset |= CP_VARED;
 	} else
 	    compvared = ztrdup("");
 	if (!*complastprompt)
-	    set &= ~CP_LASTPROMPT;
+	    kset &= ~CP_LASTPROMPT;
 	zsfree(compcontext);
 	zsfree(compparameter);
 	zsfree(compredirect);
@@ -4336,7 +4456,7 @@ callcompfunc(char *s, char *fn)
 		compcontext = "subscript";
 		if (varname) {
 		    compparameter = varname;
-		    set |= CP_PARAMETER;
+		    kset |= CP_PARAMETER;
 		}
 	    } else
 		compcontext = "math";
@@ -4344,20 +4464,20 @@ callcompfunc(char *s, char *fn)
 	} else if (lincmd) {
 	    if (insubscr) {
 		compcontext = "subscript";
-		set |= CP_PARAMETER;
+		kset |= CP_PARAMETER;
 	    } else
 		compcontext = "command";
 	} else if (linredir) {
 	    compcontext = "redirect";
 	    if (rdstr)
 		compredirect = rdstr;
-	    set |= CP_REDIRECT;
+	    kset |= CP_REDIRECT;
 	} else
 	    switch (linwhat) {
 	    case IN_ENV:
 		compcontext = (linarr ? "array_value" : "value");
 		compparameter = varname;
-		set |= CP_PARAMETER;
+		kset |= CP_PARAMETER;
 		if (!clwpos) {
 		    clwpos = 1;
 		    zsfree(clwords[1]);
@@ -4373,7 +4493,7 @@ callcompfunc(char *s, char *fn)
 		    compcontext = "command";
 		else {
 		    compcontext = "value";
-		    set |= CP_PARAMETER;
+		    kset |= CP_PARAMETER;
 		    if (clwords[0])
 			compparameter = clwords[0];
 		    aadd = 1;
@@ -4410,10 +4530,11 @@ callcompfunc(char *s, char *fn)
 		compquote = ztrdup("\"");
 		compquoting = ztrdup("double");
 	    }
-	    set |= CP_QUOTE | CP_QUOTING;
+	    kset |= CP_QUOTE | CP_QUOTING;
 	} else if (inbackt) {
 	    compquote = ztrdup("`");
 	    compquoting = ztrdup("backtick");
+	    kset |= CP_QUOTE | CP_QUOTING;
 	} else {
 	    compquote = ztrdup("");
 	    compquoting = ztrdup("");
@@ -4444,12 +4565,16 @@ callcompfunc(char *s, char *fn)
 	compiprefix = ztrdup("");
 	zsfree(compisuffix);
 	compisuffix = ztrdup("");
+	zsfree(compqiprefix);
+	compqiprefix = ztrdup(qipre ? qipre : "");
+	zsfree(compqisuffix);
+	compqisuffix = ztrdup(qisuf ? qisuf : "");
 	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
 	compnmatches = mnum;
 
 	zsfree(complist);
 	switch (uselist) {
-	case 0: complist = ""; set &= ~CP_LIST; break;
+	case 0: complist = ""; kset &= ~CP_LIST; break;
 	case 1: complist = "list"; break;
 	case 2: complist = "autolist"; break;
 	case 3: complist = "ambiguous"; break;
@@ -4464,14 +4589,14 @@ callcompfunc(char *s, char *fn)
 	    }
 	} else {
 	    compinsert = "";
-	    set &= ~CP_INSERT;
+	    kset &= ~CP_INSERT;
 	}
 	compinsert = ztrdup(compinsert);
 	if (useexact)
 	    compexact = ztrdup("accept");
 	else {
 	    compexact = ztrdup("");
-	    set &= ~CP_EXACT;
+	    kset &= ~CP_EXACT;
 	}
 	zsfree(comptoend);
 	if (movetoend == 1)
@@ -4485,11 +4610,11 @@ callcompfunc(char *s, char *fn)
 		compoldlist = "shown";
 	    else
 		compoldlist = "yes";
-	    set |= CP_OLDLIST;
+	    kset |= CP_OLDLIST;
 	    if (menucur) {
 		sprintf(buf, "%d", (*menucur)->gnum);
 		compoldins = buf;
-		set |= CP_OLDINS;
+		kset |= CP_OLDINS;
 	    } else
 		compoldins = "";
 	} else
@@ -4500,7 +4625,8 @@ callcompfunc(char *s, char *fn)
 	incompfunc = 1;
 	startparamscope();
 	makecompparamsptr();
-	comp_setunsetptr(set, ~set);
+	comp_setunsetptr(rset, (~rset & CP_ALLREALS),
+			 kset, (~kset & CP_ALLKEYS));
 	makezleparams(1);
 	sfcontext = SFC_CWIDGET;
 	NEWHEAPS(compheap) {
@@ -4557,8 +4683,10 @@ callcompfunc(char *s, char *fn)
 	oldins = (hasperm && menucur &&
 		  compoldins && !strcmp(compoldins, "keep"));
 
-	zfree(comppms, CP_NUM * sizeof(Param));
-	comppms = ocpms;
+	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
+	zfree(compkpms, CP_KEYPARAMS * sizeof(Param));
+	comprpms = ocrpms;
+	compkpms = ockpms;
     }
     lastval = lv;
 }
@@ -4586,10 +4714,10 @@ makecomplist(char *s, int incmd, int lst)
     struct cmlist ms;
     Cmlist m;
     char *p, *os = s;
-    int onm = nmatches;
+    int onm = nmatches, osi = movefd(0);
 
     /* Inside $... ? */
-    if (compfunc && (p = check_param(s, 0)))
+    if (compfunc && (p = check_param(s, 0, NULL)))
 	os = s = p;
 
     /* We build a copy of the list of matchers to use to make sure that this
@@ -4676,6 +4804,8 @@ makecomplist(char *s, int incmd, int lst)
 	    validlist = 1;
 	    amatches = pmatches;
 
+	    redup(osi, 0);
+
 	    return 0;
 	}
 	PERMALLOC {
@@ -4685,6 +4815,8 @@ makecomplist(char *s, int incmd, int lst)
 	if (nmatches && !errflag) {
 	    validlist = 1;
 
+	    redup(osi, 0);
+
 	    return 0;
 	}
 	if (!m || !(m = m->next))
@@ -4693,6 +4825,7 @@ makecomplist(char *s, int incmd, int lst)
 	errflag = 0;
 	compmatcher++;
     }
+    redup(osi, 0);
     return 1;
 }
 
@@ -4760,25 +4893,279 @@ comp_str(int *ipl, int *pl, int untok)
 
 /**/
 int
+set_comp_sep(void)
+{
+    int lip, lp;
+    char *s = comp_str(&lip, &lp, 0);
+
+    if (compisuffix)
+	s = dyncat(s, compisuffix);
+
+    return sep_comp_string("", s, lip + lp, 0);
+}
+
+/**/
+static int
+sep_comp_string(char *ss, char *s, int noffs, int rec)
+{
+    LinkList foo = newlinklist();
+    LinkNode n;
+    int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
+    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll;
+    int ois = instring, oib = inbackt;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+
+    /* Put the string in the lexer buffer and call the lexer to *
+     * get the words we have to expand.                        */
+    zleparse = 1;
+    addedx = 1;
+    noerrs = 1;
+    lexsave();
+    tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
+    strcpy(tmp, ss);
+    tmp[sl] = ' ';
+    memcpy(tmp + sl + 1, s, noffs);
+    tmp[(scs = cs = sl + 1 + noffs)] = 'x';
+    strcpy(tmp + sl + 2 + noffs, s + noffs);
+    inpush(dupstrspace(tmp), 0, NULL);
+    line = (unsigned char *) tmp;
+    ll = tl - 1;
+    strinbeg();
+    stophist = 2;
+    noaliases = 1;
+    do {
+	ctxtlex();
+	if (tok == LEXERR) {
+	    int j;
+
+	    if (!tokstr)
+		break;
+	    for (j = 0, p = tokstr; *p; p++)
+		if (*p == Snull || *p == Dnull)
+		    j++;
+	    if (j & 1) {
+		tok = STRING;
+		if (p > tokstr && p[-1] == ' ')
+		    p[-1] = '\0';
+	    }
+	}
+	if (tok == ENDINPUT || tok == LEXERR)
+	    break;
+	if (tokstr && *tokstr)
+	    addlinknode(foo, (p = ztrdup(tokstr)));
+	else
+	    p = NULL;
+	if (!got && !zleparse) {
+	    DPUTS(!p, "no current word in substr");
+	    cur = i;
+	    swb = wb - 1;
+	    swe = we - 1;
+	    soffs = cs - swb;
+	    chuck(p + soffs);
+	    ns = dupstring(p);
+	}
+	i++;
+    } while (tok != ENDINPUT && tok != LEXERR);
+    noaliases = 0;
+    strinend();
+    inpop();
+    errflag = zleparse = 0;
+    noerrs = ne;
+    lexrestore();
+    wb = owb;
+    we = owe;
+    cs = ocs;
+    line = (unsigned char *) ol;
+    ll = oll;
+    if (cur < 0 || i < 1)
+	return 1;
+    if (*ns == Snull || *ns == Dnull) {
+	instring = (*ns == Snull ? 1 : 2);
+	inbackt = 0;
+	swb++;
+	if (ns[strlen(ns) - 1] == *ns)
+	    swe--;
+	autoq = (*ns == Snull ? '\'' : '"');
+    } else {
+	instring = 0;
+	autoq = '\0';
+    }
+    for (p = ns, i = swb; *p; p++, i++) {
+	if (INULL(*p)) {
+	    if (i < scs)
+		soffs--;
+	    if (p[1] || *p != Bnull) {
+		if (*p == Bnull) {
+		    if (scs == i + 1)
+			scs++, soffs++;
+		} else {
+		    if (scs > i--)
+			scs--;
+		}
+	    } else {
+		if (scs == swe)
+		    scs--;
+	    }
+	    chuck(p--);
+	}
+    }
+    sav = s[(i = swb - sl - 1)];
+    s[i] = '\0';
+    qp = tricat(qipre, s, "");
+    s[i] = sav;
+    if (swe < swb)
+	swe = swb;
+    swe -= sl + 1;
+    sl = strlen(s);
+    if (swe > sl)
+	swe = sl, ns[swe - swb + 1] = '\0';
+    qs = tricat(s + swe, qisuf, "");
+    sl = strlen(ns);
+    if (soffs > sl)
+	soffs = sl;
+
+    if (rec) {
+	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
+	int olws = clwsize, olwn = clwnum, olwp = clwpos;
+	int obr = brange, oer = erange, oof = offs;
+	
+	clwsize = clwnum = countlinknodes(foo);
+	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+	    p = clwords[i] = (char *) getdata(n);
+	    untokenize(p);
+	}
+	clwords[i] = NULL;
+	clwpos = cur;
+	cmdstr = ztrdup(clwords[0]);
+	brange = 0;
+	erange = clwnum - 1;
+	qipre = qp;
+	qisuf = qs;
+	offs = soffs;
+	makecomplistcmd(ns, !clwpos, CFN_FIRST);
+	offs = oof;
+	zsfree(cmdstr);
+	cmdstr = os;
+	freearray(clwords);
+	clwords = ow;
+	clwsize = olws;
+	clwnum = olwn;
+	clwpos = olwp;
+	brange = obr;
+	erange = oer;
+	zsfree(qipre);
+	qipre = oqp;
+	zsfree(qisuf);
+	qisuf = oqs;
+    } else {
+	int set = CP_QUOTE | CP_QUOTING, unset = 0;
+
+	zsfree(compquote);
+	zsfree(compquoting);
+	if (instring == 2) {
+	    compquote = "\"";
+	    compquoting = "double";
+	} else if (instring == 1) {
+	    compquote = "'";
+	    compquoting = "single";
+	} else {
+	    compquote = compquoting = "";
+	    unset = set;
+	    set = 0;
+	}
+	compquote = ztrdup(compquote);
+	compquoting = ztrdup(compquoting);
+	comp_setunsetptr(0, 0, set, unset);
+
+	if (unset(COMPLETEINWORD)) {
+	    untokenize(ns);
+	    zsfree(compprefix);
+	    compprefix = ztrdup(ns);
+	    zsfree(compsuffix);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss, sav;
+	    
+	    ss = ns + soffs;
+
+	    sav = *ss;
+	    *ss = '\0';
+	    untokenize(ns);
+	    compprefix = ztrdup(ns);
+	    *ss = sav;
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	zsfree(compisuffix);
+	compisuffix = ztrdup("");
+	zsfree(compqiprefix);
+	compqiprefix = qp;
+	zsfree(compqisuffix);
+	compqisuffix = qs;
+
+	freearray(compwords);
+	i = countlinknodes(foo);
+	compwords = (char **) zalloc((i + 1) * sizeof(char *));
+	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+	    p = compwords[i] = (char *) getdata(n);
+	    untokenize(p);
+	}
+	compwords[i] = NULL;
+    }
+    autoq = oaq;
+    instring = ois;
+    inbackt = oib;
+
+    return 0;
+}
+
+/**/
+int
 makecomplistcall(Compctl cc)
 {
     int nm = mnum;
 
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
-	    int ooffs = offs, lip, lp;
-	    char *str = comp_str(&lip, &lp, 0);
-	    char *oisuf = isuf;
-
+	    int ooffs = offs, lip, lp, ois = instring, oib = inbackt;
+	    char *str = comp_str(&lip, &lp, 0), qc;
+	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
+
+	    if (compquote && (qc = *compquote)) {
+		if (qc == '`') {
+		    instring = 0;
+		    inbackt = 0;
+		    autoq = '\0';
+		} else {
+		    instring = (qc == '\'' ? 1 : 2);
+		    inbackt = 0;
+		    autoq = qc;
+		}
+	    } else {
+		instring = inbackt = 0;
+		autoq = '\0';
+	    }
 	    isuf = dupstring(compisuffix);
 	    ctokenize(isuf);
 	    remnulargs(isuf);
+	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
+	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
 	    offs = lip + lp;
 	    cc->refc++;
 	    ccont = 0;
 	    makecomplistor(cc, str, lincmd, lip, 0);
 	    offs = ooffs;
 	    isuf = oisuf;
+	    zsfree(qipre);
+	    zsfree(qisuf);
+	    qipre = oqp;
+	    qisuf = oqs;
+	    instring = ois;
+	    inbackt = oib;
+	    autoq = oaq;
 	    compnmatches = mnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
@@ -4807,10 +5194,26 @@ makecomplistctl(int flags)
 	HEAPALLOC {
 	    int ooffs = offs, lip, lp;
 	    char *str = comp_str(&lip, &lp, 0), *t;
-	    char *os = cmdstr, **ow = clwords, **p, **q;
-	    int on = clwnum, op = clwpos;
-	    char *oisuf = isuf;
-
+	    char *os = cmdstr, **ow = clwords, **p, **q, qc;
+	    int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
+	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
+
+	    if (compquote && (qc = *compquote)) {
+		if (qc == '`') {
+		    instring = 0;
+		    inbackt = 0;
+		    autoq = '\0';
+		} else {
+		    instring = (qc == '\'' ? 1 : 2);
+		    inbackt = 0;
+		    autoq = qc;
+		}
+	    } else {
+		instring = inbackt = 0;
+		autoq = '\0';
+	    }
+	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
+	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
 	    isuf = dupstring(compisuffix);
 	    ctokenize(isuf);
 	    remnulargs(isuf);
@@ -4830,6 +5233,13 @@ makecomplistctl(int flags)
 	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
 	    incompfunc = 1;
 	    isuf = oisuf;
+	    zsfree(qipre);
+	    zsfree(qisuf);
+	    qipre = oqp;
+	    qisuf = oqs;
+	    instring = ois;
+	    inbackt = oib;
+	    autoq = oaq;
 	    offs = ooffs;
 	    compnmatches = mnum;
 	    zsfree(cmdstr);
@@ -5078,9 +5488,11 @@ makecomplistext(Compctl occ, char *os, int incmd)
     Compctl compc;
     Compcond or, cc;
     Comp comp;
-    int compadd, m = 0, d = 0, t, tt, i, j, a, b;
+    int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
     char *sc = NULL, *s, *ss;
 
+    ins = (instring ? instring : (inbackt ? 3 : 0));
+
     /* This loops over the patterns separated by `-'s. */
     for (compc = occ->ext; compc; compc = compc->next) {
 	compadd = t = brange = 0;
@@ -5096,6 +5508,11 @@ makecomplistext(Compctl occ, char *os, int incmd)
 		    brange = 0;
 		    erange = clwnum - 1;
 		    switch (cc->type) {
+		    case CCT_QUOTE:
+			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
+			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
+			     (cc->u.s.s[i][0] == 'b' && ins == 3));
+			break;
 		    case CCT_POS:
 			tt = clwpos;
 			goto cct_num;
@@ -5366,7 +5783,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	ic = 0;
 
     /* Check if we have to complete a parameter name... */
-    if (!incompfunc && (p = check_param(s, 1))) {
+    if (!incompfunc && (p = check_param(s, 1, NULL))) {
 	s = p;
 	/* And now make sure that we complete parameter names. */
 	cc = &cc_dummy;
@@ -5917,7 +6334,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 	opts[NULLGLOB] = 1;
 
-	/* Put the strin in the lexer buffer and call the lexer to *
+	/* Put the string in the lexer buffer and call the lexer to *
 	 * get the words we have to expand.                        */
 	zleparse = 1;
 	lexsave();
@@ -6127,6 +6544,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    clwords[brange - 1] = ops;
 	ccont = occ;
     }
+    if (cc->substr)
+	sep_comp_string(cc->substr, s, offs, 1);
     uremnode(ccstack, firstnode(ccstack));
     if (cc->matcher)
 	mstack = mstack->next;
@@ -6417,6 +6836,7 @@ dupmatch(Cmatch m)
     r->brsl = m->brsl;
     r->rems = ztrdup(m->rems);
     r->remf = ztrdup(m->remf);
+    r->autoq = m->autoq;
 
     return r;
 }
@@ -6938,7 +7358,7 @@ do_ambiguous(void)
 	 * completion options.                                             */
 	do_ambig_menu();
     } else if (ainfo) {
-	int atend = (cs == we), oll = ll, la;
+	int atend = (cs == we), oll = ll, la, eq, tcs;
 	VARARR(char, oline, ll);
 
 	menucur = NULL;
@@ -6954,7 +7374,13 @@ do_ambiguous(void)
 
 	/* Now get the unambiguous string and insert it into the line. */
 	cline_str(ainfo->line, 1, NULL);
-
+	if (eparq) {
+	    tcs = cs;
+	    cs = lastend;
+	    for (eq = eparq; eq; eq--)
+		inststrlen("\"", 0, 1);
+	    cs = tcs;
+	}
 	/* la is non-zero if listambiguous may be used. Copying and
 	 * comparing the line looks like BFI but it is the easiest
 	 * solution. Really. */
@@ -7078,12 +7504,18 @@ do_single(Cmatch m)
 	 * so generate one automagically.     */
 	cs = scs;
 	if (m->ripre && (m->flags & CMF_PARBR)) {
+	    int pq;
+
 	    /*{{*/
 	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
+	    cs += eparq;
+	    for (pq = parq; pq; pq--)
+		inststrlen("\"", 1, 1);
+	    menuinsc += parq;
 	    inststrlen("}", 1, 1);
 	    menuinsc++;
 	    if (menuwe)
-		menuend++;
+		menuend += menuinsc;
 	}
 	if ((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) {
 	    /* If we have a filename or we completed a parameter name      *
@@ -7105,6 +7537,7 @@ do_single(Cmatch m)
 		    noerrs = 1;
 		    parsestr(p);
 		    singsub(&p);
+		    errflag = 0;
 		    noerrs = ne;
 		} else {
 		    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
@@ -7153,24 +7586,30 @@ do_single(Cmatch m)
 	    if ((!menucmp || menuwe) && isset(AUTOPARAMKEYS))
 		suffixlen[','] = suffixlen['}'] = 1;
 	}
-    } else if (!menucmp && !havesuff && (!(m->flags & CMF_FILE) || !sr)) {
+    } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
 	/* If we didn't add a suffix, add a space, unless we are *
 	 * doing menu completion or we are completing files and  *
 	 * the string doesn't name an existing file.             */
-	inststrlen(" ", 1, 1);
-	menuinsc++;
-	if (menuwe)
-	    makesuffix(1);
+	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
+	    inststrlen(&(m->autoq), 1, 1);
+	    menuinsc++;
+	}
+	if (!menucmp) {
+	    inststrlen(" ", 1, 1);
+	    menuinsc++;
+	    if (menuwe)
+		makesuffix(1);
+	}
     }
     if (menuwe && m->ripre && isset(AUTOPARAMKEYS))
-	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc);
+	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc - parq);
 
     if ((menucmp && !menuwe) || !movetoend)
 	cs = menuend;
 }
 
 /* This maps the value in v into the range [0,m-1], decrementing v
- * if it is non-negative and making negative values cound backwards. */
+ * if it is non-negative and making negative values count backwards. */
 
 static int
 comp_mod(int v, int m)
diff --git a/Src/glob.c b/Src/glob.c
index 596a05ebf..35aa447de 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -97,6 +97,7 @@ typedef struct stat *Statptr;	 /* This makes the Ultrix compiler happy.  Go figu
 #define TT_MINS 2
 #define TT_WEEKS 3
 #define TT_MONTHS 4
+#define TT_SECONDS 5
 
 #define TT_BYTES 0
 #define TT_POSIX_BLOCKS 1
@@ -1615,6 +1616,8 @@ glob(LinkList list, LinkNode np)
 				units = TT_WEEKS, ++s;
 			    else if (*s == 'M')
 				units = TT_MONTHS, ++s;
+			    else if (*s == 's')
+				units = TT_SECONDS, ++s;
 			}
 			/* See if it's greater than, equal to, or less than */
 			if ((range = *s == '+' ? 1 : *s == '-' ? -1 : 0))
diff --git a/Src/hist.c b/Src/hist.c
index 6b2c2a653..4d672deab 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -58,8 +58,8 @@ int excs, exlast;
  * the line does not get added to the list until hend(), if at all.
  * However, curhist is incremented to reflect the current line anyway
  * and a temporary history entry is inserted while the user is editing.
- * If the resulting line was not added to the list, curhist is
- * decremented in hend().
+ * If the resulting line was not added to the list, a flag is set so
+ * that curhist will be decremented in hbegin().
  */
  
 /**/
@@ -984,6 +984,8 @@ hend(void)
 void
 remhist(void)
 {
+    if (hist_ring == &curline)
+	return;
     if (!(histactive & HA_ACTIVE)) {
 	if (!(histactive & HA_JUNKED)) {
 	    freehistnode((HashNode)hist_ring);
diff --git a/Src/init.c b/Src/init.c
index 7f1b2153a..f9e6574dd 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -550,7 +550,11 @@ setupvals(void)
     cdpath   = mkarray(NULL);
     manpath  = mkarray(NULL);
     fignore  = mkarray(NULL);
+#ifdef FUNCTION_DIR
+    fpath    = mkarray(ztrdup(FUNCTION_DIR));
+#else
     fpath    = mkarray(NULL);
+#endif
     mailpath = mkarray(NULL);
     watch    = mkarray(NULL);
     psvar    = mkarray(NULL);
@@ -758,33 +762,31 @@ run_init_scripts(void)
 #ifdef GLOBAL_ZSHENV
 	source(GLOBAL_ZSHENV);
 #endif
-	if (isset(RCS)) {
-	    if (unset(PRIVILEGED))
-		sourcehome(".zshenv");
-	    if (islogin) {
+	if (isset(RCS) && unset(PRIVILEGED))
+	    sourcehome(".zshenv");
+	if (islogin) {
 #ifdef GLOBAL_ZPROFILE
-		if (isset(GLOBALRCS))
+	    if (isset(RCS) && isset(GLOBALRCS))
 		    source(GLOBAL_ZPROFILE);
 #endif
-		if (unset(PRIVILEGED))
-		    sourcehome(".zprofile");
-	    }
-	    if (interact) {
+	    if (isset(RCS) && unset(PRIVILEGED))
+		sourcehome(".zprofile");
+	}
+	if (interact) {
 #ifdef GLOBAL_ZSHRC
-		if (isset(GLOBALRCS))
-		    source(GLOBAL_ZSHRC);
+	    if (isset(RCS) && isset(GLOBALRCS))
+		source(GLOBAL_ZSHRC);
 #endif
-		if (unset(PRIVILEGED))
-		    sourcehome(".zshrc");
-	    }
-	    if (islogin) {
+	    if (isset(RCS) && unset(PRIVILEGED))
+		sourcehome(".zshrc");
+	}
+	if (islogin) {
 #ifdef GLOBAL_ZLOGIN
-		if (isset(GLOBALRCS))
-		    source(GLOBAL_ZLOGIN);
+	    if (isset(RCS) && isset(GLOBALRCS))
+		source(GLOBAL_ZLOGIN);
 #endif
-		if (unset(PRIVILEGED))
-		    sourcehome(".zlogin");
-	    }
+	    if (isset(RCS) && unset(PRIVILEGED))
+		sourcehome(".zlogin");
 	}
     }
     noerrexit = 0;
diff --git a/Src/loop.c b/Src/loop.c
index 95ec4832a..070173fa2 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -138,6 +138,7 @@ execselect(Cmd cmd)
     LinkNode n;
     int i;
     FILE *inp;
+    size_t more;
 
     node = cmd->u.forcmd;
     args = cmd->args;
@@ -154,7 +155,7 @@ execselect(Cmd cmd)
     lastval = 0;
     pushheap();
     inp = fdopen(dup((SHTTY == -1) ? 0 : SHTTY), "r");
-    selectlist(args);
+    more = selectlist(args, 0);
     for (;;) {
 	for (;;) {
 	    if (empty(bufstack)) {
@@ -181,7 +182,7 @@ execselect(Cmd cmd)
 		*s = '\0';
 	    if (*str)
 	      break;
-	    selectlist(args);
+	    more = selectlist(args, more);
 	}
 	setsparam("REPLY", ztrdup(str));
 	i = atoi(str);
@@ -217,8 +218,8 @@ execselect(Cmd cmd)
 /* And this is used to print select lists. */
 
 /**/
-static void
-selectlist(LinkList l)
+size_t
+selectlist(LinkList l, size_t start)
 {
     size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct;
     LinkNode n;
@@ -245,7 +246,7 @@ selectlist(LinkList l)
     else
 	fw = (columns - 1) / fct;
     colsz = (ct + fct - 1) / fct;
-    for (t1 = 0; t1 != colsz; t1++) {
+    for (t1 = start; t1 != colsz && t1 - start < lines - 2; t1++) {
 	ap = arr + t1;
 	do {
 	    int t2 = strlen(*ap) + 2, t3;
@@ -271,6 +272,8 @@ selectlist(LinkList l)
        }
        while (*ap);*/
     fflush(stderr);
+
+    return t1 < colsz ? t1 : 0;
 }
 
 /**/
diff --git a/Src/main.c b/Src/main.c
index 7ec8f07bd..762e06b90 100644
--- a/Src/main.c
+++ b/Src/main.c
@@ -45,12 +45,24 @@ main(int argc, char **argv)
 
     for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++);
 
-    if (!(zsh_name = strrchr(argv[0], '/')))
-	zsh_name = argv[0];
-    else
-	zsh_name++;
-    if (*zsh_name == '-')
-	zsh_name++;
+    zsh_name = argv[0];
+    do {
+      char *arg0 = zsh_name;
+      if (!(zsh_name = strrchr(arg0, '/')))
+	  zsh_name = arg0;
+      else
+	  zsh_name++;
+      if (*zsh_name == '-')
+	  zsh_name++;
+      if (strcmp(zsh_name, "su") == 0) {
+	  char *sh = zgetenv("SHELL");
+	  if (sh && *sh && arg0 != sh)
+	      zsh_name = sh;
+	  else
+	      break;
+      } else
+	  break;
+    } while (zsh_name);
 
     fdtable_size = OPEN_MAX;
     fdtable = zcalloc(fdtable_size);
diff --git a/Src/options.c b/Src/options.c
index 5725d27c0..504ccdf7e 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -438,17 +438,6 @@ emulate(const char *zsh_name, int fully)
 {
     char ch = *zsh_name;
 
-    if (!strcmp("su", zsh_name)) {
-	/* We haven't set up the paramtable yet, so just use zgetenv */
-	char *ptr = zgetenv("SHELL");
-	if (ptr && *ptr) {
-	    zsh_name = ptr;
-	    if ((ptr = strrchr(zsh_name, '/')))
-		zsh_name = ptr+1;
-	    ch = *zsh_name;
-	} else
-	    ch = 'z';
-    }
     if (ch == 'r')
 	ch = zsh_name[1];
 
diff --git a/Src/subst.c b/Src/subst.c
index 47b59e793..0017b205e 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -976,7 +976,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		zerr("bad substitution", NULL, 0);
 		return NULL;
 	    }
-	} else if (INULL(*s))
+	} else if (inbrace && INULL(*s))
 	    s++;
 	else
 	    break;
@@ -984,7 +984,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     globsubst = globsubst && !qt;
 
     idbeg = s;
-    if ((subexp = (s[-1] && isstring(*s) &&
+    if ((subexp = (inbrace && s[-1] && isstring(*s) &&
 		   (s[1] == Inbrace || s[1] == Inpar)))) {
 	int sav;
 	int quoted = *s == Qstring;
@@ -1168,6 +1168,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     }
 
     idend = s;
+    if (inbrace)
+	while (INULL(*s))
+	    s++;
     if ((colf = *s == ':'))
 	s++;
 
@@ -1484,6 +1487,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	val = dupstring("");
 	isarr = 0;
     } else if (isarr && aval && aval[0] && !aval[1]) {
+	/* treat a one-element array as a scalar for purposes of   *
+	 * concatenation with surrounding text (some${param}thing) *
+	 * and rc_expand_param handling.  Note: mult_isarr (above) *
+	 * propagates the true array type from nested expansions.  */
 	val = aval[0];
 	isarr = 0;
     }
@@ -1500,8 +1507,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		val = aval[0];
 	    else
 		isarr = 2;
-	    mult_isarr = isarr;
 	}
+	mult_isarr = isarr;
     }
     if (casmod) {
 	if (isarr) {
@@ -1665,7 +1672,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    return NULL;
 	xlen = strlen(x);
 	*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst);
-	if (qt && !*y && isarr != 2)
+	if (qt && !*y)
 	    y = dupstring(nulstring);
 	setdata(n, (void *) y);
     }
diff --git a/Src/zsh.export b/Src/zsh.export
index 7595f1d2e..ef9fca8c6 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -145,6 +145,7 @@ metadiffer
 metafy
 metalen
 mode_to_octal
+movefd
 movehistent
 mypgrp
 mypid
@@ -194,6 +195,7 @@ quietgethist
 quotedzputs
 readoutput
 realparamtab
+redup
 refreshptr
 remlpaths
 remnulargs
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index 214f26681..bd9aabc2e 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -30,6 +30,9 @@ version.h: $(sdir_top)/Config/version.mk
 
 zshpaths.h: FORCE Makemod
 	@echo '#define MODULE_DIR "'$(MODDIR)'"' > zshpaths.h.tmp
+	@if test x$(fndir) != xno; then \
+	  echo '#define FUNCTION_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \
+	fi
 	@if cmp -s zshpaths.h zshpaths.h.tmp; then \
 	    rm -f zshpaths.h.tmp; \
 	    echo "\`zshpaths.h' is up to date." ; \