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.h5
-rw-r--r--Src/Zle/comp1.c8
-rw-r--r--Src/Zle/comp1.export3
-rw-r--r--Src/Zle/compctl.c67
-rw-r--r--Src/Zle/iwidgets.list2
-rw-r--r--Src/Zle/zle.h24
-rw-r--r--Src/Zle/zle_main.c29
-rw-r--r--Src/Zle/zle_params.c23
-rw-r--r--Src/Zle/zle_thingy.c57
-rw-r--r--Src/Zle/zle_tricky.c847
-rw-r--r--Src/builtin.c132
-rw-r--r--Src/glob.c321
-rw-r--r--Src/init.c45
-rw-r--r--Src/lex.c5
-rw-r--r--Src/params.c66
-rw-r--r--Src/subst.c45
-rw-r--r--Src/zsh.export1
-rw-r--r--Src/zsh.h11
18 files changed, 1078 insertions, 613 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index afd55b7f1..20e3d2cce 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -267,3 +267,8 @@ struct cline {
 #define CLF_MISS  4
 #define CLF_DIFF  8
 #define CLF_SUF  16
+
+/* Flags for makecomplist*(). Things not to do. */
+
+#define CFN_FIRST   1
+#define CFN_DEFAULT 2
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 42bc92bb2..36588a385 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -44,12 +44,6 @@ Cmlist cmatcher;
 /* pointers to functions required by zle and defined by compctl */
 
 /**/
-void (*printcompctlptr) _((char *, Compctl, int, int));
-
-/**/
-Compctl (*compctl_widgetptr) _((char *, char **));
-
-/**/
 void (*makecompparamsptr) _((void));
 
 /* pointers to functions required by compctl and defined by zle */
@@ -66,6 +60,8 @@ int (*getcpatptr) _((char *, int, char *, int));
 /**/
 void (*makecomplistcallptr) _((Compctl));
 
+/**/
+void (*makecomplistctlptr) _((int));
 
 /* Hash table for completion info for commands */
  
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index c90161740..72581173b 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -11,7 +11,6 @@ clwsize
 cmatcher
 compcommand
 compcontext
-compctl_widgetptr
 compctltab
 compcurrent
 compiprefix
@@ -28,8 +27,8 @@ incompctlfunc
 incompfunc
 instring
 makecomplistcallptr
+makecomplistctlptr
 makecompparamsptr
 patcomps
-printcompctlptr
 quotename
 rembslash
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 1913d3828..879042cf3 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1638,38 +1638,6 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     return ret;
 }
 
-/* Externally callable version of get_compctl.  Used for completion widgets */
-
-/**/
-static Compctl
-compctl_widget(char *name, char **argv)
-{
-  Compctl cc = (Compctl) zcalloc(sizeof(*cc));
-  cclist = 0;
-  showmask = 0;
-
-  if (get_compctl(name, &argv, cc, 1, 0, 0)) {
-      freecompctl(cc);
-      return NULL;
-  }
-
-  if (cclist & COMP_REMOVE) {
-      zwarnnam(name, "use -D to delete widget", NULL, 0);
-      return NULL;
-  } else if (cclist) {
-      zwarnnam(name, "special options illegal in widget", NULL, 0);
-      freecompctl(cc);
-      return NULL;
-  } else if (*argv) {
-      zwarnnam(name, "command names illegal in widget", NULL, 0);
-      freecompctl(cc);
-      return NULL;
-  }
-  cc->refc++;
-
-  return cc;
-}
-
 /**/
 static int
 bin_complist(char *name, char **argv, char *ops, int func)
@@ -1677,7 +1645,7 @@ bin_complist(char *name, char **argv, char *ops, int func)
     Compctl cc;
     int ret = 0;
 
-    if (!incompfunc) {
+    if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
@@ -1706,7 +1674,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     char *pre = NULL, *suf = NULL, *group = NULL;
     int f = 0, q = 0, m = 0, ns = 0, a = 0;
 
-    if (!incompfunc) {
+    if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
@@ -1792,15 +1760,27 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	}
     }
  ca_args:
-    if (!*argv) {
-	zerrnam(name, "missing completions", NULL, 0);
+    if (!*argv)
 	return 1;
-    }
+
     addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
 		  f, q, m, ns, a, argv);
     return 0;
 }
 
+/**/
+static int
+bin_compcall(char *name, char **argv, char *ops, int func)
+{
+    if (incompfunc != 1) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) |
+		       (ops['D'] ? 0 : CFN_DEFAULT));
+    return 0;
+}
+
 #define VAR(X) ((void *) (&(X)))
 static struct compparam {
     char *name;
@@ -1823,7 +1803,7 @@ void makecompparams(void)
     struct compparam *cp;
 
     for (cp = compparams; cp->name; cp++) {
-	Param pm = createparam(cp->name, cp->type | PM_SPECIAL);
+	Param pm = createparam(cp->name, cp->type | PM_SPECIAL|PM_REMOVABLE);
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
 	DPUTS(!pm, "param not set in makecompparams");
@@ -1856,7 +1836,7 @@ compunsetfn(Param pm, int exp)
 static int
 comp_wrapper(List list, FuncWrap w, char *name)
 {
-    if (!incompfunc)
+    if (incompfunc != 1)
 	return 1;
     else {
 	char *octxt, *ocmd, *opre, *osuf, *oipre;
@@ -1907,7 +1887,7 @@ ignore_prefix(int l)
 static int
 comp_check(void)
 {
-    if (!incompfunc) {
+    if (incompfunc != 1) {
 	zerr("condition can only be used in completion function", NULL, 0);
 	return 0;
     }
@@ -2119,7 +2099,8 @@ cond_nmatches(char **a, int id)
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
     BUILTIN("complist", 0, bin_complist, 1, -1, 0, NULL, NULL),
-    BUILTIN("compadd", 0, bin_compadd, 1, -1, 0, NULL, NULL),
+    BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
+    BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 };
 
 static struct conddef cotab[] = {
@@ -2149,8 +2130,6 @@ int
 setup_compctl(Module m)
 {
     compctltab->printnode = printcompctlp;
-    printcompctlptr = printcompctl;
-    compctl_widgetptr = compctl_widget;
     makecompparamsptr = makecompparams;
     return 0;
 }
@@ -2183,8 +2162,6 @@ int
 finish_compctl(Module m)
 {
     compctltab->printnode = NULL;
-    printcompctlptr = NULL;
-    compctl_widgetptr = NULL;
     makecompparamsptr = NULL;
     return 0;
 }
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 12425d872..61ad0e24a 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -26,7 +26,7 @@
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
-"copy-prev-word", copyprevword, 0
+"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX
 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
 "delete-char", deletechar, ZLE_KEEPSUFFIX
 "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index f12505bd3..144bb1996 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -46,7 +46,6 @@ struct widget {
     union {
 	ZleIntFunc fn;	/* pointer to internally implemented widget */
 	char *fnnam;	/* name of the shell function for user-defined widget */
-        Compctl cc;     /* for use with a WIDGET_COMP widget */
 	struct {
 	    ZleIntFunc fn; /* internal widget function to call */
 	    char *wid;     /* name of widget to call */
@@ -56,20 +55,15 @@ struct widget {
 };
 
 #define WIDGET_INT	(1<<0)    /* widget is internally implemented */
-#define WIDGET_COMP	(1<<1)	  /* Special completion widget */
-#define WIDGET_NCOMP    (1<<2)    /* new style completion widget */
-#define ZLE_MENUCMP	(1<<3)    /* DON'T invalidate completion list */
-#define ZLE_YANK	(1<<4)
-#define ZLE_LINEMOVE	(1<<5)    /* command is a line-oriented movement */
-#define ZLE_LASTCOL     (1<<6)    /* command maintains lastcol correctly */
-#define ZLE_KILL	(1<<7)
-#define ZLE_KEEPSUFFIX	(1<<9)    /* DON'T remove added suffix */
-#define ZLE_USEMENU	(1<<10)   /* Do    ) use menu completion for   */
-#define ZLE_NOMENU	(1<<11)   /* Don't )  widget, else use default */
-#define ZLE_USEGLOB	(1<<12)   /* Do    ) use glob completion for   */
-#define ZLE_NOGLOB	(1<<13)   /* Don't )  widget, else use default */
-#define ZLE_NOTCOMMAND  (1<<14)   /* widget should not alter lastcmd */
-#define ZLE_ISCOMP      (1<<15)   /* usable for new style completion */
+#define WIDGET_NCOMP    (1<<1)    /* new style completion widget */
+#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
+#define ZLE_YANK	(1<<3)
+#define ZLE_LINEMOVE	(1<<4)    /* command is a line-oriented movement */
+#define ZLE_LASTCOL     (1<<5)    /* command maintains lastcol correctly */
+#define ZLE_KILL	(1<<6)
+#define ZLE_KEEPSUFFIX	(1<<7)    /* DON'T remove added suffix */
+#define ZLE_NOTCOMMAND  (1<<8)    /* widget should not alter lastcmd */
+#define ZLE_ISCOMP      (1<<9)	  /* usable for new style completion */
 /* thingies */
 
 struct thingy {
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 2c0d3655e..03549b5d0 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -45,10 +45,10 @@ int mark;
 /**/
 int c;
 
-/* the binding for this key */
+/* the bindings for the previous and for this key */
 
 /**/
-Thingy bindk;
+Thingy lbindk, bindk;
 
 /* insert mode/overwrite mode flag */
 
@@ -554,6 +554,7 @@ zleread(char *lp, char *rp, int ha)
 void
 execzlefunc(Thingy func)
 {
+    int r = 0;
     Widget w;
 
     if(func->flags & DISABLED) {
@@ -565,14 +566,13 @@ execzlefunc(Thingy func)
 	showmsg(msg);
 	zsfree(msg);
 	feep();
-    } else if((w = func->widget)->flags &
-	      (WIDGET_INT|WIDGET_COMP | WIDGET_NCOMP)) {
+    } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
 	if(!(wflags & ZLE_KEEPSUFFIX))
 	    removesuffix();
 	if(!(wflags & ZLE_MENUCMP) ||
-	   ((wflags & (WIDGET_COMP|WIDGET_NCOMP)) && compwidget != w)) {
+	   ((wflags & WIDGET_NCOMP) && compwidget != w)) {
 	    /* If we are doing a special completion, and the widget
 	     * is not the one currently in use for special completion,
 	     * we are starting a new completion.
@@ -584,16 +584,14 @@ execzlefunc(Thingy func)
 	    vilinerange = 1;
 	if(!(wflags & ZLE_LASTCOL))
 	    lastcol = -1;
-	if (wflags & WIDGET_COMP) {
-	    compwidget = w;
-	    completespecial();
-	} else if (wflags & WIDGET_NCOMP) {
+	if (wflags & WIDGET_NCOMP) {
 	    compwidget = w;
 	    completecall();
 	} else
 	    w->u.fn();
 	if (!(wflags & ZLE_NOTCOMMAND))
 	    lastcmd = wflags;
+	r = 1;
     } else {
 	List l = getshfunc(w->u.fnnam);
 
@@ -610,14 +608,20 @@ execzlefunc(Thingy func)
 	    int osc = sfcontext;
 
 	    startparamscope();
-	    makezleparams();
+	    makezleparams(0);
 	    sfcontext = SFC_WIDGET;
 	    doshfunc(w->u.fnnam, l, NULL, 0, 1);
 	    sfcontext = osc;
 	    endparamscope();
 	    lastcmd = 0;
+	    r = 1;
 	}
     }
+    if (r) {
+	unrefthingy(lbindk);
+	refthingy(func);
+	lbindk = func;
+    }
 }
 
 /* initialise command modifiers */
@@ -877,9 +881,11 @@ setup_zle(Module m)
     comp_strptr = comp_str;
     getcpatptr = getcpat;
     makecomplistcallptr = makecomplistcall;
+    makecomplistctlptr = makecomplistctl;
 
     /* initialise the thingies */
     init_thingies();
+    lbindk = NULL;
 
     /* miscellaneous initialisations */
     stackhist = stackcs = -1;
@@ -920,6 +926,8 @@ finish_zle(Module m)
 {
     int i;
 
+    unrefthingy(lbindk);
+
     cleanup_keymaps();
     deletehashtable(thingytab);
 
@@ -944,6 +952,7 @@ finish_zle(Module m)
     comp_strptr = NULL;
     getcpatptr = NULL;
     makecomplistcallptr = NULL;
+    makecomplistctlptr = NULL;
 
     return 0;
 }
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index e0c4e94ec..74f905ef4 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -61,17 +61,22 @@ static struct zleparam {
 	zleunsetfn, NULL },
     { "RBUFFER", PM_SCALAR,  FN(set_rbuffer), FN(get_rbuffer),
 	zleunsetfn, NULL },
+    { "WIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_widget),
+        zleunsetfn, NULL },
+    { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget),
+        zleunsetfn, NULL },
     { NULL, 0, NULL, NULL, NULL, NULL }
 };
 
 /**/
 void
-makezleparams(void)
+makezleparams(int ro)
 {
     struct zleparam *zp;
 
     for(zp = zleparams; zp->name; zp++) {
-	Param pm = createparam(zp->name, zp->type | PM_SPECIAL);
+	Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
+					  (ro ? PM_READONLY : 0)));
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, zp->name);
 	DPUTS(!pm, "param not set in makezleparams");
@@ -197,3 +202,17 @@ get_rbuffer(Param pm)
 {
     return metafy((char *)line + cs, ll - cs, META_HEAPDUP);
 }
+
+/**/
+static char *
+get_widget(Param pm)
+{
+    return bindk->nam;
+}
+
+/**/
+static char *
+get_lwidget(Param pm)
+{
+    return (lbindk ? lbindk->nam : "");
+}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 2e21b5add..629d5a84e 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -244,9 +244,7 @@ unbindwidget(Thingy t, int override)
 static void
 freewidget(Widget w)
 {
-    if ((w->flags & WIDGET_COMP) && w->u.cc)
-	freecompctl(w->u.cc);
-    else if (w->flags & WIDGET_NCOMP) {
+    if (w->flags & WIDGET_NCOMP) {
 	zsfree(w->u.comp.wid);
 	zsfree(w->u.comp.func);
     } else if(!(w->flags & WIDGET_INT))
@@ -339,7 +337,7 @@ bin_zle(char *name, char **args, char *ops, int func)
 	{ 'D', bin_zle_del,  1, -1 },
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
-	{ 'C', bin_zle_compctl, 1, -1},
+	{ 'C', bin_zle_complete, 3, 3 },
 	{ 'c', bin_zle_complete, 3, 3 },
 	{ 0,   bin_zle_call, 0, -1 },
     };
@@ -392,14 +390,11 @@ scanlistwidgets(HashNode hn, int list)
     if(w->flags & WIDGET_INT)
 	return;
     if(list) {
-	fputs((w->flags & WIDGET_COMP) ? "zle -C " : "zle -N ", stdout);
+	fputs("zle -N ", stdout);
 	if(t->nam[0] == '-')
 	    fputs("-- ", stdout);
 	quotedzputs(t->nam, stdout);
-	if (w->flags & WIDGET_COMP) {
-	    if (printcompctlptr && w->u.cc)
-		printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0);
-	} else if (w->flags & WIDGET_NCOMP) {
+	if (w->flags & WIDGET_NCOMP) {
 	    fputc(' ', stdout);
 	    quotedzputs(w->u.comp.wid, stdout);
 	    fputc(' ', stdout);
@@ -410,11 +405,7 @@ scanlistwidgets(HashNode hn, int list)
 	}
     } else {
 	nicezputs(t->nam, stdout);
-	if (w->flags & WIDGET_COMP) {
-	    fputs(" -C", stdout);
-	    if (printcompctlptr && w->u.cc)
-		printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0);
-	} else if (w->flags & WIDGET_NCOMP) {
+	if (w->flags & WIDGET_NCOMP) {
 	    fputs(" -c ", stdout);
 	    nicezputs(w->u.comp.wid, stdout);
 	    fputc(' ', stdout);
@@ -482,44 +473,6 @@ bin_zle_new(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_compctl(char *name, char **args, char *ops, char func)
-{
-    Compctl cc = NULL;
-    Widget w;
-    char *wname = args[0];
-
-    if (!compctl_widgetptr) {
-	zwarnnam(name, "compctl module is not loaded", NULL, 0);
-	return 1;
-    }
-
-    args++;
-
-    if (*args && !(cc = compctl_widgetptr(name, args)))
-	return 1;
-
-    w = zalloc(sizeof(*w));
-    w->flags = WIDGET_COMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
-    w->first = NULL;
-    w->u.cc = cc;
-    if(bindwidget(w, rthingy(wname))) {
-	freewidget(w);
-	zerrnam(name, "widget name `%s' is protected", wname, 0);
-	return 1;
-    }
-    if (ops['m'])
-	w->flags |= ZLE_USEMENU;
-    else if (ops['M'])
-	w->flags |= ZLE_NOMENU;
-    if (ops['g'])
-	w->flags |= ZLE_USEGLOB;
-    else if (ops['G'])
-	w->flags |= ZLE_NOGLOB;
-    return 0;
-}
-
-/**/
-static int
 bin_zle_complete(char *name, char **args, char *ops, char func)
 {
     Thingy t;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index a958752ca..df3e11f46 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -98,10 +98,12 @@ static int menupos, menulen, menuend, menuwe, menuinsc;
 
 /* This is for completion inside a brace expansion. brbeg and brend hold  *
  * strings that were temporarily removed from the string to complete.     *
- * brpl and brsl hold the offset of these strings.                        */
+ * brpl and brsl hold the offset of these strings.                        *
+ * brpcs and brscs hold the positions of the re-inserted string in the    *
+ * line.                                                                  */
 
 static char *brbeg = NULL, *brend = NULL;
-static int brpl, brsl;
+static int brpl, brsl, brpcs, brscs;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
@@ -261,7 +263,6 @@ usetab(void)
 }
 
 enum { COMP_COMPLETE,
-       COMP_WIDGET,
        COMP_LIST_COMPLETE,
        COMP_SPELL,
        COMP_EXPAND,
@@ -271,18 +272,6 @@ enum { COMP_COMPLETE,
 
 /**/
 void
-completespecial(void)
-{
-    int flags = compwidget->flags;
-    usemenu = (flags & ZLE_USEMENU) ? 1 : (flags & ZLE_NOMENU) ? 0
-	: isset(MENUCOMPLETE);
-    useglob = (flags & ZLE_USEGLOB) ? 1 : (flags & ZLE_NOGLOB) ? 0
-	: isset(GLOBCOMPLETE);
-    docomplete(compwidget->u.cc ? COMP_WIDGET : COMP_COMPLETE);
-}
-
-/**/
-void
 completecall(void)
 {
     compfunc = compwidget->u.comp.func;
@@ -426,19 +415,30 @@ reversemenucomplete(void)
 void
 acceptandmenucomplete(void)
 {
-    int sl = suffixlen[' '];
-
     if (!menucmp) {
 	feep();
 	return;
     }
-    cs = menupos + menulen + menuinsc;
-    if (sl)
-	backdel(sl);
-    inststrlen(" ", 1, 1);
-    menuinsc = menulen = 0;
-    menupos = cs;
-    menuwe = 1;
+    if (brbeg && *brbeg) {
+	int l = (brscs >= 0 ? brscs : cs) - brpcs;
+
+	zsfree(brbeg);
+	brbeg = (char *) zalloc(l + 2);
+	memcpy(brbeg, line + brpcs, l);
+	brbeg[l] = ',';
+	brbeg[l + 1] = '\0';
+    } else {
+	int sl = suffixlen[' '];
+
+	cs = menupos + menulen + menuinsc;
+	if (sl)
+	    backdel(sl);
+
+	inststrlen(" ", 1, 1);
+	menuinsc = menulen = 0;
+	menupos = cs;
+	menuwe = 1;
+    }
     menucomplete();
 }
 
@@ -447,6 +447,10 @@ acceptandmenucomplete(void)
 
 static int lincmd, linredir;
 
+/* The string for the redirection operator. */
+
+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       *
@@ -998,6 +1002,8 @@ get_comp_string(void)
 	    oins = ins;
 	    /* Get the next token. */
 	    ctxtlex();
+	    if (inredir)
+		rdstr = tokstrings[tok];
 	    if (tok == DINPAR)
 		tokstr = NULL;
 
@@ -1859,7 +1865,8 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
 /* Do the matching for a prefix. */
 
 static char *
-match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
+match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
+	  Cmatcher nm)
 {
     static unsigned char *ea;
     static int ealen = 0;
@@ -1867,61 +1874,27 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
     static int rwlen;
 
     int ll = strlen(l), lw = strlen(w), mlw;
-    int il = 0, iw = 0, t, stil, stiw, std, bc = brpl;
-    char *nw = rw, *stl = NULL, *stw;
+    int il = 0, iw = 0, t, bc = brpl;
+    char *nw = rw;
     Cmlist ms;
-    Cmatcher mp, stm;
+    Cmatcher mp, lm = NULL;
     Cline lr = NULL;
 
-    *nlp = NULL;
+    if (nlp) {
+	*nlp = NULL;
 
-    if (ll > ealen) {
-	/* This is the `in'/`out' string for pattern matching. */
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc(ealen = ll + 20);
+	if (ll > ealen) {
+	    /* This is the `in'/`out' string for pattern matching. */
+	    if (ealen)
+		zfree(ea, ealen);
+	    ea = (unsigned char *) zalloc(ealen = ll + 20);
+	}
     }
     while (ll && lw) {
-	if (*l == *w) {
-	    /* Same character, take it. */
-
-	    if (stl) {
-		/* But first check, if we were collecting characters *
-		 * for a `*'. */
-		int sl = iw - stiw;
-
-		nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0);
-
-		addtocline(nlp, &lr, stl, stm->llen,
-			   stw, sl, stm, (std ? CLF_SUF : 0));
-
-		stl = NULL;
-
-		if (bc <= 0 && bplp) {
-		    *bplp = nw - rw;
-		    bplp = NULL;
-		}
-	    }
-	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0);
-
-	    addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0);
-
-	    l++;
-	    w++;
-	    il++;
-	    iw++;
-	    ll--;
-	    lw--;
-	    bc--;
-
-	    if (bc <= 0 && bplp) {
-		*bplp = nw - rw;
-		bplp = NULL;
-	    }
-	    continue;
-	}
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
+		if (nm == mp || lm == mp)
+		    continue;
 		t = 1;
 		/* Try to match the prefix, if any. */
 		if (mp->flags & CMF_LEFT) {
@@ -1947,22 +1920,42 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 				else
 				    t = 0;
 			    }
-			    if (t && !stl) {
-				/* We simply keep the current position   *
-				 * and start collecting characters until *
-				 * another matcher matches. */
-				std = (mp->flags & CMF_LEFT);
-				stl = l;
-				stil = il;
-				stw = w;
-				stiw = iw;
-				stm = mp;
-				t = 0;
-				l += mp->llen;
-				il += mp->llen;
-				ll -= mp->llen;
-				
-				break;
+			    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;
+
+				for (; k; i++, j++, k--, q++) {
+				    if (match_pfx(p, q, NULL, NULL,
+						  NULL, NULL, mp))
+					break;
+				}
+				if (k) {
+				    if (nlp) {
+					nw = addtoword(&rw, &rwlen, nw, mp,
+						       l, w, i, 0);
+					addtocline(nlp, &lr, l, mp->llen,
+						   w, i, mp, 
+						   ((mp->flags & CMF_LEFT) ?
+						    CLF_SUF : 0));
+				    }
+				    w = q;
+				    iw = j;
+				    lw = k;
+				    l = p;
+				    il = jj;
+				    ll = kk;
+				    bc -= i;
+
+				    if (bc <= 0 && bplp) {
+					*bplp = nw - rw;
+					bplp = NULL;
+				    }
+				    lm = mp;
+				    break;
+				}
+				else
+				    t = 0;
 			    }
 			    else
 				t = 0;
@@ -1988,26 +1981,10 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 		if (t) {
 		    /* If it matched, build a new chunk on the Cline list *
 		     * and add the string to the built match. */
-		    if (stl) {
-			int sl = iw - stiw;
-			
-			nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0);
-			
-			addtocline(nlp, &lr, 
-				   stl, stm->llen, stw, sl, stm,
-				   (std ? CLF_SUF : 0));
-			
-			stl = NULL;
-
-			if (bc <= 0 && bplp) {
-			    *bplp = nw - rw;
-			    bplp = NULL;
-			}
+		    if (nlp) {
+			nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0);
+			addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0);
 		    }
-		    nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0);
-		    
-		    addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0);
-		    
 		    l += mp->llen;
 		    w += mlw;
 		    ll -= mp->llen;
@@ -2023,62 +2000,84 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 		    break;
 		}
 	    }
-	    if (mp)
+	    if (mp) {
+		if (mp != lm)
+		    lm = NULL;
 		break;
+	    }
 	}
-	if (!stl && !t) {
-	    if (*nlp) {
+	if (t)
+	    continue;
+	if (*l == *w) {
+	    /* Same character, take it. */
+	    if (nlp) {
+		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0);
+		addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0);
+	    }
+	    l++;
+	    w++;
+	    il++;
+	    iw++;
+	    ll--;
+	    lw--;
+	    bc--;
+
+	    if (bc <= 0 && bplp) {
+		*bplp = nw - rw;
+		bplp = NULL;
+	    }
+	    lm = NULL;
+	} else {
+	    if (nlp && *nlp) {
 		lr->next = freecl;
 		freecl = *nlp;
 	    }
 	    return NULL;
 	}
-	if (stl) {
-	    /* We are collecting characters, just skip over. */
-	    w++;
-	    lw--;
-	    iw++;
-	}
     }
-    *lp = iw;
+    if (lp)
+	*lp = iw;
     if (lw) {
-	/* There is a unmatched portion in the word, keep it. */
-	if (rlp) {
-	    w = dupstring(w);
-	    addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID);
-
-	    *rlp = lr;
-	} else {
-	    addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END);
+	if (nlp) {
+	    /* There is a unmatched portion in the word, keep it. */
+	    if (rlp) {
+		w = dupstring(w);
+		addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID);
 
-	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
+		*rlp = lr;
+	    } else {
+		addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END);
+		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
+	    }
 	}
-    }
-    else if (rlp) {
-	if (lr) {
+    } else if (rlp) {
+	if (nlp && lr) {
 	    lr->next = freecl;
 	    freecl = *nlp;
 	}
 	return NULL;
     }
-    if (nw)
+    if (nlp && nw)
 	*nw = '\0';
 
     if (ll) {
-	if (*nlp) {
+	if (nlp && *nlp) {
 	    lr->next = freecl;
 	    freecl = *nlp;
 	}
-	return 0;
+	return NULL;
     }
-    /* Finally, return the built match string. */
-    return dupstring(rw);
+    if (nlp)
+	/* Finally, return the built match string. */
+	return dupstring(rw);
+    
+    return ((char *) 1);
 }
 
 /* Do the matching for a suffix. */
 
 static char *
-match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
+match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 {
     static unsigned char *ea;
     static int ealen = 0;
@@ -2086,60 +2085,29 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
     static int rwlen;
 
     int ll = strlen(l), lw = strlen(w), mlw;
-    int il = 0, iw = 0, t, stil, stiw, std, bc = brsl;
-    char *nw = rw, *stl = NULL, *stw;
+    int il = 0, iw = 0, t, bc = brsl;
+    char *nw = rw;
     Cmlist ms;
-    Cmatcher mp, stm;
+    Cmatcher mp, lm = NULL;
     Cline lr = NULL;
 
     l += ll;
     w += lw;
 
-    *nlp = NULL;
+    if (nlp) {
+	*nlp = NULL;
 
-    if (ll > ealen) {
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc(ealen = ll + 20);
+	if (ll > ealen) {
+	    if (ealen)
+		zfree(ea, ealen);
+	    ea = (unsigned char *) zalloc(ealen = ll + 20);
+	}
     }
     while (ll && lw) {
-	if (l[-1] == w[-1]) {
-	    if (stl) {
-		int sl = iw - stiw;
-
-		stl -= stm->llen;
-		stw -= sl;
-		nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1);
-
-		addtocline(nlp, &lr, stl, stm->llen,
-			   stw, sl, stm, (std ? CLF_SUF : 0));
-
-		stl = NULL;
-
-		if (bc <= 0 && bslp) {
-		    *bslp = nw - rw;
-		    bslp = NULL;
-		}
-	    }
-	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1);
-
-	    addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0);
-
-	    l--;
-	    w--;
-	    il++;
-	    iw++;
-	    ll--;
-	    lw--;
-	    bc--;
-	    if (bc <= 0 && bslp) {
-		*bslp = nw - rw;
-		bslp = NULL;
-	    }
-	    continue;
-	}
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
+		if (nm == mp || lm == mp)
+		    continue;
 		t = 1;
 		if (mp->flags & CMF_RIGHT) {
 		    if (il < mp->ralen || iw < mp->ralen)
@@ -2164,22 +2132,43 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 				else
 				    t = 0;
 			    }
-			    if (t && !stl) {
-				std = (mp->flags & CMF_LEFT);
-				stl = l;
-				stil = il;
-				stw = w;
-				stiw = iw;
-				stm = mp;
-				t = 0;
-				l -= mp->llen;
-				il += mp->llen;
-				ll -= mp->llen;
-				
-				break;
+			    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;
+
+				for (; k; i++, j++, k--, q--)
+				    if (match_sfx(p, q, NULL, NULL,
+						  NULL, mp))
+					break;
+				if (k) {
+				    if (nlp) {
+					nw = addtoword(&rw, &rwlen, nw, mp,
+						       l - mp->llen, w - i,
+						       i, 1);
+					addtocline(nlp, &lr, l - mp->llen,
+						   mp->llen, w - i, i, mp, 
+						   ((mp->flags & CMF_LEFT) ?
+						    CLF_SUF : 0));
+				    }
+				    w = q;
+				    iw = j;
+				    lw = k;
+				    l = p;
+				    il = jj;
+				    ll = kk;
+				    bc -= i;
+
+				    if (bc <= 0 && bslp) {
+					*bslp = nw - rw;
+					bslp = NULL;
+				    }
+				    lm = mp;
+				    break;
+				}
+				else
+				    t = 0;
 			    }
-			    else
-				t = 0;
 			}
 		    } else {
 			t = pattern_match(mp->line, l - mp->llen, NULL, ea) &&
@@ -2199,30 +2188,11 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 			t = 0;
 		}
 		if (t) {
-		    if (stl) {
-			int sl = iw - stiw;
-			
-			stl -= stm->llen;
-			stw -= sl;
-			
-			nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1);
-			
-			addtocline(nlp, &lr,
-				   stl, stm->llen, stw, sl, stm,
-				   (std ? CLF_SUF : 0));
-			
-			stl = NULL;
-
-			if (bc <= 0 && bslp) {
-			    *bslp = nw - rw;
-			    bslp = NULL;
-			}
+		    if (nlp) {
+			nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1);
+			addtocline(nlp, &lr, l - mp->llen, mp->llen,
+				   w - mlw, mlw, mp, 0);
 		    }
-		    nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1);
-		    
-		    addtocline(nlp, &lr, l - mp->llen, mp->llen,
-			       w - mlw, mlw, mp, 0);
-		    
 		    l -= mp->llen;
 		    w -= mlw;
 		    ll -= mp->llen;
@@ -2237,34 +2207,55 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 		    break;
 		}
 	    }
-	    if (mp)
+	    if (mp) {
+		if (mp != lm)
+		    lm = NULL;
 		break;
+	    }
 	}
-	if (!stl && !t) {
-	    if (*nlp) {
+	if (t)
+	    continue;
+	if (l[-1] == w[-1]) {
+	    if (nlp) {
+		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1);
+		addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0);
+	    }
+	    l--;
+	    w--;
+	    il++;
+	    iw++;
+	    ll--;
+	    lw--;
+	    bc--;
+	    if (bc <= 0 && bslp) {
+		*bslp = nw - rw;
+		bslp = NULL;
+	    }
+	    lm = NULL;
+	} else {
+	    if (nlp && *nlp) {
 		lr->next = freecl;
 		freecl = *nlp;
 	    }
 	    return NULL;
 	}
-	if (stl) {
-	    w--;
-	    lw--;
-	    iw++;
-	}
     }
-    *lp = iw;
-    if (nw)
+    if (lp)
+	*lp = iw;
+    if (nlp && nw)
 	*nw = '\0';
 
     if (ll) {
-	if (*nlp) {
+	if (nlp && *nlp) {
 	    lr->next = freecl;
 	    freecl = *nlp;
 	}
-	return 0;
+	return NULL;
     }
-    return dupstring(rw);
+    if (nlp)
+	return dupstring(rw);
+
+    return ((char *) 1);
 }
 
 /* Check if the word `w' matches. */
@@ -2283,8 +2274,8 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 	int sl;
 	Cline sli, last;
 
-	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) {
-	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) {
+	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl, NULL))) {
+	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl, NULL))) {
 		int pml, sml;
 
 		last->llen -= sl;
@@ -2305,7 +2296,7 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 	}
 	else
 	    return NULL;
-    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl)))
+    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl, NULL)))
 	return NULL;
 
     if (lppre && *lppre) {
@@ -2373,6 +2364,7 @@ instmatch(Cmatch m)
     if (brbeg && *brbeg) {
 	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
 	l = strlen(brbeg);
+	brpcs = cs;
 	inststrlen(brbeg, 1, l);
 	r += l;
 	ocs += l;
@@ -2385,12 +2377,13 @@ instmatch(Cmatch m)
     if (brend && *brend) {
 	a = cs;
 	cs -= m->brsl;
-	ocs = cs;
+	ocs = brscs = cs;
 	l = strlen(brend);
 	inststrlen(brend, 1, l);
 	r += l;
 	cs = a + l;
-    }
+    } else
+	brscs = -1;
     if (m->suf) {
 	inststrlen(m->suf, 1, (l = strlen(m->suf)));
 	r += l;
@@ -2509,6 +2502,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		if (!ai->firstm)
 		    ai->firstm = cm;
 	    }
+	    compnmatches = mnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 }
@@ -3180,6 +3174,108 @@ docompletion(char *s, int lst, int incmd)
     } LASTALLOC;
 }
 
+/* This calls the given function for new style completion. */
+
+/**/
+static void
+callcompfunc(char *s, char *fn)
+{
+    List list;
+    int lv = lastval;
+    
+    if ((list = getshfunc(fn)) != &dummy_list) {
+	LinkList args = newlinklist();
+	char **p, *tmp;
+	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
+	
+	addlinknode(args, fn);
+	
+	zsfree(compcontext);
+	zsfree(compcommand);
+	compcommand = "";
+	if (inwhat == IN_MATH) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		compcommand = varname ? varname : "";
+	    } else
+		compcontext = "math";
+	    usea = 0;
+	} else if (lincmd)
+	    compcontext = (insubscr ? "subscript" : "command");
+	else if (linredir) {
+	    compcontext = "redirect";
+	    if (rdstr)
+		compcommand = rdstr;
+	} else
+	    switch (inwhat) {
+	    case IN_ENV:
+		compcontext = "value";
+		compcommand = varname;
+		usea = 0;
+		break;
+	    case IN_COND:
+		compcontext = "condition";
+		break;
+	    default:
+		if (cmdstr) {
+		    compcontext = "argument";
+		    compcommand = cmdstr;
+		} else {
+		    compcontext = "value";
+		    if (clwords[0])
+			compcommand = clwords[0];
+		}
+		aadd = 1;
+	    }
+	compcontext = ztrdup(compcontext);
+	tmp = quotename(compcommand, NULL, NULL, NULL);
+	untokenize(tmp);
+	compcommand = ztrdup(tmp);
+	if (usea && (!aadd || clwords[0]))
+	    for (p = clwords + aadd; *p; p++) {
+		tmp = dupstring(*p);
+		untokenize(tmp);
+		addlinknode(args, tmp);
+	    }
+	zsfree(compprefix);
+	zsfree(compsuffix);
+	if (unset(COMPLETEINWORD)) {
+	    tmp = quotename(s, NULL, NULL, NULL);
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss = s + offs, sav;
+	    
+	    tmp = quotename(s, &ss, NULL, NULL);
+	    sav = *ss;
+	    *ss = '\0';
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    *ss = sav;
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
+	compnmatches = mnum;
+	incompfunc = 1;
+	startparamscope();
+	makecompparamsptr();
+	makezleparams(1);
+	sfcontext = SFC_CWIDGET;
+	NEWHEAPS(compheap) {
+	    doshfunc(fn, list, args, 0, 1);
+	} OLDHEAPS;
+	sfcontext = osc;
+	endparamscope();
+	lastcmd = 0;
+	incompfunc = icf;
+    }
+    lastval = lv;
+}
+
 /* The beginning and end of a word range to be used by -l. */
 
 static int brange, erange;
@@ -3232,98 +3328,10 @@ makecomplist(char *s, int incmd, int lst)
 	ccused = newlinklist();
 	ccstack = newlinklist();
 
-	if (compfunc) {
-	    List list;
-	    int lv = lastval;
-
-	    if ((list = getshfunc(compfunc)) != &dummy_list) {
-		LinkList args = newlinklist();
-		char **p, *tmp;
-		int aadd = 0, usea = 1;
-
-		addlinknode(args, compfunc);
-
-		zsfree(compcontext);
-		zsfree(compcommand);
-		compcommand = "";
-		if (inwhat == IN_MATH) {
-		    if (insubscr) {
-			compcontext = "subscript";
-			compcommand = varname ? varname : "";
-		    } else
-			compcontext = "math";
-		    usea = 0;
-		} else if (lincmd)
-		    compcontext = (insubscr ? "subscript" : "command");
-		else if (linredir)
-		    compcontext = "redirect";
-		else
-		    switch (inwhat) {
-		    case IN_ENV:
-			compcontext = "value";
-			compcommand = varname;
-			usea = 0;
-			break;
-		    case IN_COND:
-			compcontext = "condition";
-			break;
-		    default:
-			if (cmdstr) {
-			    compcontext = "argument";
-			    compcommand = cmdstr;
-			} else {
-			    compcontext = "value";
-			    if (clwords[0])
-				compcommand = clwords[0];
-			}
-			aadd = 1;
-		    }
-		compcontext = ztrdup(compcontext);
-		tmp = quotename(compcommand, NULL, NULL, NULL);
-		untokenize(tmp);
-		compcommand = ztrdup(tmp);
-		if (usea && (!aadd || clwords[0]))
-		    for (p = clwords + aadd; *p; p++) {
-			tmp = dupstring(*p);
-			untokenize(tmp);
-			addlinknode(args, tmp);
-		    }
-		zsfree(compprefix);
-		zsfree(compsuffix);
-		if (unset(COMPLETEINWORD)) {
-		    tmp = quotename(s, NULL, NULL, NULL);
-		    untokenize(tmp);
-		    compprefix = ztrdup(tmp);
-		    compsuffix = ztrdup("");
-		} else {
-		    char *ss = s + offs, sav;
-
-		    tmp = quotename(s, &ss, NULL, NULL);
-		    sav = *ss;
-		    *ss = '\0';
-		    untokenize(tmp);
-		    compprefix = ztrdup(tmp);
-		    *ss = sav;
-		    untokenize(ss);
-		    compsuffix = ztrdup(ss);
-		}
-		zsfree(compiprefix);
-		compiprefix = ztrdup("");
-		compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
-		compnmatches = mnum;
-		incompfunc = 1;
-		startparamscope();
-		makecompparamsptr();
-		NEWHEAPS(compheap) {
-		    doshfunc(compfunc, list, args, 0, 1);
-		} OLDHEAPS;
-		endparamscope();
-		lastcmd = 9;
-		incompfunc = 0;
-	    }
-	    lastval = lv;
-	} else
-	    makecomplistglobal(s, incmd, lst);
+	if (compfunc)
+	    callcompfunc(s, compfunc);
+	else
+	    makecomplistglobal(s, incmd, lst, 0);
 
 	endcmgroup(NULL);
 
@@ -3368,11 +3376,11 @@ ctokenize(char *p)
 	if (*p == '\\')
 	    bslash = 1;
 	else {
-	    if (*p == '$') {
+	    if (*p == '$' || *p == '=') {
 		if (bslash)
 		    p[-1] = Bnull;
 		else
-		    *p = String;
+		    *p = (*p == '$' ? String : Equals);
 	    }
 	    bslash = 0;
 	}
@@ -3431,22 +3439,75 @@ makecomplistcall(Compctl cc)
     } SWITCHBACKHEAPS;
 }
 
+/* A simple counter to avoid endless recursion between old and new style *
+ * completion. */
+
+static int cdepth = 0;
+
+#define MAX_CDEPTH 16
+
+/**/
+void
+makecomplistctl(int flags)
+{
+    if (cdepth == MAX_CDEPTH)
+	return;
+
+    cdepth++;
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    int ooffs = offs, lip, lp;
+	    char *str = comp_str(&lip, &lp), *t;
+	    char *os = cmdstr, **ow = clwords, **p, **q;
+	    int on = clwnum, op = clwpos;
+
+	    clwnum = arrlen(pparams) + 1;
+	    clwpos = compcurrent - 1;
+	    cmdstr = ztrdup(compcommand);
+	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	    clwords[0] = ztrdup(cmdstr);
+	    for (p = pparams, q = clwords + 1; *p; p++, q++) {
+		t = dupstring(*p);
+		ctokenize(t);
+		remnulargs(t);
+		*q = ztrdup(t);
+	    }
+	    *q = NULL;
+	    offs = lip + lp;
+	    incompfunc = 2;
+	    makecomplistglobal(str,
+			       (!clwpos && !strcmp(compcontext, "command")),
+			       COMP_COMPLETE, flags);
+	    incompfunc = 1;
+	    offs = ooffs;
+	    compnmatches = mnum;
+	    zsfree(cmdstr);
+	    freearray(clwords);
+	    cmdstr = os;
+	    clwords = ow;
+	    clwnum = on;
+	    clwpos = op;
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+    cdepth--;
+}
+
 /* This function gets the compctls for the given command line and *
  * adds all completions for them. */
 
 /**/
 static void
-makecomplistglobal(char *os, int incmd, int lst)
+makecomplistglobal(char *os, int incmd, int lst, int flags)
 {
     Compctl cc;
     char *s;
 
-    if (lst == COMP_WIDGET) {
-	cc = compwidget->u.cc;
-    } else if (inwhat == IN_ENV)
+    ccont = CC_CCCONT;
+
+    if (inwhat == IN_ENV) {
         /* Default completion for parameter values. */
         cc = &cc_default;
-    else if (inwhat == IN_MATH) {
+    } else if (inwhat == IN_MATH) {
         /* Parameter names inside mathematical expression. */
         cc_dummy.mask = CC_PARAMS;
 	cc = &cc_dummy;
@@ -3469,16 +3530,17 @@ makecomplistglobal(char *os, int incmd, int lst)
 	cc = &cc_default;
     else {
 	/* Otherwise get the matches for the command. */
-	makecomplistcmd(os, incmd);
+	makecomplistcmd(os, incmd, flags);
 	cc = NULL;
     }
     if (cc) {
 	/* First, use the -T compctl. */
-	makecomplistcc(&cc_first, os, incmd);
-
-	if (!(ccont & CC_CCCONT))
-	    return;
+	if (!(flags & CFN_FIRST)) {
+	    makecomplistcc(&cc_first, os, incmd);
 
+	    if (!(ccont & CC_CCCONT))
+		return;
+	}
 	makecomplistcc(cc, os, incmd);
     }
 }
@@ -3487,18 +3549,19 @@ makecomplistglobal(char *os, int incmd, int lst)
 
 /**/
 static void
-makecomplistcmd(char *os, int incmd)
+makecomplistcmd(char *os, int incmd, int flags)
 {
     Compctl cc;
     Compctlp ccp;
     char *s;
 
     /* First, use the -T compctl. */
-    makecomplistcc(&cc_first, os, incmd);
-
-    if (!(ccont & CC_CCCONT))
-	return;
+    if (!(flags & CFN_FIRST)) {
+	makecomplistcc(&cc_first, os, incmd);
 
+	if (!(ccont & CC_CCCONT))
+	    return;
+    }
     /* Then search the pattern compctls, with the command name and the *
      * full pathname of the command. */
     makecomplistpc(os, incmd);
@@ -3526,9 +3589,11 @@ makecomplistcmd(char *os, int incmd)
 	    (cc = ccp->cc)) ||
 	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
 	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
-	    (cc = ccp->cc)))))
+	    (cc = ccp->cc))))) {
+	if (flags & CFN_DEFAULT)
+	    return;
 	cc = &cc_default;
-
+    }
     makecomplistcc(cc, os, incmd);
 }
 
@@ -3817,12 +3882,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
     ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
 
-    if (!incompfunc && findnode(ccstack, cc))
+    if (incompfunc != 1 && findnode(ccstack, cc))
 	return;
 
     addlinknode(ccstack, cc);
 
-    if (!incompfunc && allccs) {
+    if (incompfunc != 1 && allccs) {
 	if (findnode(allccs, cc)) {
 	    uremnode(ccstack, firstnode(ccstack));
 	    return;
@@ -4430,45 +4495,50 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	/* Add user names. */
 	maketildelist();
     if (cc->func) {
-	/* This handles the compctl -K flag. */
-	List list;
-	char **r;
-	int lv = lastval;
-
-	/* Get the function. */
-	if ((list = getshfunc(cc->func)) != &dummy_list) {
-	    /* We have it, so build a argument list. */
-	    LinkList args = newlinklist();
-	    int osc = sfcontext;
-
-	    addlinknode(args, cc->func);
-
-	    if (delit) {
-		p = dupstrpfx(os, ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-		p = dupstring(os + ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-	    } else {
-		addlinknode(args, lpre);
-		addlinknode(args, lsuf);
+	if (cc->func[0] == ' ')
+	    /* Temporary hack for access to new style completione. */
+	    callcompfunc(os, cc->func + 1);
+	else {
+	    /* This handles the compctl -K flag. */
+	    List list;
+	    char **r;
+	    int lv = lastval;
+	    
+	    /* Get the function. */
+	    if ((list = getshfunc(cc->func)) != &dummy_list) {
+		/* We have it, so build a argument list. */
+		LinkList args = newlinklist();
+		int osc = sfcontext;
+		
+		addlinknode(args, cc->func);
+		
+		if (delit) {
+		    p = dupstrpfx(os, ooffs);
+		    untokenize(p);
+		    addlinknode(args, p);
+		    p = dupstring(os + ooffs);
+		    untokenize(p);
+		    addlinknode(args, p);
+		} else {
+		    addlinknode(args, lpre);
+		    addlinknode(args, lsuf);
+		}
+		
+		/* This flag allows us to use read -l and -c. */
+		if (incompfunc != 1)
+		    incompctlfunc = 1;
+		sfcontext = SFC_COMPLETE;
+		/* Call the function. */
+		doshfunc(cc->func, list, args, 0, 1);
+		sfcontext = osc;
+		incompctlfunc = 0;
+		/* And get the result from the reply parameter. */
+		if ((r = get_user_var("reply")))
+		    while (*r)
+			addmatch(*r++, NULL);
 	    }
-
-	    /* This flag allows us to use read -l and -c. */
-	    if (!incompfunc)
-		incompctlfunc = 1;
-	    sfcontext = SFC_COMPLETE;
-	    /* Call the function. */
-	    doshfunc(cc->func, list, args, 0, 1);
-	    sfcontext = osc;
-	    incompctlfunc = 0;
-	    /* And get the result from the reply parameter. */
-	    if ((r = get_user_var("reply")))
-		while (*r)
-		    addmatch(*r++, NULL);
+	    lastval = lv;
 	}
-	lastval = lv;
     }
     if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
 	/* Get job names. */
@@ -4625,7 +4695,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    }
 
 	    /* No harm in allowing read -l and -c here, too */
-	    if (!incompfunc)
+	    if (incompfunc != 1)
 		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    doshfunc(cc->ylist, list, args, 0, 1);
@@ -4692,7 +4762,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    clwords += brange;
 	}
 	/* Produce the matches. */
-	makecomplistcmd(s, incmd);
+	makecomplistcmd(s, incmd, CFN_FIRST);
 
 	/* And restore the things we changed. */
 	clwords = ow;
@@ -5021,9 +5091,10 @@ permmatches(void)
 				   &nn, &nl);
 	    g->mcount = nn;
 	    g->lcount = nn - nl;
-	    if (g->ylist) 
+	    if (g->ylist) {
 		g->lcount = arrlen(g->ylist);
-
+		smatches = 2;
+	    }
 	    g->expls = (Cexpl *) makearray(g->lexpls, 0, &(g->ecount), NULL);
 
 	    g->ccount = 0;
diff --git a/Src/builtin.c b/Src/builtin.c
index f6941286d..ea1ac8ab9 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -50,7 +50,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafilrtux", NULL),
+    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -60,7 +60,7 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRUZafilrtu", "x"),
+    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "x"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
@@ -78,7 +78,7 @@ static struct builtin builtins[] =
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZailrtu", NULL),
+    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZailrtu", NULL),
     BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
@@ -93,7 +93,7 @@ static struct builtin builtins[] =
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafiltux", "r"),
+    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafiltux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -107,7 +107,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafilrtuxm", NULL),
+    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -1468,11 +1468,11 @@ getasg(char *s)
 /* function to set a single parameter */
 
 /**/
-int
+Param
 typeset_single(char *cname, char *pname, Param pm, int func,
-	       int on, int off, int roff, char *value)
+	       int on, int off, int roff, char *value, Param altpm)
 {
-    int usepm, tc;
+    int usepm, tc, keeplocal = 0;
 
     /* use the existing pm? */
     usepm = pm && !(pm->flags & PM_UNSET);
@@ -1490,24 +1490,24 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	locallevel != pm->level && func != BIN_EXPORT)
 	usepm = 0;
 
-    /* attempting a type conversion? */
+    /* attempting a type conversion, or making a tied colonarray? */
     if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) &
-			(PM_INTEGER|PM_HASHED|PM_ARRAY))))
+			(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED))))
 	usepm = 0;
     if (tc && (pm->flags & PM_SPECIAL)) {
 	zerrnam(cname, "%s: can't change type of a special parameter",
 		pname, 0);
-	return 1;
+	return NULL;
     }
 
     if (usepm) {
 	if (!on && !roff && !value) {
 	    paramtab->printnode((HashNode)pm, 0);
-	    return 0;
+	    return pm;
 	}
 	if ((pm->flags & PM_RESTRICTED && isset(RESTRICTED))) {
 	    zerrnam(cname, "%s: restricted", pname, 0);
-	    return 1;
+	    return pm;
 	}
 	if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
 	    !(pm->flags & PM_READONLY & ~off))
@@ -1530,9 +1530,9 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 		setsparam(pname, ztrdup(value));
 	} else if (value) {
 	    zwarnnam(cname, "can't assign new value for array %s", pname, 0);
-	    return 1;
+	    return NULL;
 	}
-	return 0;
+	return pm;
     }
 
     /*
@@ -1542,10 +1542,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
      * last case only, we need to delete the old parameter.
      */
     if (tc) {
-	if (pm->flags & PM_READONLY) {
-	    on |= ~off & PM_READONLY;
-	    pm->flags &= ~PM_READONLY;
-	}
+	/* Maintain existing readonly/exported status... */
+	on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->flags;
+	/* ...but turn off existing readonly so we can delete it */
+	pm->flags &= ~PM_READONLY;
+	/*
+	 * If we're just changing the type, we should keep the
+	 * variable at the current level of localness.
+	 */
+	keeplocal = pm->level;
 	/*
 	 * Try to carry over a value, but not when changing from,
 	 * to, or between non-scalar types.
@@ -1563,17 +1568,33 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     pm = createparam(pname, on & ~PM_READONLY);
     DPUTS(!pm, "BUG: parameter not created");
     pm->ct = auxlen;
-    if (func != BIN_EXPORT)
+
+    if (altpm && PM_TYPE(pm->flags) == PM_SCALAR) {
+	/*
+	 * It seems safer to set this here than in createparam(),
+	 * to make sure we only ever use the colonarr functions
+	 * when u.data is correctly set.
+	 */
+	pm->sets.cfn = colonarrsetfn;
+	pm->gets.cfn = colonarrgetfn;
+	pm->u.data = &altpm->u.arr;
+    }
+
+    if (keeplocal)
+	pm->level = keeplocal;
+    else if (func != BIN_EXPORT)
 	pm->level = locallevel;
     if (value && !(pm->flags & (PM_ARRAY|PM_HASHED)))
 	setsparam(pname, ztrdup(value));
     pm->flags |= (on & PM_READONLY);
     if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) {
 	zerrnam(cname, "%s: can't assign initial value for array", pname, 0);
-	return 1;
+	/* the only safe thing to do here seems to be unset the param */
+	unsetparam_pm(pm, 0, 1);
+	return NULL;
     }
 
-    return 0;
+    return pm;
 }
 
 /* declare, export, integer, local, readonly, typeset */
@@ -1585,7 +1606,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     Param pm;
     Asgment asg;
     Comp com;
-    char *optstr = "aiALRZlurtxU";
+    char *optstr = "aiALRZlurtxUT";
     int on = 0, off = 0, roff, bit = PM_ARRAY;
     int i;
     int returnval = 0, printflags = 0;
@@ -1619,6 +1640,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	off |= PM_UPPER;
     if (on & PM_HASHED)
 	off |= PM_ARRAY;
+    if (on & PM_TIED)
+	off |= PM_INTEGER | PM_ARRAY | PM_HASHED;
+
     on &= ~off;
 
     /* Given no arguments, list whatever the options specify. */
@@ -1631,6 +1655,58 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
+    if (on & PM_TIED) {
+	Param apm;
+	char *name1;
+
+	if (ops['m']) {
+	    zwarnnam(name, "incompatible options for -T", NULL, 0);
+	    return 1;
+	}
+	on &= ~off;
+	if (!argv[1] || argv[2]) {
+	    zwarnnam(name, "-T requires names of scalar and array", NULL, 0);
+	    return 1;
+	}
+
+	/*
+	 * 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)))
+	    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,
+				(Param)paramtab->getnode(paramtab,
+							 asg->name),
+				func, on, off, roff, asg->value, apm))) {
+	    unsetparam_pm(apm, 1, 1);
+	    return 1;
+	}
+
+	pm->ename = name1;
+	apm->ename = ztrdup(asg->name);
+
+	return 0;
+    }
+    if (off & PM_TIED) {
+	zerrnam(name, "use unset to remove tied variables", NULL, 0);
+	return 1;
+    }
+
     /* With the -m option, treat arguments as glob patterns */
     if (ops['m']) {
 	MUSTUSEHEAP("typeset -m");
@@ -1664,8 +1740,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    }
 	    for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
 		pm = (Param) getdata(pmnode);
-		if (typeset_single(name, pm->nam, pm, func, on, off, roff,
-				   asg->value))
+		if (!typeset_single(name, pm->nam, pm, func, on, off, roff,
+				    asg->value, NULL))
 		    returnval = 1;
 	    }
 	}
@@ -1680,9 +1756,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    returnval = 1;
 	    continue;
 	}
-	if (typeset_single(name, asg->name,
-			   (Param)paramtab->getnode(paramtab, asg->name),
-			   func, on, off, roff, asg->value))
+	if (!typeset_single(name, asg->name,
+			    (Param)paramtab->getnode(paramtab, asg->name),
+			    func, on, off, roff, asg->value, NULL))
 	    returnval = 1;
     }
     return returnval;
diff --git a/Src/glob.c b/Src/glob.c
index 6536e0f06..7a3839576 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -31,7 +31,42 @@
 #include "glob.pro"
 
 /* flag for CSHNULLGLOB */
- 
+
+typedef struct gmatch *Gmatch; 
+
+struct gmatch {
+    char *name;
+    long size;
+    long atime;
+    long mtime;
+    long ctime;
+    long links;
+    long _size;
+    long _atime;
+    long _mtime;
+    long _ctime;
+    long _links;
+};
+
+#define GS_NAME   1
+#define GS_SIZE   2
+#define GS_ATIME  4
+#define GS_MTIME  8
+#define GS_CTIME 16
+#define GS_LINKS 32
+
+#define GS_SHIFT  5
+#define GS__SIZE  (GS_SIZE << GS_SHIFT)
+#define GS__ATIME (GS_ATIME << GS_SHIFT)
+#define GS__MTIME (GS_MTIME << GS_SHIFT)
+#define GS__CTIME (GS_CTIME << GS_SHIFT)
+#define GS__LINKS (GS_LINKS << GS_SHIFT)
+
+#define GS_DESC  2048
+
+#define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS)
+#define GS_LINKED (GS_NORMAL << GS_SHIFT)
+
 /**/
 int badcshglob;
  
@@ -42,8 +77,8 @@ static int matchct;		/* number of matches found              */
 static char *pathbuf;		/* pathname buffer                      */
 static int pathbufsz;		/* size of pathbuf			*/
 static int pathbufcwd;		/* where did we chdir()'ed		*/
-static char **matchbuf;		/* array of matches                     */
-static char **matchptr;		/* &matchbuf[matchct]                   */
+static Gmatch matchbuf;		/* array of matches                     */
+static Gmatch matchptr;		/* &matchbuf[matchct]                   */
 static char *colonmod;		/* colon modifiers in qualifier list    */
 
 typedef struct stat *Statptr;	 /* This makes the Ultrix compiler happy.  Go figure. */
@@ -81,6 +116,7 @@ static struct qual *quals;
 static int qualct, qualorct;
 static int range, amc, units;
 static int gf_nullglob, gf_markdirs, gf_noglobdots, gf_listtypes, gf_follow;
+static int gf_sorts, gf_nsorts, gf_sortlist[11];
 
 /* Prefix, suffix for doing zle trickery */
 
@@ -213,6 +249,7 @@ insert(char *s, int checked)
 
 	if (!statted && statfullpath(s, &buf, 1))
 	    return;
+	statted = 1;
 	qo = quals;
 	for (qn = qo; qn && qn->func;) {
 	    range = qn->range;
@@ -237,19 +274,44 @@ insert(char *s, int checked)
 	    }
 	    qn = qn->next;
 	}
-    } else if (!checked && statfullpath(s, NULL, 1))
-	return;
-
+    } else if (!checked) {
+	if (statfullpath(s, NULL, 1))
+	    return;
+	statted = 1;
+    }
     news = dyncat(pathbuf, news);
     if (colonmod) {
 	/* Handle the remainder of the qualifer:  e.g. (:r:s/foo/bar/). */
 	s = colonmod;
 	modify(&news, &s);
     }
-    *matchptr++ = news;
+    if (!statted && (gf_sorts & GS_NORMAL)) {
+	statfullpath(s, &buf, 1);
+	statted = 1;
+    }
+    if (statted != 2 && (gf_sorts & GS_LINKED)) {
+	if (statted) {
+	    if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0))
+		memcpy(&buf2, &buf, sizeof(buf));
+	} else if (statfullpath(s, &buf2, 0))
+	    statfullpath(s, &buf2, 1);
+    }
+    matchptr->name = news;
+    matchptr->size = buf.st_size;
+    matchptr->atime = buf.st_atime;
+    matchptr->mtime = buf.st_mtime;
+    matchptr->ctime = buf.st_ctime;
+    matchptr->links = buf.st_nlink;
+    matchptr->_size = buf2.st_size;
+    matchptr->_atime = buf2.st_atime;
+    matchptr->_mtime = buf2.st_mtime;
+    matchptr->_ctime = buf2.st_ctime;
+    matchptr->_links = buf2.st_nlink;
+    matchptr++;
+
     if (++matchct == matchsz) {
-	matchbuf = (char **)realloc((char *)matchbuf,
-				    sizeof(char **) * (matchsz *= 2));
+	matchbuf = (Gmatch )realloc((char *)matchbuf,
+				    sizeof(struct gmatch) * (matchsz *= 2));
 
 	matchptr = matchbuf + matchct;
     }
@@ -944,21 +1006,144 @@ qgetnum(char **s)
     return v;
 }
 
-/* get octal number after qualifier */
+/* get mode spec after qualifier */
 
 /**/
 static long
-qgetoctnum(char **s)
+qgetmodespec(char **s)
 {
-    long v = 0;
+    long yes = 0, no = 0, val, mask, t;
+    char *p = *s, c, how, end;
 
-    if (!idigit(**s)) {
-	zerr("octal number expected", NULL, 0);
-	return 0;
+    if ((c = *p) == '=' || c == Equals || c == '+' || c == '-' ||
+	c == '?' || c == Quest || (c >= '0' && c <= '7')) {
+	end = 0;
+	c = 0;
+    } else {
+	end = (c == '<' ? '>' :
+	       (c == '[' ? ']' :
+		(c == '{' ? '}' :
+		 (c == Inang ? Outang :
+		  (c == Inbrack ? Outbrack :
+		   (c == Inbrace ? Outbrace : c))))));
+	p++;
     }
-    while (**s >= '0' && **s <= '7')
-	v = v * 010 + *(*s)++ - '0';
-    return v;
+    do {
+	mask = 0;
+	while (((c = *p) == 'u' || c == 'g' || c == 'o' || c == 'a') && end) {
+	    switch (c) {
+	    case 'o': mask |= 01007; break;
+	    case 'g': mask |= 02070; break;
+	    case 'u': mask |= 04700; break;
+	    case 'a': mask |= 07777; break;
+	    }
+	    p++;
+	}
+	how = ((c == '+' || c == '-') ? c : '=');
+	if (c == '+' || c == '-' || c == '=' || c == Equals)
+	    p++;
+	val = 0;
+	if (mask) {
+	    while ((c = *p++) != ',' && c != end) {
+		switch (c) {
+		case 'x': val |= 00111; break;
+		case 'w': val |= 00222; break;
+		case 'r': val |= 00444; break;
+		case 's': val |= 06000; break;
+		case 't': val |= 01000; break;
+		case '0': case '1': case '2': case '3':
+		case '4': case '5': case '6': case '7':
+		    t = ((long) c - '0');
+		    val |= t | (t << 3) | (t << 6);
+		    break;
+		default:
+		    zerr("invalid mode specification", NULL, 0);
+		    return 0;
+		}
+	    }
+	    if (how == '=' || how == '+') {
+		yes |= val & mask;
+		val = ~val;
+	    }
+	    if (how == '=' || how == '-')
+		no |= val & mask;
+	} else {
+	    t = 07777;
+	    while ((c = *p) == '?' || c == Quest ||
+		   (c >= '0' && c <= '7')) {
+		if (c == '?' || c == Quest) {
+		    t = (t << 3) | 7;
+		    val <<= 3;
+		} else {
+		    t <<= 3;
+		    val = (val << 3) | ((long) c - '0');
+		}
+		p++;
+	    }
+	    if (end && c != end && c != ',') {
+		zerr("invalid mode specification", NULL, 0);
+		return 0;
+	    }
+	    if (how == '=') {
+		yes = (yes & ~t) | val;
+		no = (no & ~t) | (~val & ~t);
+	    } else if (how == '+')
+		yes |= val;
+	    else
+		no |= val;
+	}
+    } while (end && c != end);
+
+    *s = p;
+    return ((yes & 07777) | ((no & 07777) << 12));
+}
+
+static int
+gmatchcmp(Gmatch a, Gmatch b)
+{
+    int i, *s;
+    long r;
+
+    for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
+	switch (*s & ~GS_DESC) {
+	case GS_NAME:
+	    r = notstrcmp(&a->name, &b->name);
+	    break;
+	case GS_SIZE:
+	    r = b->size - a->size;
+	    break;
+	case GS_ATIME:
+	    r = a->atime - b->atime;
+	    break;
+	case GS_MTIME:
+	    r = a->mtime - b->mtime;
+	    break;
+	case GS_CTIME:
+	    r = a->ctime - b->ctime;
+	    break;
+	case GS_LINKS:
+	    r = b->links - a->links;
+	    break;
+	case GS__SIZE:
+	    r = b->_size - a->_size;
+	    break;
+	case GS__ATIME:
+	    r = a->_atime - b->_atime;
+	    break;
+	case GS__MTIME:
+	    r = a->_mtime - b->_mtime;
+	    break;
+	case GS__CTIME:
+	    r = a->_ctime - b->_ctime;
+	    break;
+	case GS__LINKS:
+	    r = b->_links - a->_links;
+	    break;
+	}
+	if (r)
+	    return (int) ((*s & GS_DESC) ? -r : r);
+    }
+    return 0;
 }
 
 /* Main entry point to the globbing code for filename globbing. *
@@ -976,7 +1161,8 @@ glob(LinkList list, LinkNode np)
     Complist q;				/* pattern after parsing         */
     char *ostr = (char *)getdata(np);	/* the pattern before the parser */
 					/* chops it up                   */
-
+    int first = 0, last = -1;		/* index of first/last match to  */
+				        /* return */
     MUSTUSEHEAP("glob");
     if (unset(GLOBOPT) || !haswilds(ostr)) {
 	untokenize(ostr);
@@ -994,6 +1180,7 @@ glob(LinkList list, LinkNode np)
     gf_markdirs = isset(MARKDIRS);
     gf_listtypes = gf_follow = 0;
     gf_noglobdots = unset(GLOBDOTS);
+    gf_sorts = gf_nsorts = 0;
 
     /* Check for qualifiers */
     if (isset(BAREGLOBQUAL) && str[sl - 1] == Outpar) {
@@ -1246,10 +1433,9 @@ glob(LinkList list, LinkNode np)
 			}
 			break;
 		    case 'o':
-			/* Match octal mode of file exactly. *
-			 * Currently undocumented.           */
-			func = qualeqflags;
-			data = qgetoctnum(&s);
+			/* Match modes with chmod-spec. */
+			func = qualmodeflags;
+			data = qgetmodespec(&s);
 			break;
 		    case 'M':
 			/* Mark directories with a / */
@@ -1315,6 +1501,51 @@ glob(LinkList list, LinkNode np)
 			data = qgetnum(&s);
 			break;
 
+		    case 'O':
+			{
+			    int t;
+
+			    switch (*s) {
+			    case 'n': t = GS_NAME; break;
+			    case 'L': t = GS_SIZE; break;
+			    case 'l': t = GS_LINKS; break;
+			    case 'a': t = GS_ATIME; break;
+			    case 'm': t = GS_MTIME; break;
+			    case 'c': t = GS_CTIME; break;
+			    default:
+				zerr("unknown sort specifier", NULL, 0);
+				return;
+			    }
+			    if ((sense & 2) && t != GS_NAME)
+				t <<= GS_SHIFT;
+			    if (gf_sorts & t) {
+				zerr("doubled sort specifier", NULL, 0);
+				return;
+			    }
+			    gf_sorts |= t;
+			    gf_sortlist[gf_nsorts++] = t |
+				((sense & 1) ? GS_DESC : 0);
+			    s++;
+			    break;
+			}
+		    case '[':
+		    case Inbrack:
+			{
+			    char *os = --s;
+			    struct value v;
+
+			    v.isarr = SCANPM_WANTVALS;
+			    v.pm = NULL;
+			    v.b = -1;
+			    v.inv = 0;
+			    if (getindex(&s, &v) || s == os) {
+				zerr("invalid subscript", NULL, 0);
+				return;
+			    }
+			    first = v.a;
+			    last = v.b;
+			    break;
+			}
 		    default:
 			zerr("unknown file attribute", NULL, 0);
 			return;
@@ -1353,10 +1584,14 @@ glob(LinkList list, LinkNode np)
 	zerr("bad pattern: %s", ostr, 0);
 	return;
     }
-
+    if (!gf_nsorts) {
+	gf_sortlist[0] = gf_sorts = GS_NAME;
+	gf_nsorts = 1;
+    }
     /* Initialise receptacle for matched files, *
      * expanded by insert() where necessary.    */
-    matchptr = matchbuf = (char **)zalloc((matchsz = 16) * sizeof(char *));
+    matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
+					 sizeof(struct gmatch));
     matchct = 0;
 
     /* The actual processing takes place here: matches go into  *
@@ -1375,18 +1610,32 @@ glob(LinkList list, LinkNode np)
 	    return;
 	} else {
 	    /* treat as an ordinary string */
-	    untokenize(*matchptr++ = dupstring(ostr));
+	    untokenize(matchptr->name = dupstring(ostr));
+	    matchptr++;
 	    matchct = 1;
 	}
     }
     /* Sort arguments in to lexical (and possibly numeric) order. *
      * This is reversed to facilitate insertion into the list.    */
-    qsort((void *) & matchbuf[0], matchct, sizeof(char *),
-	       (int (*) _((const void *, const void *)))notstrcmp);
-
-    matchptr = matchbuf;
-    while (matchct--)		/* insert matches in the arg list */
-	insertlinknode(list, node, *matchptr++);
+    qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
+	       (int (*) _((const void *, const void *)))gmatchcmp);
+
+    if (first < 0)
+	first += matchct;
+    if (last < 0)
+	last += matchct;
+    if (first < 0)
+	first = 0;
+    if (last >= matchct)
+	last = matchct - 1;
+    if (first <= last) {
+	matchptr = matchbuf + matchct - 1 - last;
+	last -= first;
+	while (last-- >= 0) {		/* insert matches in the arg list */
+	    insertlinknode(list, node, matchptr->name);
+	    matchptr++;
+	}
+    }
     free(matchbuf);
 }
 
@@ -2918,13 +3167,15 @@ qualflags(struct stat *buf, long mod)
     return mode_to_octal(buf->st_mode) & mod;
 }
 
-/* mode matches number supplied exactly  */
+/* mode matches specification */
 
 /**/
 static int
-qualeqflags(struct stat *buf, long mod)
+qualmodeflags(struct stat *buf, long mod)
 {
-    return mode_to_octal(buf->st_mode) == mod;
+    long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12;
+
+    return ((v & y) == y && !(v & n));
 }
 
 /* regular executable file? */
diff --git a/Src/init.c b/Src/init.c
index 0c874eead..5e0a550dd 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -300,18 +300,61 @@ init_io(void)
 
     /* Make sure the tty is opened read/write. */
     if (isatty(0)) {
+#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)
+		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
+	 * unable to open /dev/tty or /dev/pts/<terminal number here>
+	 * because something in Sun's STREAMS modules doesn't like
+	 * it. The open() call fails with EBUSY which is not even
+	 * listed as a possibility in the open(2) man page.  So we'll
+	 * try to outsmart The Company.  -- <dave@srce.hr>
+	 *
+	 * Presumably there's no harm trying this on any OS, given that
+	 * isatty(0) worked but opening the tty didn't.  Possibly we won't
+	 * get the tty read/write, but it's the best we can do -- pws
+	 *
+	 * Try both stdin and stdout before trying /dev/tty. -- Bart
+	 */
+#if defined(HAVE_FCNTL_H) && defined(F_GETFL)
+#define rdwrtty(fd)	((fcntl(fd, F_GETFL) & O_RDWR) == O_RDWR)
+#else
+#define rdwrtty(fd)	1
+#endif
+	if (SHTTY == -1 && rdwrtty(0)) {
+	    SHTTY = movefd(dup(0));
+	}
+    }
+    if (SHTTY == -1 && isatty(1) && rdwrtty(1) &&
+	(SHTTY = movefd(dup(1))) != -1) {
+	zsfree(ttystrname);
+	ttystrname = ztrdup(ttyname(1));
     }
     if (SHTTY == -1 &&
 	(SHTTY = movefd(open("/dev/tty", O_RDWR | O_NOCTTY))) != -1) {
 	zsfree(ttystrname);
-	ttystrname = ztrdup("/dev/tty");
+	ttystrname = ztrdup(ttyname(SHTTY));
     }
     if (SHTTY == -1) {
 	zsfree(ttystrname);
 	ttystrname = ztrdup("");
+    } else if (!ttystrname) {
+	ttystrname = ztrdup("/dev/tty");
     }
 
     /* We will only use zle if shell is interactive, *
diff --git a/Src/lex.c b/Src/lex.c
index b08dfed5b..7371243a7 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -109,7 +109,8 @@ int parend;
  
 /* text of puctuation tokens */
 
-static char *tokstrings[WHILE + 1] = {
+/**/
+char *tokstrings[WHILE + 1] = {
     NULL,	/* NULLTOK	  0  */
     ";",	/* SEPER	     */
     "\\n",	/* NEWLIN	     */
@@ -120,7 +121,7 @@ static char *tokstrings[WHILE + 1] = {
     ")",	/* OUTPAR	     */
     "||",	/* DBAR		     */
     "&&",	/* DAMPER	     */
-    ")",	/* OUTANG	  10 */
+    ">",	/* OUTANG	  10 */
     ">|",	/* OUTANGBANG	     */
     ">>",	/* DOUTANG	     */
     ">>|",	/* DOUTANGBANG	     */
diff --git a/Src/params.c b/Src/params.c
index 3dc7e1a1b..f57413a2e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -679,7 +679,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
     Comp c;
 
     /* first parse any subscription flags */
-    if (*s == '(' || *s == Inpar) {
+    if (v->pm && (*s == '(' || *s == Inpar)) {
 	int escapes = 0;
 	int waste;
 	for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
@@ -765,7 +765,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 	} else if (rev) {
 	    v->isarr |= SCANPM_WANTVALS;
 	}
-	if (!down && PM_TYPE(v->pm->flags) == PM_HASHED)
+	if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED)
 	    v->isarr &= ~SCANPM_MATCHMANY;
 	*inv = ind;
     }
@@ -785,7 +785,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
     singsub(&s);
 
     if (!rev) {
-	if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+	if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) {
 	    HashTable ht = v->pm->gets.hfn(v->pm);
 	    if (!ht) {
 		ht = newparamtable(17, v->pm->nam);
@@ -1371,7 +1371,7 @@ setarrvalue(Value v, char **val)
 	freearray(val);
 	return;
     }
-    if (PM_TYPE(v->pm->flags) & ~(PM_ARRAY|PM_HASHED)) {
+    if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED))) {
 	freearray(val);
 	zerr("attempt to assign array value to non-array", NULL, 0);
 	return;
@@ -1501,7 +1501,7 @@ setsparam(char *s, char *val)
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_SCALAR);
 	else if (PM_TYPE(v->pm->flags) == PM_ARRAY &&
-		 !(v->pm->flags & PM_SPECIAL) && unset(KSHARRAYS)) {
+		 !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) {
 	    unsetparam(t);
 	    createparam(t, PM_SCALAR);
 	    v = NULL;
@@ -1545,7 +1545,7 @@ setaparam(char *s, char **val)
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_ARRAY);
 	else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
-		 !(v->pm->flags & PM_SPECIAL)) {
+		 !(v->pm->flags & (PM_SPECIAL|PM_TIED))) {
 	    int uniq = v->pm->flags & PM_UNIQUE;
 	    unsetparam(t);
 	    createparam(t, PM_ARRAY | uniq);
@@ -1662,26 +1662,36 @@ unsetparam_pm(Param pm, int altflag, int exp)
 	    unsetparam_pm(altpm, 1, exp);
     }
 
-    /* If this was a local variable, we need to keep the old     *
-     * struct so that it is resurrected at the right level.      *
-     * This is partly because when an array/scalar value is set  *
-     * and the parameter used to be the other sort, unsetparam() *
-     * is called.  Beyond that, there is an ambiguity:  should   *
-     * foo() { local bar; unset bar; } make the global bar       *
-     * available or not?  The following makes the answer "no".   */
-    if ((locallevel && locallevel >= pm->level) || (pm->flags & PM_SPECIAL))
+    /*
+     * If this was a local variable, we need to keep the old
+     * struct so that it is resurrected at the right level.
+     * This is partly because when an array/scalar value is set
+     * and the parameter used to be the other sort, unsetparam()
+     * is called.  Beyond that, there is an ambiguity:  should
+     * foo() { local bar; unset bar; } make the global bar
+     * available or not?  The following makes the answer "no".
+     *
+     * Some specials, such as those used in zle, still need removing
+     * from the parameter table; they have the PM_REMOVABLE flag.
+     */
+    if ((locallevel && locallevel >= pm->level) ||
+	(pm->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL)
 	return;
 
-    paramtab->removenode(paramtab, pm->nam); /* remove parameter node from table */
+    /* remove parameter node from table */
+    paramtab->removenode(paramtab, pm->nam);
 
     if (pm->old) {
 	oldpm = pm->old;
 	paramtab->addnode(paramtab, oldpm->nam, oldpm);
-	if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && oldpm->sets.cfn == strsetfn)
+	if ((PM_TYPE(oldpm->flags) == PM_SCALAR) &&
+	    oldpm->sets.cfn == strsetfn)
 	    adduserdir(oldpm->nam, oldpm->u.str, 0, 0);
     }
 
-    paramtab->freenode((HashNode) pm); /* free parameter node */
+    /* Even removable specials shouldn't be deleted. */
+    if (!(pm->flags & PM_SPECIAL))
+	paramtab->freenode((HashNode) pm); /* free parameter node */
 }
 
 /* Standard function to unset a parameter.  This is mostly delegated to *
@@ -1759,6 +1769,9 @@ arrsetfn(Param pm, char **x)
     if (pm->flags & PM_UNIQUE)
 	uniqarray(x);
     pm->u.arr = x;
+    /* Arrays tied to colon-arrays may need to fix the environment */
+    if (pm->ename && x)
+	arrfixenv(pm->ename, x);
 }
 
 /* Function to get value of an association parameter */
@@ -1950,7 +1963,8 @@ arrvarsetfn(Param pm, char **x)
 char *
 colonarrgetfn(Param pm)
 {
-    return zjoin(*(char ***)pm->u.data, ':');
+    char ***dptr = (char ***)pm->u.data;
+    return *dptr ? zjoin(*dptr, ':') : "";
 }
 
 /**/
@@ -1959,8 +1973,15 @@ colonarrsetfn(Param pm, char *x)
 {
     char ***dptr = (char ***)pm->u.data;
 
-    freearray(*dptr);
-    *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : mkarray(NULL);
+    /*
+     * If this is tied to a parameter (rather than internal) array,
+     * the array itself may be NULL.  Otherwise, we have to make
+     * sure it doesn't ever get null.
+     */
+    if (*dptr)
+	freearray(*dptr);
+    *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) :
+	(pm->flags & PM_TIED) ? NULL : mkarray(NULL);
     if (pm->ename)
 	arrfixenv(pm->nam, *dptr);
     zsfree(x);
@@ -2399,7 +2420,7 @@ arrfixenv(char *s, char **t)
     MUSTUSEHEAP("arrfixenv");
     if (t == path)
 	cmdnamtab->emptytable(cmdnamtab);
-    u = zjoin(t, ':');
+    u = t ? zjoin(t, ':') : "";
     len_s = strlen(s);
     pm = (Param) paramtab->getnode(paramtab, s);
     for (ep = environ; *ep; ep++)
@@ -2602,6 +2623,9 @@ freeparamnode(HashNode hn)
     if (delunset)
 	pm->unsetfn(pm, 1);
     zsfree(pm->nam);
+    /* If this variable was tied by the user, ename was ztrdup'd */
+    if (pm->flags & PM_TIED)
+	zsfree(pm->ename);
     zfree(pm, sizeof(struct param));
 }
 
diff --git a/Src/subst.c b/Src/subst.c
index 5211ee0ee..b77dbd4a4 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -702,6 +702,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     int whichlen = 0;
     int chkset = 0;
     int vunset = 0;
+    int wantt = 0;
     int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
     char *val = NULL, **aval = NULL;
     unsigned int fwidth = 0;
@@ -902,6 +903,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    hvals = SCANPM_WANTVALS;
 		    break;
 
+		case 't':
+		    wantt = 1;
+		    break;
+
 		default:
 		  flagerr:
 		    zerr("error in flags", NULL, 0);
@@ -978,9 +983,47 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	*s = sav;
 	v = (Value) NULL;
     } else {
-	if (!(v = fetchvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1,
+	if (!(v = fetchvalue(&s, (wantt ? -1 :
+				  ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
 			     hkeys|hvals)))
 	    vunset = 1;
+
+	if (wantt) {
+	    if (v) {
+		int f = v->pm->flags;
+
+		switch (PM_TYPE(f)) {
+		case PM_SCALAR:  val = "scalar"; break;
+		case PM_ARRAY:   val = "array"; break;
+		case PM_INTEGER: val = "integer"; break;
+		case PM_HASHED:  val = "association"; break;
+		}
+		val = dupstring(val);
+		if (f & PM_LEFT)
+		    val = dyncat(val, "-left");
+		if (f & PM_RIGHT_B)
+		    val = dyncat(val, "-right_blanks");
+		if (f & PM_RIGHT_Z)
+		    val = dyncat(val, "-right_zeros");
+		if (f & PM_LOWER)
+		    val = dyncat(val, "-lower");
+		if (f & PM_UPPER)
+		    val = dyncat(val, "-upper");
+		if (f & PM_READONLY)
+		    val = dyncat(val, "-readonly");
+		if (f & PM_TAGGED)
+		    val = dyncat(val, "-tag");
+		if (f & PM_EXPORTED)
+		    val = dyncat(val, "-export");
+		if (f & PM_UNIQUE)
+		    val = dyncat(val, "-unique");
+		vunset = 0;
+	    } else
+		val = dupstring("");
+
+	    v = NULL;
+	    isarr = 0;
+	}
     }
     while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
 	if (!v) {
diff --git a/Src/zsh.export b/Src/zsh.export
index c85bfbad4..32c0e7d3c 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -214,6 +214,7 @@ tgoto
 tok
 tokenize
 tokstr
+tokstrings
 tputs
 trashzleptr
 tricat
diff --git a/Src/zsh.h b/Src/zsh.h
index dabcd90c8..e23f9c895 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -780,6 +780,7 @@ struct shfunc {
 #define SFC_HOOK     2		/* one of the special functions */
 #define SFC_WIDGET   3		/* user defined widget */
 #define SFC_COMPLETE 4		/* called from completion code */
+#define SFC_CWIDGET  5		/* new style completion widget */
 
 /* node in list of function call wrappers */
 
@@ -916,10 +917,12 @@ struct param {
 #define PM_TAGGED	(1<<9)	/* tagged                                     */
 #define PM_EXPORTED	(1<<10)	/* exported                                   */
 #define PM_UNIQUE	(1<<11)	/* remove duplicates                          */
-#define PM_SPECIAL	(1<<12) /* special builtin parameter                  */
-#define PM_DONTIMPORT	(1<<13)	/* do not import this variable                */
-#define PM_RESTRICTED	(1<<14) /* cannot be changed in restricted mode       */
-#define PM_UNSET	(1<<15)	/* has null value                             */
+#define PM_TIED 	(1<<12)	/* array tied to colon-path or v.v. */
+#define PM_SPECIAL	(1<<13) /* special builtin parameter                  */
+#define PM_DONTIMPORT	(1<<14)	/* do not import this variable                */
+#define PM_RESTRICTED	(1<<15) /* cannot be changed in restricted mode       */
+#define PM_UNSET	(1<<16)	/* has null value                             */
+#define PM_REMOVABLE	(1<<17)	/* special can be removed from paramtab */
 
 /* Flags for extracting elements of arrays and associative arrays */
 #define SCANPM_WANTVALS   (1<<0)