about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Makemod.in.in23
-rw-r--r--Src/Modules/curses.c8
-rw-r--r--Src/Modules/files.c4
-rw-r--r--Src/Modules/hlgroup.c216
-rw-r--r--Src/Modules/hlgroup.mdd7
-rw-r--r--Src/Modules/ksh93.c287
-rw-r--r--Src/Modules/ksh93.mdd10
-rw-r--r--Src/Modules/mapfile.c10
-rw-r--r--Src/Modules/param_private.c149
-rw-r--r--Src/Modules/parameter.c28
-rw-r--r--Src/Modules/pcre.c327
-rw-r--r--Src/Modules/pcre.mdd2
-rw-r--r--Src/Modules/stat.c2
-rw-r--r--Src/Modules/terminfo.c4
-rw-r--r--Src/Modules/watch.c99
-rw-r--r--Src/Modules/zftp.c16
-rw-r--r--Src/Modules/zprof.c6
-rw-r--r--Src/Modules/zpty.c2
-rw-r--r--Src/Modules/zutil.c65
-rw-r--r--Src/Zle/compcore.c19
-rw-r--r--Src/Zle/complete.c6
-rw-r--r--Src/Zle/complist.c81
-rw-r--r--Src/Zle/compmatch.c8
-rw-r--r--Src/Zle/compresult.c12
-rw-r--r--Src/Zle/computil.c4
-rw-r--r--Src/Zle/textobjects.c6
-rw-r--r--Src/Zle/zle.h17
-rw-r--r--Src/Zle/zle_hist.c12
-rw-r--r--Src/Zle/zle_keymap.c64
-rw-r--r--Src/Zle/zle_main.c33
-rw-r--r--Src/Zle/zle_refresh.c500
-rw-r--r--Src/Zle/zle_thingy.c6
-rw-r--r--Src/Zle/zle_tricky.c71
-rw-r--r--Src/Zle/zle_utils.c55
-rw-r--r--Src/Zle/zle_vi.c9
-rw-r--r--Src/builtin.c354
-rw-r--r--Src/compat.c100
-rw-r--r--Src/exec.c232
-rw-r--r--Src/glob.c17
-rw-r--r--Src/hashtable.c28
-rw-r--r--Src/hist.c69
-rw-r--r--Src/init.c178
-rw-r--r--Src/input.c46
-rw-r--r--Src/jobs.c226
-rw-r--r--Src/lex.c125
-rw-r--r--Src/loop.c26
-rw-r--r--Src/makepro.awk6
-rw-r--r--Src/math.c44
-rw-r--r--Src/mem.c8
-rw-r--r--Src/mkbltnmlst.sh36
-rw-r--r--Src/modentry.c10
-rw-r--r--Src/module.c34
-rw-r--r--Src/options.c13
-rw-r--r--Src/params.c524
-rw-r--r--Src/parse.c36
-rw-r--r--Src/pattern.c104
-rw-r--r--Src/prompt.c392
-rw-r--r--Src/prototypes.h54
-rw-r--r--Src/signals.c100
-rw-r--r--Src/signals.h15
-rw-r--r--Src/signames2.awk18
-rw-r--r--Src/sort.c4
-rw-r--r--Src/subst.c358
-rw-r--r--Src/text.c2
-rw-r--r--Src/utils.c444
-rw-r--r--Src/zsh.h226
-rw-r--r--Src/zsh_system.h11
-rw-r--r--Src/ztype.h3
68 files changed, 4202 insertions, 1809 deletions
diff --git a/Src/Makemod.in.in b/Src/Makemod.in.in
index ea0cdc3a4..3343ae1d0 100644
--- a/Src/Makemod.in.in
+++ b/Src/Makemod.in.in
@@ -52,32 +52,17 @@ DLCOMPILE   = $(CC) -c -I. -I$(dir_top)/Src -I$(sdir_top)/Src -I$(sdir_top)/Src/
 LINK        = $(CC) $(LDFLAGS) $(EXELDFLAGS) $(EXTRA_LDFLAGS) -o $@
 DLLINK      = $(DLLD) $(LDFLAGS) $(LIBLDFLAGS) $(DLLDFLAGS) -o $@
 
-KNR_OBJ=.o
-KNROBJ=._foo_
+OBJ=.o
 
-ANSIOBJ=.o
-ANSI_OBJ=._foo_
+.SUFFIXES: .c .$(DL_EXT) ..o .o .syms .pro .epro
 
-.SUFFIXES: .c .$(DL_EXT) ..o .._foo_ .o ._foo_ .syms .pro .epro
-
-.c$(ANSI@U@OBJ):
+.c$(OBJ):
 	$(COMPILE) -o $@ $<
 	@rm -f $(dir_src)/stamp-modobjs
 
-.c$(KNR@U@OBJ):
-	@ANSI2KNR@ $< > $@.c
-	$(COMPILE) -o $@ $@.c
-	rm -f $@.c
-	@rm -f $(dir_src)/stamp-modobjs
-
-.c.$(ANSI@U@OBJ):
+.c.$(OBJ):
 	$(DLCOMPILE) -o $@ $<
 
-.c.$(KNR@U@OBJ):
-	@ANSI2KNR@ $< > $@.c
-	$(DLCOMPILE) -o $@ $@.c
-	rm -f $@.c
-
 .c.syms:
 	$(AWK) -f $(sdir_src)/makepro.awk $< $(subdir) > $@
 
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index e46903916..8950cc153 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -1302,7 +1302,7 @@ zccmd_mouse(const char *nam, char **args)
 	    zlong delay;
 
 	    if (!*++args ||
-		((delay = zstrtol(*args, &eptr, 10)), eptr != NULL)) {
+		((delay = zstrtol(*args, &eptr, 10)), *eptr != '\0')) {
 		zwarnnam(nam, "mouse delay requires an integer argument");
 		return 1;
 	    }
@@ -1326,7 +1326,7 @@ zccmd_mouse(const char *nam, char **args)
 		if (old_mask != zcurses_mouse_mask)
 		    zcurses_flags |= ZCF_MOUSE_MASK_CHANGED;
 	    } else {
-		zwarnnam(nam, "unrecognised mouse command: %s", *arg);
+		zwarnnam(nam, "unrecognised mouse command: %s", arg);
 		return 1;
 	    }
 	}
@@ -1426,10 +1426,10 @@ zccmd_querychar(const char *nam, char **args)
     inc &= A_CHARTEXT;
     if (imeta(inc)) {
 	instr[0] = Meta;
-	instr[1] = STOUC(inc ^ 32);
+	instr[1] = (unsigned char) (inc ^ 32);
 	instr[2] = '\0';
     } else {
-	instr[0] = STOUC(inc);
+  	instr[0] = (unsigned char) inc;
 	instr[1] = '\0';
     }
     attrs = inc;
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index bf0e8f8a8..a3fec1daa 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -29,8 +29,8 @@
 
 #include "files.mdh"
 
-typedef int (*MoveFunc) _((char const *, char const *));
-typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
+typedef int (*MoveFunc) (char const *, char const *);
+typedef int (*RecurseFunc) (char *, char *, struct stat const *, void *);
 
 struct recursivecmd;
 
diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c
new file mode 100644
index 000000000..082762623
--- /dev/null
+++ b/Src/Modules/hlgroup.c
@@ -0,0 +1,216 @@
+/*
+ * hlgroup.c - Supporting parameters for highlight groups
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2024 Oliver Kiddle
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Oliver Kiddle or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Oliver Kiddle and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Oliver Kiddle and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Oliver Kiddle and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "hlgroup.mdh"
+#include "hlgroup.pro"
+
+#define GROUPVAR ".zle.hlgroups"
+
+static const struct gsu_scalar pmesc_gsu =
+{ strgetfn, nullstrsetfn, nullunsetfn };
+
+/**/
+static char *
+convertattr(char *attrstr, int sgr)
+{
+    zattr atr;
+    char *r, *s;
+    int len;
+
+    match_highlight(attrstr, &atr, NULL);
+    s = zattrescape(atr, sgr ? NULL : &len);
+
+    if (sgr) {
+	char *c = s, *t = s - 1;
+
+	while (c[0] == '\033' && c[1] == '[') {
+	    c += 2;
+	    while (isdigit(*c) || *c == ';')
+		*++t = *c++;
+	    t++;
+	    if (*c != 'm')
+		break;
+	    *t = ';';
+	    c++;
+	}
+	if (t <= s) { /* always return at least "0" */
+	    *s = '0';
+	    t = s + 1;
+	}
+	*t = '\0';
+	len = t - s;
+    }
+
+    r = dupstring_wlen(s, len);
+    free(s);
+    return r;
+}
+
+/**/
+static HashNode
+getgroup(const char *name, int sgr)
+{
+    Param pm = NULL;
+    HashNode hn;
+    HashTable hlg;
+    Value v;
+    struct value vbuf;
+    char *var = GROUPVAR;
+
+    pm = (Param) hcalloc(sizeof(struct param));
+    pm->gsu.s = &pmesc_gsu;
+    pm->node.nam = dupstring(name);
+    pm->node.flags = PM_SCALAR|PM_SPECIAL;
+
+    if (!(v = getvalue(&vbuf, &var, 0)) ||
+	     PM_TYPE(v->pm->node.flags) != PM_HASHED ||
+	     !(hlg = v->pm->gsu.h->getfn(v->pm)) ||
+	     !(hn = gethashnode2(hlg, name)) ||
+	     (((Param) hn)->node.flags & PM_UNSET))
+    {
+	pm->u.str = dupstring("");
+	pm->node.flags |= PM_UNSET;
+    } else {
+	pm->u.str = convertattr(((Param) hn)->u.str, sgr);
+    }
+
+    return &pm->node;
+}
+
+/**/
+static void
+scangroup(ScanFunc func, int flags, int sgr)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+    HashTable hlg;
+    Value v;
+    struct value vbuf;
+    char *var = GROUPVAR;
+
+    if (!(v = getvalue(&vbuf, &var, 0)) ||
+	     PM_TYPE(v->pm->node.flags) != PM_HASHED)
+	return;
+    hlg = v->pm->gsu.h->getfn(v->pm);
+
+    memset((void *)&pm, 0, sizeof(struct param));
+    pm.node.flags = PM_SCALAR;
+    pm.gsu.s = &pmesc_gsu;
+
+    for (i = 0; i < hlg->hsize; i++)
+	for (hn = hlg->nodes[i]; hn; hn = hn->next) {
+	    pm.u.str = convertattr(((Param) hn)->u.str, sgr);
+	    pm.node.nam = hn->nam;
+	    func(&pm.node, flags);
+	}
+}
+/**/
+static HashNode
+getpmesc(UNUSED(HashTable ht), const char *name)
+{
+    return getgroup(name, 0);
+}
+
+/**/
+static void
+scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+    scangroup(func, flags, 0);
+}
+
+/**/
+static HashNode
+getpmsgr(UNUSED(HashTable ht), const char *name)
+{
+    return getgroup(name, 1);
+}
+
+/**/
+static void
+scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+    scangroup(func, flags, 1);
+}
+
+static struct paramdef partab[] = {
+    SPECIALPMDEF(".zle.esc", PM_READONLY_SPECIAL, 0, getpmesc, scanpmesc),
+    SPECIALPMDEF(".zle.sgr", PM_READONLY_SPECIAL, 0, getpmsgr, scanpmsgr)
+};
+
+static struct features module_features = {
+    NULL, 0,
+    NULL, 0,
+    NULL, 0,
+    partab, sizeof(partab)/sizeof(*partab),
+    0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+    return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
+{
+    return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+    return 0;
+}
diff --git a/Src/Modules/hlgroup.mdd b/Src/Modules/hlgroup.mdd
new file mode 100644
index 000000000..ee3ba7260
--- /dev/null
+++ b/Src/Modules/hlgroup.mdd
@@ -0,0 +1,7 @@
+name=zsh/hlgroup
+link=either
+load=yes
+
+autofeatures="p:.zle.esc p:.zle.sgr"
+
+objects="hlgroup.o"
diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c
new file mode 100644
index 000000000..3206c11f3
--- /dev/null
+++ b/Src/Modules/ksh93.c
@@ -0,0 +1,287 @@
+/*
+ * ksh93.c - support for more ksh93 features
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 Barton E. Schaefer
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Barton E. Schaefer or the Zsh Development
+ * Group be liable to any party for direct, indirect, special, incidental, or
+ * consequential damages arising out of the use of this software and its
+ * documentation, even if Barton E. Schaefer and the Zsh
+ * Development Group have been advised of the possibility of such damage.
+ *
+ * Barton E. Schaefer and the Zsh Development Group
+ * specifically disclaim any warranties, including, but not limited to, the
+ * implied warranties of merchantability and fitness for a particular purpose.
+ * The software provided hereunder is on an "as is" basis, and
+ * Barton E. Schaefer and the Zsh Development Group have no
+ * obligation to provide maintenance, support, updates, enhancements, or
+ * modifications.
+ *
+ */
+
+#include "ksh93.mdh"
+#include "ksh93.pro"
+
+/* Implementing "namespace" requires creating a new keword.  Hrm. */
+
+/*
+ * Standard module configuration/linkage
+ */
+
+static struct builtin bintab[] = {
+    BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gur", "n")
+};
+
+#include "zsh.mdh"
+
+static void
+edcharsetfn(Param pm, char *x)
+{
+    /*
+     * To make this work like ksh, we must intercept $KEYS before the widget
+     * is looked up, so that changing the key sequence causes a different
+     * widget to be substituted.  Somewhat similar to "bindkey -s".
+     *
+     * Ksh93 adds SIGKEYBD to the trap list for this purpose.
+     */
+    ;
+}
+
+static char **
+matchgetfn(Param pm)
+{
+    char **zsh_match = getaparam("match");
+
+    /* For this to work accurately, ksh emulation should always imply
+     * that the (#m) and (#b) extendedglob operators are enabled.
+     *
+     * When we have a 0th element (ksharrays), it is $MATCH.  Elements
+     * 1st and larger mirror the $match array.
+     */
+
+    if (pm->u.arr)
+	freearray(pm->u.arr);
+    if (zsh_match && *zsh_match) {
+	if (isset(KSHARRAYS)) {
+	    char **ap =
+		(char **) zalloc(sizeof(char *) * (arrlen(zsh_match)+1));
+	    pm->u.arr = ap;
+	    *ap++ = ztrdup(getsparam("MATCH"));
+	    while (*zsh_match)
+		*ap = ztrdup(*zsh_match++);
+	} else
+	    pm->u.arr = zarrdup(zsh_match);
+    } else if (isset(KSHARRAYS)) {
+	pm->u.arr = mkarray(ztrdup(getsparam("MATCH")));
+    } else
+	pm->u.arr = NULL;
+
+    return arrgetfn(pm);
+}
+
+static const struct gsu_scalar constant_gsu =
+    { strgetfn, NULL, nullunsetfn };
+
+static const struct gsu_scalar sh_edchar_gsu =
+    { strvargetfn, edcharsetfn, nullunsetfn };
+static const struct gsu_scalar sh_edmode_gsu =
+    { strgetfn, nullstrsetfn, nullunsetfn };
+static const struct gsu_array sh_match_gsu =
+    { matchgetfn, arrsetfn, stdunsetfn };
+static const struct gsu_scalar sh_name_gsu =
+    { strvargetfn, nullstrsetfn, nullunsetfn };
+static const struct gsu_scalar sh_subscript_gsu =
+    { strvargetfn, nullstrsetfn, nullunsetfn };
+
+static char sh_unsetval[2];	/* Dummy to treat as NULL */
+static char *sh_name = sh_unsetval;
+static char *sh_subscript = sh_unsetval;
+static char *sh_edchar = sh_unsetval;
+static char sh_edmode[2];
+
+/*
+ * Some parameters listed here do not appear in ksh93.mdd autofeatures
+ * because they are only instantiated by ksh93_wrapper() below.  This
+ * obviously includes those commented out here.
+ */
+static struct paramdef partab[] = {
+    PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL,
+	     &sh_edchar, &sh_edchar_gsu),
+    PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL,
+	     &sh_edmode, &sh_edmode_gsu),
+    PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu),
+    PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu),
+    PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu),
+    PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL,
+	     &sh_name, &sh_name_gsu),
+    PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL,
+	     &sh_subscript, &sh_subscript_gsu),
+    PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu),
+    /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */
+    PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu)
+};
+
+static struct features module_features = {
+    bintab, sizeof(bintab)/sizeof(*bintab),
+    NULL, 0,
+    NULL, 0,
+    partab, sizeof(partab)/sizeof(*partab),
+    0
+};
+
+/**/
+static int
+ksh93_wrapper(Eprog prog, FuncWrap w, char *name)
+{
+    Funcstack f;
+    Param pm;
+    zlong num = funcstack->prev ? getiparam(".sh.level") : 0;
+
+    if (!EMULATION(EMULATE_KSH))
+	return 1;
+
+    if (num == 0)
+	for (f = funcstack; f; f = f->prev, num++);
+    else
+	num++;
+
+    queue_signals();
+    ++locallevel;		/* Make these local */
+#define LOCAL_NAMEREF (PM_LOCAL|PM_UNSET|PM_NAMEREF)
+    if ((pm = createparam(".sh.command", LOCAL_NAMEREF))) {
+	pm->level = locallevel;	/* Why is this necessary? */
+	/* Force scoping by assignent hack */
+	setloopvar(".sh.command", "ZSH_DEBUG_CMD");
+	pm->node.flags |= PM_READONLY;
+    }
+    /* .sh.edchar is in partab and below */
+    if (zleactive && (pm = createparam(".sh.edcol", LOCAL_NAMEREF))) {
+	pm->level = locallevel;
+	setloopvar(".sh.edcol", "CURSOR");
+	pm->node.flags |= (PM_NAMEREF|PM_READONLY);
+    }
+    /* .sh.edmode is in partab and below */
+    if (zleactive && (pm = createparam(".sh.edtext", LOCAL_NAMEREF))) {
+	pm->level = locallevel;
+	setloopvar(".sh.edtext", "BUFFER");
+	pm->node.flags |= PM_READONLY;
+    }
+
+    if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) {
+	pm->level = locallevel;
+	setsparam(".sh.fun", ztrdup(name));
+	pm->node.flags |= PM_READONLY;
+    }
+    if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) {
+	pm->level = locallevel;
+	setiparam(".sh.level", num);
+    }
+    if (zleactive) {
+	extern mod_import_variable char *curkeymapname;	/* XXX */
+	extern mod_import_variable char *varedarg;	/* XXX */
+	/* bindkey -v forces VIMODE so this test is as good as any */
+	if (curkeymapname && isset(VIMODE) &&
+	    strcmp(curkeymapname, "main") == 0)
+	    strcpy(sh_edmode, "\033");
+	else
+	    strcpy(sh_edmode, "");
+	if (sh_edchar == sh_unsetval)
+	    sh_edchar = dupstring(getsparam("KEYS"));
+	if (varedarg) {
+	    char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0);
+	    if (ie && *ie) {
+		*ie++ = '\0';
+		/* Assume bin_vared has validated subscript */
+		sh_subscript = dupstring(ie);
+		ie = sh_subscript + strlen(sh_subscript);
+		*--ie = '\0';
+	    } else
+		sh_subscript = sh_unsetval;
+	    if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) {
+		pm->level = locallevel;
+		setloopvar(".sh.value", "BUFFER");	/* Hack */
+		pm->node.flags |= PM_READONLY;
+	    }
+	} else
+	    sh_name = sh_subscript = sh_unsetval;
+    } else {
+	sh_edchar = sh_name = sh_subscript = sh_unsetval;
+	strcpy(sh_edmode, "");
+	/* TODO:
+	 * - disciplines
+	 * - special handling of .sh.value in math
+	 */
+    }
+    --locallevel;
+    unqueue_signals();
+
+    return 1;
+}
+
+static struct funcwrap wrapper[] = {
+    WRAPDEF(ksh93_wrapper),
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+    return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+    *features = featuresarray(m, &module_features);
+    return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+    return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(Module m)
+{
+    return addwrapper(m, wrapper);
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    struct paramdef *p;
+
+    deletewrapper(m, wrapper);
+
+    /* Clean up namerefs, otherwise deleteparamdef() is confused */
+    for (p = partab; p < partab + sizeof(partab)/sizeof(*partab); ++p) {
+	if (p->flags & PM_NAMEREF) {
+	    HashNode hn = gethashnode2(paramtab, p->name);
+	    if (hn)
+		((Param)hn)->node.flags &= ~PM_NAMEREF;
+	}
+    }
+    return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+    return 0;
+}
diff --git a/Src/Modules/ksh93.mdd b/Src/Modules/ksh93.mdd
new file mode 100644
index 000000000..85e35e9cb
--- /dev/null
+++ b/Src/Modules/ksh93.mdd
@@ -0,0 +1,10 @@
+name=zsh/ksh93
+link=either
+load=yes
+
+autofeatures="b:nameref"
+autofeatures_emu="b:nameref p:.sh.command p:.sh.edcol p:.sh.edtext p:.sh.file p:.sh.lineno p:.sh.match p:.sh.subshell p:.sh.version"
+
+moddeps="zsh/zle"
+
+objects="ksh93.o"
diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c
index dd86fb596..84cdfea18 100644
--- a/Src/Modules/mapfile.c
+++ b/Src/Modules/mapfile.c
@@ -170,6 +170,8 @@ get_contents(char *fname)
 #ifdef USE_MMAP
     caddr_t mmptr;
     struct stat sbuf;
+#else
+    off_t size;
 #endif
     char *val;
     unmetafy(fname = ztrdup(fname), &fd);
@@ -196,12 +198,8 @@ get_contents(char *fname)
     close(fd);
 #else /* don't USE_MMAP */
     val = NULL;
-    if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) {
-	LinkList ll;
-
-	if ((ll = readoutput(fd, 1, 0)))
-	    val = peekfirst(ll);
-    }
+    if ((size = zstuff(&val, fname)) > 0)
+	val = metafy(val, size, META_HEAPDUP);
 #endif /* USE_MMAP */
     free(fname);
     return val;
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index 065fa63d2..044617190 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -87,9 +87,52 @@ makeprivate(HashNode hn, UNUSED(int flags))
 	      ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL &&
 	       /* typeset_single() line 2300 discards PM_REMOVABLE -- why? */
 	       !is_private(pm->old))))) {
-	    zwarnnam("private", "can't change scope of existing param: %s",
-		     pm->node.nam);
-	    makeprivate_error = 1;
+	    if (is_private(pm->old)) {
+		if (pm->old->node.flags & PM_READONLY) {
+		    zerr("read-only variable: %s", pm->node.nam);
+		    makeprivate_error = 1;
+		} else if ((pm->node.flags | pm->old->node.flags) ==
+		    pm->old->node.flags) {
+		    /* private called twice on same parameter */
+		    Param tpm = pm;
+		    pm = pm->old;
+		    --locallevel;
+		    /* why have a union if we need this switch anyway? */
+		    switch (PM_TYPE(pm->node.flags)) {
+		    case PM_SCALAR:
+			pm->gsu.s->setfn(pm, tpm->u.str);
+			tpm->u.str = NULL;
+			break;
+		    case PM_INTEGER:
+			pm->gsu.i->setfn(pm, tpm->u.val);
+			break;
+		    case PM_EFLOAT:
+		    case PM_FFLOAT:
+			pm->gsu.f->setfn(pm, tpm->u.dval);
+			break;
+		    case PM_ARRAY:
+			pm->gsu.a->setfn(pm, tpm->u.arr);
+			tpm->u.arr = NULL;
+			break;
+		    case PM_HASHED:
+			pm->gsu.h->setfn(pm, tpm->u.hash);
+			tpm->u.hash = NULL;
+			break;
+		    }
+		    ++locallevel;
+		    if (!(tpm->node.flags & PM_UNSET))
+			pm->node.flags &= ~PM_UNSET;
+		} else {
+		    zerrnam("private",
+			    "can't change type of private param: %s",
+			    pm->node.nam);
+		    makeprivate_error = 1;
+		}
+	    } else {
+		zerrnam("private", "can't change scope of existing param: %s",
+			pm->node.nam);
+		makeprivate_error = 1;
+	    }
 	    return;
 	}
 	struct gsu_closure *gsu = zalloc(sizeof(struct gsu_closure));
@@ -122,6 +165,7 @@ makeprivate(HashNode hn, UNUSED(int flags))
 	    break;
 	default:
 	    makeprivate_error = 1;
+	    zfree(gsu, sizeof(struct gsu_closure));
 	    break;
 	}
 	/* PM_HIDE so new parameters in deeper scopes do not shadow */
@@ -229,7 +273,9 @@ setfn_error(Param pm)
  * calling the original unsetfn.  This assures that if the old unsetfn
  * wants to use its getfn or setfn, they're unconditionally present.
  * The "explicit" flag indicates that "unset" was called, if zero the
- * parameter is going out of scope (see params.c).
+ * parameter is going out of scope (see params.c).  PM_DECLARED is
+ * asserted as if TYPESET_TO_UNSET were in use so that the private
+ * parameter is re-used rather than re-created when assigned again.
  *
  */
 
@@ -252,7 +298,7 @@ pps_setfn(Param pm, char *x)
 {
     struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
     GsuScalar gsu = (GsuScalar)(c->g);
-    if (locallevel == pm->level)
+    if (locallevel == pm->level || locallevel > private_wraplevel)
 	gsu->setfn(pm, x);
     else
 	setfn_error(pm);
@@ -267,9 +313,10 @@ pps_unsetfn(Param pm, int explicit)
     pm->gsu.s = gsu;
     if (locallevel <= pm->level)
 	gsu->unsetfn(pm, explicit);
-    if (explicit)
+    if (explicit) {
+	pm->node.flags |= PM_DECLARED;
 	pm->gsu.s = (GsuScalar)c;
-    else
+    } else
 	zfree(c, sizeof(struct gsu_closure));
 }
 
@@ -291,7 +338,7 @@ ppi_setfn(Param pm, zlong x)
 {
     struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
     GsuInteger gsu = (GsuInteger)(c->g);
-    if (locallevel == pm->level)
+    if (locallevel == pm->level || locallevel > private_wraplevel)
 	gsu->setfn(pm, x);
     else
 	setfn_error(pm);
@@ -306,9 +353,10 @@ ppi_unsetfn(Param pm, int explicit)
     pm->gsu.i = gsu;
     if (locallevel <= pm->level)
 	gsu->unsetfn(pm, explicit);
-    if (explicit)
+    if (explicit) {
+	pm->node.flags |= PM_DECLARED;
 	pm->gsu.i = (GsuInteger)c;
-    else
+    } else
 	zfree(c, sizeof(struct gsu_closure));
 }
 
@@ -330,7 +378,7 @@ ppf_setfn(Param pm, double x)
 {
     struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
     GsuFloat gsu = (GsuFloat)(c->g);
-    if (locallevel == pm->level)
+    if (locallevel == pm->level || locallevel > private_wraplevel)
 	gsu->setfn(pm, x);
     else
 	setfn_error(pm);
@@ -345,9 +393,10 @@ ppf_unsetfn(Param pm, int explicit)
     pm->gsu.f = gsu;
     if (locallevel <= pm->level)
 	gsu->unsetfn(pm, explicit);
-    if (explicit)
+    if (explicit) {
+	pm->node.flags |= PM_DECLARED;
 	pm->gsu.f = (GsuFloat)c;
-    else
+    } else
 	zfree(c, sizeof(struct gsu_closure));
 }
 
@@ -370,7 +419,7 @@ ppa_setfn(Param pm, char **x)
 {
     struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
     GsuArray gsu = (GsuArray)(c->g);
-    if (locallevel == pm->level)
+    if (locallevel == pm->level || locallevel > private_wraplevel)
 	gsu->setfn(pm, x);
     else
 	setfn_error(pm);
@@ -385,9 +434,10 @@ ppa_unsetfn(Param pm, int explicit)
     pm->gsu.a = gsu;
     if (locallevel <= pm->level)
 	gsu->unsetfn(pm, explicit);
-    if (explicit)
+    if (explicit) {
+	pm->node.flags |= PM_DECLARED;
 	pm->gsu.a = (GsuArray)c;
-    else
+    } else
 	zfree(c, sizeof(struct gsu_closure));
 }
 
@@ -411,7 +461,7 @@ pph_setfn(Param pm, HashTable x)
 {
     struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
     GsuHash gsu = (GsuHash)(c->g);
-    if (locallevel == pm->level)
+    if (locallevel == pm->level || locallevel > private_wraplevel)
 	gsu->setfn(pm, x);
     else
 	setfn_error(pm);
@@ -426,9 +476,10 @@ pph_unsetfn(Param pm, int explicit)
     pm->gsu.h = gsu;
     if (locallevel <= pm->level)
 	gsu->unsetfn(pm, explicit);
-    if (explicit)
+    if (explicit) {
+	pm->node.flags |= PM_DECLARED;
 	pm->gsu.h = (GsuHash)c;
-    else
+    } else
 	zfree(c, sizeof(struct gsu_closure));
 }
 
@@ -489,18 +540,19 @@ static struct funcwrap wrapper[] = {
 };
 
 /**/
+static int private_wraplevel = 0;
+
+/**/
 static int
 wrap_private(Eprog prog, FuncWrap w, char *name)
 {
-    static int wraplevel = 0;
-
-    if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
-	int owl = wraplevel;
-	wraplevel = locallevel;
+    if (private_wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
+	int owl = private_wraplevel;
+	private_wraplevel = locallevel;
 	scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET);
 	runshfunc(prog, w, name);
 	scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0);
-	wraplevel = owl;
+	private_wraplevel = owl;
 	return 0;
     }
     return 1;
@@ -512,27 +564,50 @@ static GetNodeFunc getparamnode;
 static HashNode
 getprivatenode(HashTable ht, const char *nam)
 {
-    HashNode hn = getparamnode(ht, nam);
+    /* getparamnode() would follow namerefs, we must not do that here */
+    HashNode hn = gethashnode2(ht, nam);
     Param pm = (Param) hn;
 
-    while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) {
+    /* autoload has precedence over nameref, so getparamnode() */
+    if (pm && (pm->node.flags & PM_AUTOLOAD)) {
+	hn = getparamnode(ht, nam);
+	pm = (Param) hn;
+	/* how would an autoloaded private behave?  return here? */
+    }
+    while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) {
+	if (pm->level == private_wraplevel + 1) {
+	    /* Variable is in the current function scope */
+	    break;
+	}
+#if 0
 	if (!(pm->node.flags & PM_UNSET)) {
 	    /*
 	     * private parameters are always marked PM_UNSET before we
-	     * increment locallevel, so the only way we get here is
-	     * when createparam() wants a new parameter that is not at
-	     * the current locallevel and it has therefore cleared the
-	     * PM_UNSET flag.
+	     * increment locallevel, so there are three possible ways
+	     * to get here:
+	     *  1) createparam() wants a new parameter that is not at
+	     *  the current locallevel and it has therefore cleared the
+	     *  PM_UNSET flag
+	     *  2) locallevel has been incremented (startparamscope())
+	     *  outside the usual function call stack (private_wraplevel)
+	     *  3) dynamic scoping is fetching a value from a surrounding
+	     *  scope, we don't know if that's for assign or just expand
+	     * The first of those is now caught in createparam() when
+	     * testing PM_RO_BY_DESIGN and the second occurs only in
+	     * nofork substitution or handling of ZLE specials.  If the
+	     * third is an assignment, the GSU setfn rejects it.
 	     */
 	    DPUTS(pm->old, "BUG: PM_UNSET cleared in wrong scope");
-	    setfn_error(pm);
-	    /*
-	     * TODO: instead of throwing an error here, create a global
-	     * parameter, insert as pm->old, handle WARN_CREATE_GLOBAL.
-	     */
 	}
+#endif
 	pm = pm->old;
     }
+
+    /* resolve nameref after skipping private parameters */
+    if (pm && (pm->node.flags & PM_NAMEREF) &&
+	(pm->u.str || (pm->node.flags & PM_UNSET)))
+	pm = (Param) resolve_nameref(pm, NULL);
+
     return (HashNode)pm;
 }
 
@@ -571,7 +646,7 @@ printprivatenode(HashNode hn, int printflags)
 
 static struct builtin bintab[] = {
     /* Copied from BUILTIN("local"), "P" added */
-    BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P")
+    BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmrtux", "P")
 };
 
 static struct features module_features = {
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index dbb61e474..7441c30b8 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -49,13 +49,15 @@ paramtypestr(Param pm)
 	if (pm->node.flags & PM_AUTOLOAD)
 	    return dupstring("undefined");
 
-	switch (PM_TYPE(f)) {
+	/* For simplicity we treat PM_NAMEREF as PM_TYPE(PM_SCALAR) */
+	switch (PM_TYPE(f)|(f & PM_NAMEREF)) {
 	case PM_SCALAR:  val = "scalar"; break;
 	case PM_ARRAY:   val = "array"; break;
 	case PM_INTEGER: val = "integer"; break;
 	case PM_EFLOAT:
 	case PM_FFLOAT:  val = "float"; break;
 	case PM_HASHED:  val = "association"; break;
+	case PM_NAMEREF: val = "nameref"; break;
 	}
 	DPUTS(!val, "BUG: type not handled in parameter");
 	val = dupstring(val);
@@ -103,10 +105,15 @@ getpmparameter(UNUSED(HashTable ht), const char *name)
     pm->node.nam = dupstring(name);
     pm->node.flags = PM_SCALAR | PM_READONLY;
     pm->gsu.s = &nullsetscalar_gsu;
-    if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
-	!(rpm->node.flags & PM_UNSET))
+    if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) &&
+	!(rpm->node.flags & PM_UNSET)) {
 	pm->u.str = paramtypestr(rpm);
-    else {
+	if ((rpm->node.flags & PM_NAMEREF) && rpm->u.str && *(rpm->u.str) &&
+	    (rpm = (Param) realparamtab->getnode(realparamtab, name)) &&
+	    !(rpm->node.flags & PM_UNSET)) {
+	    pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm));
+	}
+    } else {
 	pm->u.str = dupstring("");
 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
     }
@@ -302,7 +309,7 @@ setfunction(char *name, char *val, int dis)
     shfunc_set_sticky(shf);
 
     if (!strncmp(name, "TRAP", 4) &&
-	(sn = getsignum(name + 4)) != -1) {
+	(sn = getsigidx(name + 4)) != -1) {
 	if (settrap(sn, NULL, ZSIG_FUNC)) {
 	    freeeprog(shf->funcdef);
 	    zfree(shf, sizeof(*shf));
@@ -1226,9 +1233,16 @@ histwgetfn(UNUSED(Param pm))
             pushnode(l, getdata(n));
 
     while (he) {
+	char *hstr = he->node.nam;
+	int len = strlen(hstr);
 	for (iw = he->nwords - 1; iw >= 0; iw--) {
-	    h = he->node.nam + he->words[iw * 2];
-	    e = he->node.nam + he->words[iw * 2 + 1];
+	    int wbegin = he->words[iw * 2];
+	    int wend = he->words[iw * 2 + 1];
+
+	    if (wbegin < 0 || wbegin >= len || wend < 0 || wend > len)
+		break;
+	    h = hstr + wbegin;
+	    e = hstr + wend;
 	    sav = *e;
 	    *e = '\0';
 	    addlinknode(l, dupstring(h));
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index 6289e003e..67157cc01 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -34,11 +34,11 @@
 #define CPCRE_PLAIN 0
 
 /**/
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
-#include <pcre.h>
+#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H)
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
 
-static pcre *pcre_pattern;
-static pcre_extra *pcre_hints;
+static pcre2_code *pcre_pattern;
 
 /**/
 static int
@@ -47,8 +47,6 @@ zpcre_utf8_enabled(void)
 #if defined(MULTIBYTE_SUPPORT) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
     static int have_utf8_pcre = -1;
 
-    /* value can toggle based on MULTIBYTE, so don't
-     * be too eager with caching */
     if (have_utf8_pcre < -1)
 	return 0;
 
@@ -56,15 +54,11 @@ zpcre_utf8_enabled(void)
 	return 0;
 
     if ((have_utf8_pcre == -1) &&
-        (!strcmp(nl_langinfo(CODESET), "UTF-8"))) {
-
-	if (pcre_config(PCRE_CONFIG_UTF8, &have_utf8_pcre))
-	    have_utf8_pcre = -2; /* erk, failed to ask */
+       (pcre2_config(PCRE2_CONFIG_UNICODE, &have_utf8_pcre))) {
+           have_utf8_pcre = -2; /* erk, failed to ask */
     }
 
-    if (have_utf8_pcre < 0)
-	return 0;
-    return have_utf8_pcre;
+    return (have_utf8_pcre == 1) && (!strcmp(nl_langinfo(CODESET), "UTF-8"));
 
 #else
     return 0;
@@ -75,47 +69,38 @@ zpcre_utf8_enabled(void)
 static int
 bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
 {
-    int pcre_opts = 0, pcre_errptr, target_len;
-    const char *pcre_error;
+    uint32_t pcre_opts = 0;
+    int target_len;
+    int pcre_error;
+    PCRE2_SIZE pcre_offset;
     char *target;
     
-    if(OPT_ISSET(ops,'a')) pcre_opts |= PCRE_ANCHORED;
-    if(OPT_ISSET(ops,'i')) pcre_opts |= PCRE_CASELESS;
-    if(OPT_ISSET(ops,'m')) pcre_opts |= PCRE_MULTILINE;
-    if(OPT_ISSET(ops,'x')) pcre_opts |= PCRE_EXTENDED;
-    if(OPT_ISSET(ops,'s')) pcre_opts |= PCRE_DOTALL;
+    if (OPT_ISSET(ops, 'a')) pcre_opts |= PCRE2_ANCHORED;
+    if (OPT_ISSET(ops, 'i')) pcre_opts |= PCRE2_CASELESS;
+    if (OPT_ISSET(ops, 'm')) pcre_opts |= PCRE2_MULTILINE;
+    if (OPT_ISSET(ops, 'x')) pcre_opts |= PCRE2_EXTENDED;
+    if (OPT_ISSET(ops, 's')) pcre_opts |= PCRE2_DOTALL;
     
     if (zpcre_utf8_enabled())
-	pcre_opts |= PCRE_UTF8;
-
-#ifdef HAVE_PCRE_STUDY
-    if (pcre_hints)
-#ifdef PCRE_CONFIG_JIT
-	pcre_free_study(pcre_hints);
-#else
-	pcre_free(pcre_hints);
-#endif
-    pcre_hints = NULL;
-#endif
+	pcre_opts |= PCRE2_UTF;
 
     if (pcre_pattern)
-	pcre_free(pcre_pattern);
+	pcre2_code_free(pcre_pattern);
     pcre_pattern = NULL;
 
     target = ztrdup(*args);
     unmetafy(target, &target_len);
 
-    if ((int)strlen(target) != target_len) {
-	zwarnnam(nam, "embedded NULs in PCRE pattern terminate pattern");
-    }
-
-    pcre_pattern = pcre_compile(target, pcre_opts, &pcre_error, &pcre_errptr, NULL);
+    pcre_pattern = pcre2_compile((PCRE2_SPTR) target, (PCRE2_SIZE) target_len,
+	    pcre_opts, &pcre_error, &pcre_offset, NULL);
 
     free(target);
 
     if (pcre_pattern == NULL)
     {
-	zwarnnam(nam, "error in regex: %s", pcre_error);
+	PCRE2_UCHAR buffer[256];
+	pcre2_get_error_message(pcre_error, buffer, sizeof(buffer));
+	zwarnnam(nam, "error in regex: %s", buffer);
 	return 1;
     }
     
@@ -123,67 +108,76 @@ bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func))
 }
 
 /**/
-#ifdef HAVE_PCRE_STUDY
-
-/**/
 static int
 bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int func))
 {
-    const char *pcre_error;
-
     if (pcre_pattern == NULL)
     {
 	zwarnnam(nam, "no pattern has been compiled for study");
 	return 1;
     }
-    
-    if (pcre_hints)
-#ifdef PCRE_CONFIG_JIT
-	pcre_free_study(pcre_hints);
-#else
-	pcre_free(pcre_hints);
-#endif
-    pcre_hints = NULL;
 
-    pcre_hints = pcre_study(pcre_pattern, 0, &pcre_error);
-    if (pcre_error != NULL)
-    {
-	zwarnnam(nam, "error while studying regex: %s", pcre_error);
-	return 1;
+    int jit = 0;
+    if (!pcre2_config(PCRE2_CONFIG_JIT, &jit) && jit) {
+	if (pcre2_jit_compile(pcre_pattern, PCRE2_JIT_COMPLETE) < 0) {
+	    zwarnnam(nam, "error while studying regex");
+	    return 1;
+	}
     }
     
     return 0;
 }
 
-/**/
-#else /* !HAVE_PCRE_STUDY */
+static int
+pcre_callout(pcre2_callout_block_8 *block, UNUSED(void *callout_data))
+{
+    Eprog prog;
+    int ret=0;
 
-# define bin_pcre_study bin_notavail
+    if (!block->callout_number &&
+	    ((prog = parse_string((char *) block->callout_string, 0))))
+    {
+	int ef = errflag, lv = lastval;
 
-/**/
-#endif /* !HAVE_PCRE_STUDY */
+	setsparam(".pcre.subject",
+		metafy((char *) block->subject, block->subject_length, META_DUP));
+	setiparam(".pcre.pos", block->current_position + 1);
+	execode(prog, 1, 0, "pcre");
+	ret = lastval | errflag;
+
+	/* Restore any user interrupt error status */
+	errflag = ef | (errflag & ERRFLAG_INT);
+	lastval = lv;
+    }
+
+    return ret;
+}
 
-/**/
 static int
-zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
-		     char *substravar, int want_offset_pair, int matchedinarr,
-		     int want_begin_end)
+zpcre_get_substrings(pcre2_code *pat, char *arg, pcre2_match_data *mdata,
+	int captured_count, char *matchvar, char *substravar, char *namedassoc,
+	int want_offset_pair, int matchedinarr, int want_begin_end)
 {
-    char **captures, *match_all, **matches;
+    PCRE2_SIZE *ovec;
+    char *match_all, **matches;
     char offset_all[50];
     int capture_start = 1;
+    int vec_off;
+    PCRE2_SPTR ntable; /* table of named captures */
+    uint32_t ncount, nsize;
 
     if (matchedinarr) {
-	/* bash-style captures[0] entire-matched string in the array */
+	/* bash-style ovec[0] entire-matched string in the array */
 	capture_start = 0;
     }
 
-    /* captures[0] will be entire matched string, [1] first substring */
-    if (!pcre_get_substring_list(arg, ovec, captured_count, (const char ***)&captures)) {
-	int nelem = arrlen(captures)-1;
+    /* ovec[0] will be entire matched string, [1] first substring */
+    ovec = pcre2_get_ovector_pointer(mdata);
+    if (ovec) {
+	int nelem = captured_count - 1;
 	/* Set to the offsets of the complete match */
 	if (want_offset_pair) {
-	    sprintf(offset_all, "%d %d", ovec[0], ovec[1]);
+	    sprintf(offset_all, "%ld %ld", ovec[0], ovec[1]);
 	    setsparam("ZPCRE_OP", ztrdup(offset_all));
 	}
 	/*
@@ -192,7 +186,7 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
 	 * ovec is length 2*(1+capture_list_length)
 	 */
 	if (matchvar) {
-	    match_all = metafy(captures[0], ovec[1] - ovec[0], META_DUP);
+	    match_all = metafy(arg + ovec[0], ovec[1] - ovec[0], META_DUP);
 	    setsparam(matchvar, match_all);
 	}
 	/*
@@ -207,21 +201,35 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
 	 */
 	if (substravar &&
 	    (!want_begin_end || nelem)) {
-	    char **x, **y;
-	    int vec_off, i;
-	    y = &captures[capture_start];
+	    char **x;
+	    int i;
 	    matches = x = (char **) zalloc(sizeof(char *) * (captured_count+1-capture_start));
-	    for (i = capture_start; i < captured_count; i++, y++) {
+	    for (i = capture_start; i < captured_count; i++) {
 		vec_off = 2*i;
-		if (*y)
-		    *x++ = metafy(*y, ovec[vec_off+1]-ovec[vec_off], META_DUP);
-		else
-		    *x++ = NULL;
+		*x++ = metafy(arg + ovec[vec_off], ovec[vec_off+1]-ovec[vec_off], META_DUP);
 	    }
 	    *x = NULL;
 	    setaparam(substravar, matches);
 	}
 
+	if (namedassoc
+		&& !pcre2_pattern_info(pat, PCRE2_INFO_NAMECOUNT, &ncount) && ncount
+		&& !pcre2_pattern_info(pat, PCRE2_INFO_NAMEENTRYSIZE, &nsize)
+		&& !pcre2_pattern_info(pat, PCRE2_INFO_NAMETABLE, &ntable))
+	{
+	    char **hash, **hashptr;
+	    uint32_t nidx;
+	    hashptr = hash = (char **)zshcalloc((ncount+1)*2*sizeof(char *));
+	    for (nidx = 0; nidx < ncount; nidx++) {
+		vec_off = (ntable[nsize * nidx] << 9) + 2 * ntable[nsize * nidx + 1];
+		/* would metafy the key but pcre limits characters in the name */
+		*hashptr++ = ztrdup((char *) ntable + nsize * nidx + 2);
+		*hashptr++ = metafy(arg + ovec[vec_off],
+			ovec[vec_off+1]-ovec[vec_off], META_DUP);
+	    }
+	    sethparam(namedassoc, hash);
+	}
+
 	if (want_begin_end) {
 	    /*
 	     * cond-infix rather than builtin; also not bash; so we set a bunch
@@ -253,7 +261,8 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
 	    setiparam("MEND", offs + !isset(KSHARRAYS) - 1);
 	    if (nelem) {
 		char **mbegin, **mend, **bptr, **eptr;
-		int i, *ipair;
+		int i;
+		size_t *ipair;
 
 		bptr = mbegin = zalloc(sizeof(char*)*(nelem+1));
 		eptr = mend = zalloc(sizeof(char*)*(nelem+1));
@@ -293,8 +302,6 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar,
 		setaparam("mend", mend);
 	    }
 	}
-
-	pcre_free_substring_list((const char **)captures);
     }
 
     return 0;
@@ -320,29 +327,33 @@ getposint(char *instr, char *nam)
 static int
 bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
 {
-    int ret, capcount, *ovec, ovecsize, c;
+    int ret, c;
+    pcre2_match_data *pcre_mdata = NULL;
     char *matched_portion = NULL;
     char *plaintext = NULL;
-    char *receptacle = NULL;
+    char *receptacle;
+    char *named = NULL;
     int return_value = 1;
     /* The subject length and offset start are both int values in pcre_exec */
     int subject_len;
     int offset_start = 0;
     int want_offset_pair = 0;
+    int use_dfa = 0;
 
     if (pcre_pattern == NULL) {
 	zwarnnam(nam, "no pattern has been compiled");
 	return 1;
     }
 
-    matched_portion = "MATCH";
-    receptacle = "match";
-    if(OPT_HASARG(ops,c='a')) {
-	receptacle = OPT_ARG(ops,c);
-    }
-    if(OPT_HASARG(ops,c='v')) {
-	matched_portion = OPT_ARG(ops,c);
+    if (!(use_dfa = OPT_ISSET(ops, 'd'))) {
+	matched_portion = OPT_HASARG(ops, c='v') ? OPT_ARG(ops, c) : "MATCH";
+	named = OPT_HASARG(ops, c='A') ? OPT_ARG(ops, c) : ".pcre.match";
+    } else if (OPT_HASARG(ops, c='v') || OPT_HASARG(ops, c='A')) {
+	zwarnnam(nam, "-d cannot be combined with -%c", c);
+	return 1;
     }
+    receptacle = OPT_HASARG(ops, 'a') ? OPT_ARG(ops, 'a') : "match";
+
     if(OPT_HASARG(ops,c='n')) { /* The offset position to start the search, in bytes. */
 	if ((offset_start = getposint(OPT_ARG(ops,c), nam)) < 0)
 	    return 1;
@@ -350,36 +361,57 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
     /* For the entire match, 'Return' the offset byte positions instead of the matched string */
     if(OPT_ISSET(ops,'b')) want_offset_pair = 1;
 
-    if ((ret = pcre_fullinfo(pcre_pattern, pcre_hints, PCRE_INFO_CAPTURECOUNT, &capcount)))
-    {
-	zwarnnam(nam, "error %d in fullinfo", ret);
-	return 1;
-    }
-
-    ovecsize = (capcount+1)*3;
-    ovec = zalloc(ovecsize*sizeof(int));
-
     plaintext = ztrdup(*args);
     unmetafy(plaintext, &subject_len);
 
+    pcre2_match_context_8 *mcontext = pcre2_match_context_create(NULL);
+    pcre2_set_callout(mcontext, &pcre_callout, 0);
+
     if (offset_start > 0 && offset_start >= subject_len)
-	ret = PCRE_ERROR_NOMATCH;
-    else
-	ret = pcre_exec(pcre_pattern, pcre_hints, plaintext, subject_len, offset_start, 0, ovec, ovecsize);
+	ret = PCRE2_ERROR_NOMATCH;
+    else if (use_dfa) {
+	PCRE2_SIZE old, wscount = 128, capcount = 128;
+	void *workspace = zhalloc(sizeof(int) * wscount);
+	pcre_mdata = pcre2_match_data_create(capcount, NULL);
+	do {
+	    ret = pcre2_dfa_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len,
+		offset_start, 0, pcre_mdata, mcontext, (int *) workspace, wscount);
+	    if (ret == PCRE2_ERROR_DFA_WSSIZE) {
+		old = wscount;
+		wscount += wscount / 2;
+		workspace = hrealloc(workspace, sizeof(int) * old, sizeof(int) * wscount);
+	    } else if (ret == 0) {
+		capcount += capcount / 2;
+		pcre2_match_data_free(pcre_mdata);
+		pcre_mdata = pcre2_match_data_create(capcount, NULL);
+	    } else
+		break;
+	} while(1);
+    } else {
+	pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL);
+	ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len,
+		offset_start, 0, pcre_mdata, mcontext);
+	if (ret > 0)
+	    ret = pcre2_get_ovector_count(pcre_mdata);
+    }
 
     if (ret==0) return_value = 0;
-    else if (ret==PCRE_ERROR_NOMATCH) /* no match */;
+    else if (ret == PCRE2_ERROR_NOMATCH) /* no match */;
     else if (ret>0) {
-	zpcre_get_substrings(plaintext, ovec, ret, matched_portion, receptacle,
-			     want_offset_pair, 0, 0);
+	zpcre_get_substrings(pcre_pattern, plaintext, pcre_mdata, ret,
+		matched_portion, receptacle, named, want_offset_pair, use_dfa, 0);
 	return_value = 0;
     }
     else {
-	zwarnnam(nam, "error in pcre_exec [%d]", ret);
+	PCRE2_UCHAR buffer[256];
+	pcre2_get_error_message(ret, buffer, sizeof(buffer));
+	zwarnnam(nam, "error in pcre matching for %s: %s", *args, buffer);
     }
     
-    if (ovec)
-	zfree(ovec, ovecsize*sizeof(int));
+    if (pcre_mdata)
+	pcre2_match_data_free(pcre_mdata);
+    if (mcontext)
+	pcre2_match_context_free(mcontext);
     zsfree(plaintext);
 
     return return_value;
@@ -389,17 +421,19 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func))
 static int
 cond_pcre_match(char **a, int id)
 {
-    pcre *pcre_pat;
-    const char *pcre_err;
+    pcre2_code *pcre_pat = NULL;
+    int pcre_err;
+    PCRE2_SIZE pcre_erroff;
     char *lhstr, *rhre, *lhstr_plain, *rhre_plain, *avar, *svar;
-    int r = 0, pcre_opts = 0, pcre_errptr, capcnt, *ov, ovsize;
+    int r = 0, pcre_opts = 0;
+    pcre2_match_data *pcre_mdata = NULL;
     int lhstr_plain_len, rhre_plain_len;
     int return_value = 0;
 
     if (zpcre_utf8_enabled())
-	pcre_opts |= PCRE_UTF8;
+	pcre_opts |= PCRE2_UTF;
     if (isset(REMATCHPCRE) && !isset(CASEMATCH))
-	pcre_opts |= PCRE_CASELESS;
+	pcre_opts |= PCRE2_CASELESS;
 
     lhstr = cond_str(a,0,0);
     rhre = cond_str(a,1,0);
@@ -407,9 +441,6 @@ cond_pcre_match(char **a, int id)
     rhre_plain = ztrdup(rhre);
     unmetafy(lhstr_plain, &lhstr_plain_len);
     unmetafy(rhre_plain, &rhre_plain_len);
-    pcre_pat = NULL;
-    ov = NULL;
-    ovsize = 0;
 
     if (isset(BASHREMATCH)) {
 	svar = NULL;
@@ -421,27 +452,27 @@ cond_pcre_match(char **a, int id)
 
     switch(id) {
 	 case CPCRE_PLAIN:
-		if ((int)strlen(rhre_plain) != rhre_plain_len) {
-		    zwarn("embedded NULs in PCRE pattern terminate pattern");
-		}
-		pcre_pat = pcre_compile(rhre_plain, pcre_opts, &pcre_err, &pcre_errptr, NULL);
-		if (pcre_pat == NULL) {
-		    zwarn("failed to compile regexp /%s/: %s", rhre, pcre_err);
+		if (!(pcre_pat = pcre2_compile((PCRE2_SPTR) rhre_plain,
+			(PCRE2_SIZE) rhre_plain_len, pcre_opts,
+			&pcre_err, &pcre_erroff, NULL)))
+		{
+		    PCRE2_UCHAR buffer[256];
+		    pcre2_get_error_message(pcre_err, buffer, sizeof(buffer));
+		    zwarn("failed to compile regexp /%s/: %s", rhre, buffer);
 		    break;
 		}
-                pcre_fullinfo(pcre_pat, NULL, PCRE_INFO_CAPTURECOUNT, &capcnt);
-    		ovsize = (capcnt+1)*3;
-		ov = zalloc(ovsize*sizeof(int));
-    		r = pcre_exec(pcre_pat, NULL, lhstr_plain, lhstr_plain_len, 0, 0, ov, ovsize);
-		/* r < 0 => error; r==0 match but not enough size in ov
+		pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pat, NULL);
+		r = pcre2_match(pcre_pat, (PCRE2_SPTR8) lhstr_plain, lhstr_plain_len,
+			0, 0, pcre_mdata, NULL);
+		/* r < 0 => error; r==0 match but not enough size in match data
 		 * r > 0 => (r-1) substrings found; r==1 => no substrings
 		 */
     		if (r==0) {
-		    zwarn("reportable zsh problem: pcre_exec() returned 0");
+		    zwarn("reportable zsh problem: pcre2_match() returned 0");
 		    return_value = 1;
 		    break;
 		}
-	        else if (r==PCRE_ERROR_NOMATCH) {
+		else if (r == PCRE2_ERROR_NOMATCH) {
 		    return_value = 0; /* no match */
 		    break;
 		}
@@ -450,9 +481,9 @@ cond_pcre_match(char **a, int id)
 		    break;
 		}
                 else if (r>0) {
-		    zpcre_get_substrings(lhstr_plain, ov, r, svar, avar, 0,
-					 isset(BASHREMATCH),
-					 !isset(BASHREMATCH));
+		    uint32_t ovec_count = pcre2_get_ovector_count(pcre_mdata);
+		    zpcre_get_substrings(pcre_pat, lhstr_plain, pcre_mdata, ovec_count, svar, avar,
+			    ".pcre.match", 0, isset(BASHREMATCH), !isset(BASHREMATCH));
 		    return_value = 1;
 		    break;
 		}
@@ -463,10 +494,10 @@ cond_pcre_match(char **a, int id)
 	free(lhstr_plain);
     if(rhre_plain)
 	free(rhre_plain);
+    if (pcre_mdata)
+	pcre2_match_data_free(pcre_mdata);
     if (pcre_pat)
-	pcre_free(pcre_pat);
-    if (ov)
-	zfree(ov, ovsize*sizeof(int));
+	pcre2_code_free(pcre_pat);
 
     return return_value;
 }
@@ -488,18 +519,18 @@ static struct conddef cotab[] = {
 
 static struct builtin bintab[] = {
     BUILTIN("pcre_compile", 0, bin_pcre_compile, 1, 1, 0, "aimxs",  NULL),
-    BUILTIN("pcre_match",   0, bin_pcre_match,   1, 1, 0, "a:v:n:b",    NULL),
+    BUILTIN("pcre_match",   0, bin_pcre_match,   1, 1, 0, "A:a:v:n:bd",    NULL),
     BUILTIN("pcre_study",   0, bin_pcre_study,   0, 0, 0, NULL,    NULL)
 };
 
 
 static struct features module_features = {
     bintab, sizeof(bintab)/sizeof(*bintab),
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
+#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H)
     cotab, sizeof(cotab)/sizeof(*cotab),
-#else /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+#else /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */
     NULL, 0,
-#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */
+#endif /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */
     NULL, 0,
     NULL, 0,
     0
@@ -546,19 +577,9 @@ cleanup_(Module m)
 int
 finish_(UNUSED(Module m))
 {
-#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC)
-#ifdef HAVE_PCRE_STUDY
-    if (pcre_hints)
-#ifdef PCRE_CONFIG_JIT
-	pcre_free_study(pcre_hints);
-#else
-	pcre_free(pcre_hints);
-#endif
-    pcre_hints = NULL;
-#endif
-
+#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H)
     if (pcre_pattern)
-	pcre_free(pcre_pattern);
+	pcre2_code_free(pcre_pattern);
     pcre_pattern = NULL;
 #endif
 
diff --git a/Src/Modules/pcre.mdd b/Src/Modules/pcre.mdd
index 6eb3c691b..3e1579117 100644
--- a/Src/Modules/pcre.mdd
+++ b/Src/Modules/pcre.mdd
@@ -1,5 +1,5 @@
 name=zsh/pcre
-link=`if test x$enable_pcre = xyes && (pcre-config --version >/dev/null 2>/dev/null); then echo dynamic; else echo no; fi`
+link=`if test x$enable_pcre = xyes; then echo dynamic; else echo no; fi`
 load=no
 
 autofeatures="b:pcre_compile b:pcre_study b:pcre_match"
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 0df9b35b7..c9f851974 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -406,7 +406,7 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func))
 	} else {
 	    for (; *arg; arg++) {
 		if (strchr("glLnNorstT", *arg))
-		    ops->ind[STOUC(*arg)] = 1;
+		    ops->ind[(unsigned char) *arg] = 1;
 		else if (*arg == 'A') {
 		    if (arg[1]) {
 			arrnam = arg+1;
diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c
index 4596b41d2..f9ab64fb3 100644
--- a/Src/Modules/terminfo.c
+++ b/Src/Modules/terminfo.c
@@ -160,7 +160,7 @@ getterminfo(UNUSED(HashTable ht), const char *name)
 	pm->node.flags |= PM_SCALAR;
 	pm->gsu.s = &nullsetscalar_gsu;
     } else if ((tistr = (char *)tigetstr(nameu)) != NULL && tistr != (char *)-1) {
-	pm->u.str = dupstring(tistr);
+	pm->u.str = metafy(tistr, -1, META_HEAPDUP);
 	pm->node.flags |= PM_SCALAR;
 	pm->gsu.s = &nullsetscalar_gsu;
     } else {
@@ -280,7 +280,7 @@ scanterminfo(UNUSED(HashTable ht), ScanFunc func, int flags)
     for (capname = (char **)strnames; *capname; capname++) {
 	if ((tistr = (char *)tigetstr(*capname)) != NULL &&
 	    tistr != (char *)-1) {
-	    pm->u.str = dupstring(tistr);
+	    pm->u.str = metafy(tistr, -1, META_HEAPDUP);
 	    pm->node.nam = dupstring(*capname);
 	    func(&pm->node, flags);
 	}
diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c
index d45c3cf3d..acc499518 100644
--- a/Src/Modules/watch.c
+++ b/Src/Modules/watch.c
@@ -255,8 +255,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
     while (*fmt)
 	if (*fmt == '\\') {
 	    if (*++fmt) {
-		if (prnt)
+		if (prnt) {
+		    applytextattributes(TSC_RAW);
 		    putchar(*fmt);
+		}
 		++fmt;
 	    } else if (fini)
 		return fmt;
@@ -266,8 +268,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 	else if (*fmt == fini)
 	    return ++fmt;
 	else if (*fmt != '%') {
-	    if (prnt)
+	    if (prnt) {
+		applytextattributes(TSC_RAW);
 		putchar(*fmt);
+	    }
 	    ++fmt;
 	} else {
 	    if (*++fmt == BEGIN3)
@@ -277,12 +281,15 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 	    else
 		switch (*(fm2 = fmt++)) {
 		case 'n':
+		    applytextattributes(TSC_RAW);
 		    printf("%.*s", (int)sizeof(u->ut_name), u->ut_name);
 		    break;
 		case 'a':
+		    applytextattributes(TSC_RAW);
 		    printf("%s", (!inout) ? "logged off" : "logged on");
 		    break;
 		case 'l':
+		    applytextattributes(TSC_RAW);
 		    if (!strncmp(u->ut_line, "tty", 3))
 			printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3);
 		    else
@@ -290,6 +297,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 		    break;
 # ifdef WATCH_UTMP_UT_HOST
 		case 'm':
+		    applytextattributes(TSC_RAW);
 		    for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) {
 			if (*p == '.' && !idigit(p[1]))
 			    break;
@@ -297,6 +305,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 		    }
 		    break;
 		case 'M':
+		    applytextattributes(TSC_RAW);
 		    printf("%.*s", (int)sizeof(u->ut_host), u->ut_host);
 		    break;
 # endif /* WATCH_UTMP_UT_HOST */
@@ -343,9 +352,11 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 		    len = ztrftime(buf, 40, fm2, tm, 0L);
 		    if (len > 0)
 			metafy(buf, len, META_NOALLOC);
+		    applytextattributes(TSC_RAW);
 		    printf("%s", (*buf == ' ') ? buf + 1 : buf);
 		    break;
 		case '%':
+		    applytextattributes(TSC_RAW);
 		    putchar('%');
 		    break;
 		case 'F':
@@ -354,16 +365,20 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 			atr = match_colour((const char**)&fmt, 1, 0);
 			if (*fmt == '}')
 			    fmt++;
-			if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) {
-			    txtunset(TXT_ATTR_FG_COL_MASK);
-			    txtset(atr & TXT_ATTR_FG_ON_MASK);
-			    set_colour_attribute(atr, COL_SEQ_FG, TSC_RAW);
+			if (atr && atr != TXT_ERROR) {
+			    tsetattrs(atr);
+			    break;
 			}
-		    }
-		    break;
+		    } /* fall-through */
 		case 'f':
-		    txtunset(TXT_ATTR_FG_ON_MASK);
-		    set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW);
+		    tunsetattrs(TXTFGCOLOUR);
+		    break;
+		case 'H':
+		    if (*fmt == '{') {
+			fmt = parsehighlight(fmt + 1, '}', &atr);
+			if (atr && atr != TXT_ERROR)
+			    treplaceattrs(atr);
+		    }
 		    break;
 		case 'K':
 		    if (*fmt == '{') {
@@ -371,49 +386,43 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 			atr = match_colour((const char**)&fmt, 0, 0);
 			if (*fmt == '}')
 			    fmt++;
-			if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) {
-			    txtunset(TXT_ATTR_BG_COL_MASK);
-			    txtset(atr & TXT_ATTR_BG_ON_MASK);
-			    set_colour_attribute(atr, COL_SEQ_BG, TSC_RAW);
+			if (atr && atr != TXT_ERROR) {
+			    tsetattrs(atr);
+			    break;
 			}
-		    }
-		    break;
+		    } /* fall-through */
 		case 'k':
-		    txtunset(TXT_ATTR_BG_ON_MASK);
-		    set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW);
+		    tunsetattrs(TXTBGCOLOUR);
 		    break;
 		case 'S':
-		    txtset(TXTSTANDOUT);
-		    tsetcap(TCSTANDOUTBEG, TSC_RAW);
+		    tsetattrs(TXTSTANDOUT);
 		    break;
 		case 's':
-		    txtunset(TXTSTANDOUT);
-		    tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY);
+		    tunsetattrs(TXTSTANDOUT);
 		    break;
 		case 'B':
-		    txtset(TXTBOLDFACE);
-		    tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY);
+		    tsetattrs(TXTBOLDFACE);
 		    break;
 		case 'b':
-		    txtunset(TXTBOLDFACE);
-		    tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY);
+		    tunsetattrs(TXTBOLDFACE);
 		    break;
 		case 'U':
-		    txtset(TXTUNDERLINE);
-		    tsetcap(TCUNDERLINEBEG, TSC_RAW);
+		    tsetattrs(TXTUNDERLINE);
 		    break;
 		case 'u':
-		    txtunset(TXTUNDERLINE);
-		    tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY);
+		    tunsetattrs(TXTUNDERLINE);
 		    break;
 		default:
+		    applytextattributes(TSC_RAW);
 		    putchar('%');
 		    putchar(*fm2);
 		    break;
 		}
 	}
-    if (prnt)
+    if (prnt) {
+	applytextattributes(TSC_RAW);
 	putchar('\n');
+    }
 
     return fmt;
 }
@@ -421,20 +430,23 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
 /* See if the watch entry matches */
 
 static int
-watchlog_match(char *teststr, char *actual, int len)
+watchlog_match(char *teststr, char *actual, size_t buflen)
 {
     int ret = 0;
     Patprog pprog;
     char *str = dupstring(teststr);
+    size_t len = strnlen(actual, buflen);
+    char *user = metafy(actual, len,
+	    len == buflen ? META_HEAPDUP : META_USEHEAP);
 
     tokenize(str);
 
     if ((pprog = patcompile(str, PAT_STATIC, 0))) {
 	queue_signals();
-	if (pattry(pprog, actual))
+	if (pattry(pprog, user))
 	    ret = 1;
 	unqueue_signals();
-    } else if (!strncmp(actual, teststr, len))
+    } else if (!strcmp(user, teststr))
 	ret = 1;
     return ret;
 }
@@ -454,10 +466,17 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
 	(void)watchlog2(inout, u, fmt, 1, 0);
 	return;
     }
-    if (*w && !strcmp(*w, "notme") &&
-	strncmp(u->ut_name, get_username(), sizeof(u->ut_name))) {
-	(void)watchlog2(inout, u, fmt, 1, 0);
-	return;
+    if (*w && !strcmp(*w, "notme")) {
+	int len = strnlen(u->ut_name, sizeof(u->ut_name));
+	char *username = metafy(u->ut_name, len,
+				(len == sizeof(u->ut_name) ?
+				 META_HEAPDUP /* allow for nul terminator */ :
+				 META_USEHEAP));
+	if (strcmp(username, get_username())) {
+	    (void)watchlog2(inout, u, fmt, 1, 0);
+	    return;
+	}
+	w++;
     }
     for (; *w; w++) {
 	bad = 0;
@@ -486,7 +505,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
 		for (vv = ++v; *vv && *vv != '%'; vv++);
 		sav = *vv;
 		*vv = '\0';
-		if (!watchlog_match(v, u->ut_host, strlen(v)))
+		if (!watchlog_match(v, u->ut_host, sizeof(u->ut_host)))
 		    bad = 1;
 		*vv = sav;
 		v = vv;
@@ -565,7 +584,7 @@ readwtab(WATCH_STRUCT_UTMP **head, int initial_sz)
 
     if (sz)
 	qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP),
-	           (int (*) _((const void *, const void *)))ucmp);
+	           (int (*) (const void *, const void *))ucmp);
     return sz;
 }
 
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index e8e239e76..0c26828fd 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -127,7 +127,7 @@ typedef int (*readwrite_t)(int, char *, off_t, int);
 
 struct zftpcmd {
     const char *nam;
-    int (*fun) _((char *, char **, int));
+    int (*fun) (char *, char **, int);
     int min, max, flags;
 };
 
@@ -944,9 +944,9 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep)
 		return 1;
 	    }
 	    for (i = 0; i < 4; i++)
-		iaddr[i] = STOUC(nums[i]);
-	    iport[0] = STOUC(nums[4]);
-	    iport[1] = STOUC(nums[5]);
+		iaddr[i] = (unsigned char) nums[i];
+	    iport[0] = (unsigned char) nums[4];
+	    iport[1] = (unsigned char) nums[5];
 
 	    memcpy(&zdsockp->in.sin_addr, iaddr, sizeof(iaddr));
 	    memcpy(&zdsockp->in.sin_port, iport, sizeof(iport));
@@ -2438,7 +2438,7 @@ zftp_type(char *name, char **args, int flags)
 	fflush(stdout);
 	return 0;
     } else {
-	nt = toupper(STOUC(*str));
+	nt = toupper((unsigned char) *str);
 	/*
 	 * RFC959 specifies other types, but these are the only
 	 * ones we know what to do with.
@@ -2472,7 +2472,7 @@ zftp_mode(char *name, char **args, UNUSED(int flags))
 	fflush(stdout);
 	return 0;
     }
-    nt = str[0] = toupper(STOUC(*str));
+    nt = str[0] = toupper((unsigned char) *str);
     if (str[1] || (nt != 'S' && nt != 'B')) {
 	zwarnnam(name, "transfer mode %s not recognised", str);
 	return 1;
@@ -3075,7 +3075,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func))
     if ((prefs = getsparam_u("ZFTP_PREFS"))) {
 	zfprefs = 0;
 	for (ptr = prefs; *ptr; ptr++) {
-	    switch (toupper(STOUC(*ptr))) {
+	    switch (toupper((unsigned char) *ptr)) {
 	    case 'S':
 		/* sendport */
 		zfprefs |= ZFPF_SNDP;
@@ -3172,7 +3172,7 @@ static struct features module_features = {
 int
 setup_(UNUSED(Module m))
 {
-    return (require_module("zsh/net/tcp", NULL, 0) == 1);
+    return 0;
 }
 
 /**/
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index 56cdab888..171a15b90 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -163,9 +163,9 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
 	*ap = NULL;
 
 	qsort(fs, ncalls, sizeof(f),
-	      (int (*) _((const void *, const void *))) cmpsfuncs);
+	      (int (*) (const void *, const void *)) cmpsfuncs);
 	qsort(as, narcs, sizeof(a),
-	      (int (*) _((const void *, const void *))) cmpparcs);
+	      (int (*) (const void *, const void *)) cmpparcs);
 
 	printf("num  calls                time                       self            name\n-----------------------------------------------------------------------------------\n");
 	for (fp = fs, i = 1; *fp; fp++, i++) {
@@ -179,7 +179,7 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
 		   (*fp)->name);
 	}
 	qsort(fs, ncalls, sizeof(f),
-	      (int (*) _((const void *, const void *))) cmptfuncs);
+	      (int (*) (const void *, const void *)) cmptfuncs);
 
 	for (fp = fs; *fp; fp++) {
 	    printf("\n-----------------------------------------------------------------------------------\n\n");
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index dfd2a2a7a..c2656698c 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -638,7 +638,7 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
 		readchar = cmd->read;
 		cmd->read = -1;
 	    } else
-		readchar = STOUC(buf[used]);
+		readchar = (unsigned char) buf[used];
 	    if (imeta(readchar)) {
 		buf[used++] = Meta;
 		buf[used++] = (char) (readchar ^ 32);
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 2f17c03f1..5eccea7a9 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -462,6 +462,28 @@ lookupstyle(char *ctxt, char *style)
 }
 
 static int
+testforstyle(char *ctxt, char *style)
+{
+    Style s;
+    Stypat p;
+    int found = 0;
+
+    s = (Style)zstyletab->getnode2(zstyletab, style);
+    if (s) {
+	MatchData match;
+	savematch(&match);
+	for (p = s->pats; p; p = p->next)
+	    if (pattry(p->prog, ctxt)) {
+		found = 1;
+		break;
+	    }
+	restorematch(&match);
+    }
+
+    return !found;	/* 0 == success */
+}
+
+static int
 bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 {
     int min, max, n, add = 0, list = ZSLIST_NONE, eval = 0;
@@ -570,6 +592,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
     case 't': min = 2; max = -1; break;
     case 'T': min = 2; max = -1; break;
     case 'm': min = 3; max =  3; break;
+    case 'q': min = 2; max =  2; break;
     case 'g': min = 1; max =  3; break;
     default:
 	zwarnnam(nam, "invalid option: %s", args[0]);
@@ -723,6 +746,15 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    return 1;
 	}
 	break;
+    case 'q':
+	{
+	    int success;
+	    queue_signals();	/* Protect PAT_STATIC */
+	    success = testforstyle(args[1], args[2]);
+	    unqueue_signals();
+	    return success;
+	}
+	break;
     case 'g':
 	{
 	    int ret = 1;
@@ -795,11 +827,11 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 
 	    if (idigit(*s)) {
 		for (min = 0; idigit(*s); s++)
-		    min = (min * 10) + (int) STOUC(*s) - '0';
+		    min = (min * 10) + (int) (unsigned char) *s - '0';
 	    }
 
 	    /* Ternary expressions */
-	    testit = (STOUC(*s) == '(');
+	    testit = ((unsigned char) *s == '(');
 	    if (testit && s[1] == '-')
 	    {
 		/* Allow %(-1... etc. */
@@ -808,25 +840,25 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 	    }
 	    if ((*s == '.' || testit) && idigit(s[1])) {
 		for (max = 0, s++; idigit(*s); s++)
-		    max = (max * 10) + (int) STOUC(*s) - '0';
+		    max = (max * 10) + (int) (unsigned char) *s - '0';
 	    } else if (*s == '.' || testit)
 		s++;
 
-	    if (testit && STOUC(*s)) {
+	    if (testit && (unsigned char) *s) {
 		int actval, testval, endcharl;
 
 		/* Only one number is useful for ternary expressions. */
 		testval = (min >= 0) ? min : (max >= 0) ? max : 0;
 
-		if (specs[STOUC(*s)] && *specs[STOUC(*s)]) {
+		if (specs[(unsigned char) *s] && *specs[(unsigned char) *s]) {
 		    if (presence) {
 			if (testval)
 #ifdef MULTIBYTE_SUPPORT
 			    if (isset(MULTIBYTE))
-				actval = MB_METASTRWIDTH(specs[STOUC(*s)]);
+				actval = MB_METASTRWIDTH(specs[(unsigned char) *s]);
 			    else
 #endif
-				actval = strlen(specs[STOUC(*s)]);
+				actval = strlen(specs[(unsigned char) *s]);
 		        else
 			    actval = 1;
 			actval = right ? (testval < actval) : (testval >= actval);
@@ -834,7 +866,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 			if (right) /* put the sign back */
 			    testval *= -1;
 			/* zero means values are equal, i.e. true */
-			actval = (int)mathevali(specs[STOUC(*s)]) - testval;
+			actval = (int) mathevali(specs[(unsigned char) *s]) - testval;
 		    }
 		} else
 		    actval = presence ? !right : testval;
@@ -855,7 +887,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 		    return NULL;
 	    } else if (skip) {
 		continue;
-	    } else if ((spec = specs[STOUC(*s)])) {
+	    } else if ((spec = specs[(unsigned char) *s])) {
 		int len;
 
 		if ((len = strlen(spec)) > max && max >= 0)
@@ -950,7 +982,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		    zwarnnam(nam, "invalid argument: %s", *ap);
 		    return 1;
 		}
-		specs[STOUC(ap[0][0])] = ap[0] + 2;
+		specs[(unsigned char) ap[0][0]] = ap[0] + 2;
 	    }
 	    out = (char *) zhalloc(olen = 128);
 
@@ -965,7 +997,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
     case 'a':
 	{
 	    char **ap, *cp;
-	    int nbc = 0, colon = 0, pre = 0, suf = 0;
+	    int nbc = 0, pre = 0, suf = 0;
 #ifdef MULTIBYTE_SUPPORT
 	    int prechars = 0;
 #endif /* MULTIBYTE_SUPPORT */
@@ -980,7 +1012,6 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		    int dchars = 0;
 #endif /* MULTIBYTE_SUPPORT */
 
-		    colon++;
 		    if ((d = cp - *ap - nbc) > pre)
 			pre = d;
 #ifdef MULTIBYTE_SUPPORT
@@ -1378,11 +1409,11 @@ rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
 					     "zregexparse-guard"), !lastval))) {
 		LinkNode aln;
 		char **mend;
-		int len;
+		int len = 0;
 
 		queue_signals();
-		mend = getaparam("mend");
-		len = atoi(mend[0]);
+		if ((mend = getaparam("mend")))
+		    len = atoi(mend[0]);
 		unqueue_signals();
 
 		for (i = len; i; i--)
@@ -1864,7 +1895,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	d->vals = d->last = NULL;
 	opt_descs = d;
 	if (!o[1])
-	    sopts[STOUC(*o)] = d;
+	    sopts[(unsigned char) *o] = d;
 	if ((flags & ZOF_MAP) && !map_opt_desc(d)) {
 	    zwarnnam(nam, "cyclic option mapping: %s", args[-1]);
 	    return 1;
@@ -1888,7 +1919,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	}
 	if (!(d = lookup_opt(o + 1))) {
 	    while (*++o) {
-		if (!(d = sopts[STOUC(*o)])) {
+		if (!(d = sopts[(unsigned char) *o])) {
 		    if (fail) {
 			if (*o != '-')
 			    zwarnnam(nam, "bad option: -%c", *o);
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 4ac5d089f..09282d42d 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1230,14 +1230,14 @@ check_param(char *s, int set, int test)
 	else if (idigit(*e))
 	    while (idigit(*e))
 		e++;
-	else if ((ie = itype_end(e, IIDENT, 0)) != e) {
+	else if ((ie = itype_end(e, INAMESPC, 0)) != e) {
 	    do {
 		e = ie;
 		if (comppatmatch && *comppatmatch &&
 		    (*e == Star || *e == Quest))
 		    ie = e + 1;
 		else
-		    ie = itype_end(e, IIDENT, 0);
+		    ie = itype_end(e, INAMESPC, 0);
 	    } while (ie != e);
 	}
 
@@ -2249,8 +2249,9 @@ addmatches(Cadata dat, char **argv)
 	    llpl = strlen(lpre);
 	    llsl = strlen(lsuf);
 
-	    if (llpl + (int)strlen(compqiprefix) + (int)strlen(lipre) != origlpre
-	     || llsl + (int)strlen(compqisuffix) + (int)strlen(lisuf) != origlsuf)
+	    /* This used to reference compqiprefix and compqisuffix, why? */
+	    if (llpl + (int)strlen(qipre) + (int)strlen(lipre) != origlpre
+	     || llsl + (int)strlen(qisuf) + (int)strlen(lisuf) != origlsuf)
 		lenchanged = 1;
 
 	    /* Test if there is an existing -P prefix. */
@@ -2898,9 +2899,9 @@ add_match_data(int alt, char *str, char *orig, Cline line,
 		*t++ = '$';
 		*t++ = '\'';
 		*t++ = '\\';
-		*t++ = '0' + ((STOUC(curchar) >> 6) & 7);
-		*t++ = '0' + ((STOUC(curchar) >> 3) & 7);
-		*t++ = '0' + (STOUC(curchar) & 7);
+		*t++ = '0' + (((unsigned char) curchar >> 6) & 7);
+		*t++ = '0' + (((unsigned char) curchar >> 3) & 7);
+		*t++ = '0' + ((unsigned char) curchar & 7);
 		*t++ = '\'';
 	    } while (cnt == MB_INCOMPLETE && fs < fe);
 	    /* Scanning restarts from the spot after the char we skipped. */
@@ -3252,7 +3253,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 	    /* Now sort the array (it contains matches). */
 	    matchorder = flags;
 	    qsort((void *) rp, n, sizeof(Cmatch),
-		  (int (*) _((const void *, const void *)))matchcmp);
+		  (int (*) (const void *, const void *))matchcmp);
 
 	    /* since the matches are sorted and the default is to remove
 	     * all duplicates, -1 (remove only consecutive dupes) is a no-op,
@@ -3294,7 +3295,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 		sp = (Cmatch *) zhalloc((n + 1) * sizeof(Cmatch));
 		memcpy(sp, rp, (n + 1) * sizeof(Cmatch));
 		qsort((void *) sp, n, sizeof(Cmatch),
-		      (int (*) _((const void *, const void *)))matchcmp);
+		      (int (*) (const void *, const void *))matchcmp);
 		for (asp = sp + 1; *asp; asp++) {
 		    Cmatch *ap = asp - 1, *bp = asp;
 		    if (matcheq(*ap, *bp)) {
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 67a60963e..342611f1f 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -518,7 +518,7 @@ parse_class(Cpattern p, char *iptr)
 	    ch = range_type((char *)iptr, nptr-iptr);
 	    iptr = nptr + 2;
 	    if (ch != PP_UNKWN)
-		*optr++ = STOUC(Meta) + ch;
+		*optr++ = (unsigned char) Meta + ch;
 	} else {
 	    /* characters stay metafied */
 	    char *ptr1 = iptr;
@@ -829,7 +829,9 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 
  ca_args:
 
-    if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
+    if (mstr && (dat.aflags & CAF_MATCH) &&
+	    (match = parse_cmatcher(name, mstr)) == pcm_err)
+    {
 	zsfree(mstr);
 	zfree(dat.dpar, dparsize);
 	return 1;
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 0dc64db6a..5619160a9 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -291,12 +291,12 @@ getcolval(char *s, int multi)
 	    case '?': *p = '\177'; break;
 	    default:
 		if (*s >= '0' && *s <= '7') {
-		    int i = STOUC(*s);
+		    int i = (unsigned char) *s;
 
 		    if (*++s >= '0' && *s <= '7') {
-			i = (i * 8) + STOUC(*s);
+			i = (i * 8) + (unsigned char) *s;
 			if (*++s >= '0' && *s <= '7')
-			    i = (i * 8) + STOUC(*s);
+			    i = (i * 8) + (unsigned char) *s;
 		    }
 		    *p = (char) i;
 		} else
@@ -305,7 +305,7 @@ getcolval(char *s, int multi)
 	} else if (*s == '^') {
 	    if ((s[1] >= '@' && s[1] <= '_') ||
 		(s[1] >= 'a' && s[1] <= 'z'))
-		*p = (char) (STOUC(*s) & ~0x60);
+		*p = (char) ((unsigned char) *s & ~0x60);
 	    else if (s[1] == '?')
 		*p = '\177';
 	    else {
@@ -794,7 +794,7 @@ clnicezputs(int do_colors, char *s, int ml)
 	 */
 	for (t = sptr; *t; t++) {
 	    /* Input is metafied... */
-	    int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
+	    int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t;
 	    /* Is the screen full? */
 	    if (ml == mlend - 1 && col == zterm_columns - 1) {
 		mlprinted = ml - oml;
@@ -852,7 +852,7 @@ clnicezputs(int do_colors, char *s, int ml)
 	    cc = *s++ ^ 32;
 
 	for (t = nicechar(cc); *t; t++) {
-	    int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
+	    int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t;
 	    if (ml == mlend - 1 && col == zterm_columns - 1) {
 		mlprinted = ml - oml;
 		return 0;
@@ -1072,7 +1072,7 @@ static int
 compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 {
     char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12];
-    int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat;
+    int l = 0, cc = 0, m, ask, beg, stat;
 
     if ((stat = !fmt)) {
 	if (mlbeg >= 0) {
@@ -1118,48 +1118,46 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 		m = 0;
 		switch (cchar) {
 		case ZWC('%'):
-		    if (dopr == 1)
+		    if (dopr == 1) {
+			applytextattributes(0);
 			putc('%', shout);
+		    }
 		    cc++;
 		    break;
 		case ZWC('n'):
 		    if (!stat) {
 			sprintf(nc, "%d", n);
-			if (dopr == 1)
+			if (dopr == 1) {
+			    applytextattributes(0);
 			    fputs(nc, shout);
+			}
 			/* everything here is ASCII... */
 			cc += strlen(nc);
 		    }
 		    break;
 		case ZWC('B'):
-		    b = 1;
 		    if (dopr)
-			tcout(TCBOLDFACEBEG);
+			tsetattrs(TXTBOLDFACE);
 		    break;
 		case ZWC('b'):
-		    b = 0; m = 1;
 		    if (dopr)
-			tcout(TCALLATTRSOFF);
+			tunsetattrs(TXTBOLDFACE);
 		    break;
 		case ZWC('S'):
-		    s = 1;
 		    if (dopr)
-			tcout(TCSTANDOUTBEG);
+			tsetattrs(TXTSTANDOUT);
 		    break;
 		case ZWC('s'):
-		    s = 0; m = 1;
 		    if (dopr)
-			tcout(TCSTANDOUTEND);
+			tunsetattrs(TXTSTANDOUT);
 		    break;
 		case ZWC('U'):
-		    u = 1;
 		    if (dopr)
-			tcout(TCUNDERLINEBEG);
+			tsetattrs(TXTUNDERLINE);
 		    break;
 		case ZWC('u'):
-		    u = 0; m = 1;
 		    if (dopr)
-			tcout(TCUNDERLINEEND);
+			tunsetattrs(TXTUNDERLINE);
 		    break;
 		case ZWC('F'):
 		case ZWC('K'):
@@ -1173,20 +1171,28 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 		    } else
 			atr = match_colour(NULL, is_fg, arg);
 		    if (atr != TXT_ERROR && dopr)
-			set_colour_attribute(atr, is_fg ? COL_SEQ_FG :
-					     COL_SEQ_BG, 0);
+			tsetattrs(atr);
 		    break;
 		case ZWC('f'):
 		    if (dopr)
-			set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0);
+			tunsetattrs(TXTFGCOLOUR);
 		    break;
 		case ZWC('k'):
 		    if (dopr)
-			set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0);
+			tunsetattrs(TXTBGCOLOUR);
+		    break;
+		case ZWC('H'):
+		    if (*p == '{') {
+			p = parsehighlight(p + 1, '}', &atr);
+			if (atr != TXT_ERROR && dopr)
+			    treplaceattrs(atr);
+		    }
 		    break;
 		case ZWC('{'):
 		    if (arg)
 			cc += arg;
+		    if (dopr)
+			applytextattributes(0);
 		    for (; *p && (*p != '%' || p[1] != '}'); p++)
 			if (dopr)
 			    putc(*p == Meta ? *++p ^ 32 : *p, shout);
@@ -1197,7 +1203,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 		    if (stat) {
 			sprintf(nc, "%d/%d", (n ? mlastm : mselect),
 				listdat.nlist);
-			m = 2;
+			m = 1;
 		    }
 		    break;
 		case ZWC('M'):
@@ -1205,20 +1211,20 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 			sprintf(nbuf, "%d/%d", (n ? mlastm : mselect),
 				listdat.nlist);
 			sprintf(nc, "%-9s", nbuf);
-			m = 2;
+			m = 1;
 		    }
 		    break;
 		case ZWC('l'):
 		    if (stat) {
 			sprintf(nc, "%d/%d", ml + 1, listdat.nlines);
-			m = 2;
+			m = 1;
 		    }
 		    break;
 		case ZWC('L'):
 		    if (stat) {
 			sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines);
 			sprintf(nc, "%-9s", nbuf);
-			m = 2;
+			m = 1;
 		    }
 		    break;
 		case ZWC('p'):
@@ -1230,7 +1236,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 				    ((ml + 1) * 100) / listdat.nlines);
 			else
 			    strcpy(nc, "Top");
-			m = 2;
+			m = 1;
 		    }
 		    break;
 		case ZWC('P'):
@@ -1242,25 +1248,19 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 				    ((ml + 1) * 100) / listdat.nlines);
 			else
 			    strcpy(nc, "Top   ");
-			m = 2;
+			m = 1;
 		    }
 		    break;
 		}
-		if (m == 2 && dopr == 1) {
+		if (m && dopr) {
 		    /* nc only contains ASCII text */
 		    int l = strlen(nc);
 
 		    if (l + cc > zterm_columns - 2)
 			nc[l -= l + cc - (zterm_columns - 2)] = '\0';
+		    applytextattributes(0);
 		    fputs(nc, shout);
 		    cc += l;
-		} else if (dopr && m == 1) {
-		    if (b)
-			tcout(TCBOLDFACEBEG);
-		    if (s)
-			tcout(TCSTANDOUTBEG);
-		    if (u)
-			tcout(TCUNDERLINEBEG);
 		}
 	    } else
 		break;
@@ -1276,6 +1276,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 		cc = 0;
 	    }
 	    if (dopr == 1) {
+		applytextattributes(0);
 		if (ml == mlend - 1 && (cc % zterm_columns) ==
 		    zterm_columns - 1) {
 		    dopr = 0;
@@ -1311,6 +1312,8 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
 	}
     }
     if (dopr) {
+	treplaceattrs(0);
+	applytextattributes(0);
         if (!(cc % zterm_columns))
             fputs(" \010", shout);
         cleareol();
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 56e5509a4..ddcecd589 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -2045,12 +2045,12 @@ join_strs(int la, char *sa, int lb, char *sb)
 				zlelineasstring(line, mp->llen, 0, &convlen,
 						NULL, 0);
 			    if (rr <= convlen) {
-				char *or = rs;
+				ptrdiff_t diff = rp - rs;
 				int alloclen = (convlen > 20) ? convlen : 20;
 
 				rs = realloc(rs, (rl += alloclen));
 				rr += alloclen;
-				rp += rs - or;
+				rp = rs + diff;
 			    }
 			    memcpy(rp, convstr, convlen);
 			    rp += convlen;
@@ -2073,11 +2073,11 @@ join_strs(int la, char *sa, int lb, char *sb)
 	} else {
 	    /* Same character, just take it. */
 	    if (rr <= 1 /* HERE charlen */) {
-		char *or = rs;
+		ptrdiff_t diff = rp - rs;
 
 		rs = realloc(rs, (rl += 20));
 		rr += 20;
-		rp += rs - or;
+		rp = rs + diff;
 	    }
 	    /* HERE: multibyte char */
 	    *rp++ = *sa;
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 57789c0f3..cd8c7dd64 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -897,7 +897,7 @@ void
 do_allmatches(UNUSED(int end))
 {
     int first = 1, nm = nmatches - 1, omc = menucmp, oma = menuacc, e;
-    Cmatch *mc;
+    Cmatch *mc = 0;
     struct menuinfo mi;
     char *p = (brbeg ? ztrdup(lastbrbeg->str) : NULL);
 
@@ -915,10 +915,10 @@ do_allmatches(UNUSED(int end))
 #endif
     }
 
+    if (minfo.group)
+	mc = (minfo.group)->matches;
 
-    mc = (minfo.group)->matches;
-
-    while (1) {
+    while (mc) {
 	if (!((*mc)->flags & CMF_ALL)) {
 	    if (!first)
 		accept_last();
@@ -1731,8 +1731,6 @@ calclist(int showall)
                                  width < zterm_columns && nth < g->dcount;
                                  nth++, tcol++) {
 
-                                m = *p;
-
                                 if (tcol == tcols) {
                                     tcol = 0;
                                     tlines++;
@@ -1994,7 +1992,6 @@ printlist(int over, CLPrintFunc printm, int showall)
 		     (listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) {
 		    if (pnl) {
 			putc('\n', shout);
-			pnl = 0;
 			ml++;
 			if (cl >= 0 && --cl <= 1) {
 			    cl = -1;
@@ -2087,7 +2084,6 @@ printlist(int over, CLPrintFunc printm, int showall)
                         (showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) {
 			if (pnl) {
 			    putc('\n', shout);
-			    pnl = 0;
 			    ml++;
 			    if (cl >= 0 && --cl <= 1) {
 				cl = -1;
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 77ccdebf7..360667884 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -911,7 +911,7 @@ struct cadef {
     Caarg rest;			/* the rest-argument */
     char **defs;		/* the original strings */
     int ndefs;			/* number of ... */
-    int lastt;			/* last time this was used */
+    time_t lastt;		/* last time this was used */
     Caopt *single;		/* array of single-letter options */
     char *match;		/* -M spec to use */
     int argsactive;		/* if normal arguments are still allowed */
@@ -2935,7 +2935,7 @@ struct cvdef {
     Cvval vals;			/* value definitions */
     char **defs;		/* original strings */
     int ndefs;			/* number of ... */
-    int lastt;			/* last time used */
+    time_t lastt;		/* last time used */
     int words;                  /* if to look at other words */
 };
 
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index c93777b65..a68c5296e 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -283,9 +283,9 @@ selectargument(UNUSED(char **args))
     free(linein);
 
     if (IS_THINGY(bindk, selectinshellword)) {
-	ZLE_CHAR_T *match = ZWS("`\'\"");
-	ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}");
-	ZLE_CHAR_T *ematch = match, *found;
+	const ZLE_CHAR_T *match = ZWS("`\'\"");
+	const ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}");
+	const ZLE_CHAR_T *ematch = match, *found;
 	int start, end = zlecs;
 	/* for 'in' widget, don't include initial blanks ... */
 	while (mark < zlecs && ZC_iblank(zleline[mark]))
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 391586c4a..5bb9e7a5e 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -186,7 +186,7 @@ typedef struct thingy *Thingy;
 
 /* widgets (ZLE functions) */
 
-typedef int (*ZleIntFunc) _((char **));
+typedef int (*ZleIntFunc) (char **);
 
 struct widget {
     int flags;		/* flags (see below) */
@@ -258,6 +258,9 @@ struct modifier {
 #define MOD_NULL  (1<<5)   /* throw away text for the vi cut buffer */
 #define MOD_CHAR  (1<<6)   /* force character-wise movement */
 #define MOD_LINE  (1<<7)   /* force line-wise movement */
+#define MOD_PRI   (1<<8)   /* OS primary selection for the vi cut buffer */
+#define MOD_CLIP  (1<<9)   /* OS clipboard for the vi cut buffer */
+#define MOD_OSSEL (MOD_PRI | MOD_CLIP)  /* either system selection */
 
 /* current modifier status */
 
@@ -316,7 +319,7 @@ struct vichange {
 
 typedef struct keymap *Keymap;
 
-typedef void (*KeyScanFunc) _((char *, Thingy, char *, void *));
+typedef void (*KeyScanFunc) (char *, Thingy, char *, void *);
 
 #define invicmdmode() (!strcmp(curkeymapname, "vicmd"))
 
@@ -432,6 +435,8 @@ enum {
 struct region_highlight {
     /* Attributes turned on in the region */
     zattr atr;
+    /* Priority for this region relative to others that overlap */
+    int layer;
     /* Start of the region */
     int start;
     /* Start of the region in metafied ZLE line */
@@ -487,11 +492,7 @@ typedef struct {
      */
     REFRESH_CHAR chr;
     /*
-     * Its attributes.  'On' attributes (TXT_ATTR_ON_MASK) are
-     * applied before the character, 'off' attributes (TXT_ATTR_OFF_MASK)
-     * after it.  'On' attributes are present for all characters that
-     * need the effect; 'off' attributes are only present for the
-     * last character in the sequence.
+     * Its attributes.
      */
     zattr atr;
 } REFRESH_ELEMENT;
@@ -523,7 +524,7 @@ typedef REFRESH_ELEMENT *REFRESH_STRING;
     ((int)((unsigned)(x) - ZSH_INVALID_WCHAR_BASE))
 /* Turn a single byte character into a private wide character */
 #define ZSH_CHAR_TO_INVALID_WCHAR(x)			\
-    ((wchar_t)(STOUC(x) + ZSH_INVALID_WCHAR_BASE))
+    ((wchar_t)((unsigned char) x + ZSH_INVALID_WCHAR_BASE))
 #endif
 
 
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index cfaa70dae..0fdad70d9 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -68,6 +68,13 @@ Keymap isearch_keymap;
  */
 #define GETZLETEXT(ent)	((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam)
 
+/*
+ * Flag that edits have been made to a zle line.
+ * If not set, nothing to forget.
+ */
+/**/
+int have_edits = 0;
+
 /**/
 void
 remember_edits(void)
@@ -81,6 +88,7 @@ remember_edits(void)
 	    if (ent->zle_text)
 		free(ent->zle_text);
 	    ent->zle_text = zlemetaline ? ztrdup(line) : line;
+	    have_edits = 1;
 	} else if (!zlemetaline)
 	    free(line);
     }
@@ -90,6 +98,10 @@ remember_edits(void)
 void
 forget_edits(void)
 {
+    if (!have_edits) {
+       return;
+    }
+    have_edits = 0;
     Histent he;
 
     for (he = hist_ring; he; he = up_histent(he)) {
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index d90838f03..5012917f5 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -404,7 +404,7 @@ static void
 scankeys(HashNode hn, UNUSED(int flags))
 {
     Key k = (Key) hn;
-    int f = k->nam[0] == Meta ? STOUC(k->nam[1])^32 : STOUC(k->nam[0]);
+    int f = k->nam[0] == Meta ? (unsigned char) k->nam[1]^32 : (unsigned char) k->nam[0];
     char m[3];
 
     while(skm_last < f) {
@@ -566,7 +566,7 @@ mod_export int
 bindkey(Keymap km, const char *seq, Thingy bind, char *str)
 {
     Key k;
-    int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+    int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0];
     char *buf, *ptr;
 
     if(km->flags & KM_IMMUTABLE)
@@ -661,7 +661,7 @@ keybind(Keymap km, char *seq, char **strp)
     Key k;
 
     if(ztrlen(seq) == 1) {
-	int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+	int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0];
 	Thingy bind = km->first[f];
 
 	if(bind)
@@ -687,7 +687,7 @@ keyisprefix(Keymap km, char *seq)
     if(!*seq)
 	return 1;
     if(ztrlen(seq) == 1) {
-	int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]);
+	int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0];
 
 	if(km->first[f])
 	    return 0;
@@ -745,7 +745,7 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func))
     static struct opn {
 	char o;
 	char selp;
-	int (*func) _((char *, char *, Keymap, char **, Options, char));
+	int (*func) (char *, char *, Keymap, char **, Options, char);
 	int min, max;
     } const opns[] = {
 	{ 'l', 0, bin_bindkey_lsmaps, 0,  -1 },
@@ -764,10 +764,10 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func))
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
+    for(op = opns; op->o && !OPT_ISSET(ops,(unsigned char) op->o); op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(OPT_ISSET(ops,STOUC(opp->o))) {
+	    if(OPT_ISSET(ops,(unsigned char) opp->o)) {
 		zwarnnam(name, "incompatible operation selection options");
 		return 1;
 	    }
@@ -1049,7 +1049,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, Options ops,
 	    char m[3];
 
 	    if(len < 2 || len > 2 + (bseq[1] == '-') ||
-	       (first = STOUC(bseq[0])) > (last = STOUC(bseq[len - 1]))) {
+	       (first = (unsigned char) bseq[0]) > (last = (unsigned char) bseq[len - 1])) {
 		zwarnnam(name, "malformed key range `%s'", useq);
 		ret = 1;
 	    } else {
@@ -1149,8 +1149,8 @@ scanbindlist(char *seq, Thingy bind, char *str, void *magic)
     if(bind == bs->bind && (bind || !strcmp(str, bs->str)) &&
        ztrlen(seq) == 1 && ztrlen(bs->lastseq) == 1) {
 	int l = bs->lastseq[1] ?
-	    STOUC(bs->lastseq[1]) ^ 32 : STOUC(bs->lastseq[0]);
-	int t = seq[1] ? STOUC(seq[1]) ^ 32 : STOUC(seq[0]);
+	    (unsigned char) bs->lastseq[1] ^ 32 : (unsigned char) bs->lastseq[0];
+	int t = seq[1] ? (unsigned char) seq[1] ^ 32 : (unsigned char) seq[0];
 
 	if(t == l + 1) {
 	    zsfree(bs->lastseq);
@@ -1315,7 +1315,7 @@ default_bindings(void)
     Keymap vismap = newkeymap(NULL, "visual");
     Keymap smap = newkeymap(NULL, ".safe");
     Keymap vimaps[2], vilmaps[2], kptr;
-    char buf[3], *ed;
+    char buf[3];
     int i;
 
     /* vi insert mode and emacs mode:  *
@@ -1445,20 +1445,14 @@ default_bindings(void)
 	}
 
     /* Put the keymaps in the right namespace.  The "main" keymap  *
-     * will be linked to the "emacs" keymap, except that if VISUAL *
-     * or EDITOR contain the string "vi" then it will be linked to *
-     * the "viins" keymap.                                         */
+     * will be linked to the "emacs" keymap.                       */
     linkkeymap(vmap, "viins", 0);
     linkkeymap(emap, "emacs", 0);
     linkkeymap(amap, "vicmd", 0);
     linkkeymap(oppmap, "viopp", 0);
     linkkeymap(vismap, "visual", 0);
     linkkeymap(smap, ".safe", 1);
-    if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) ||
-	((ed = zgetenv("EDITOR")) && strstr(ed, "vi")))
-	linkkeymap(vmap, "main", 0);
-    else
-	linkkeymap(emap, "main", 0);
+    linkkeymap(emap, "main", 0);
 
     /* the .safe map cannot be modified or deleted */
     smap->flags |= KM_IMMUTABLE;
@@ -1526,10 +1520,10 @@ getrestchar_keybuf(void)
      */
     while (1) {
 	if (bufind < buflen) {
-	    c = STOUC(keybuf[bufind++]);
+	    c = (unsigned char) keybuf[bufind++];
 	    if (c == Meta) {
 		DPUTS(bufind == buflen, "Meta at end of keybuf");
-		c = STOUC(keybuf[bufind++]) ^ 32;
+		c = (unsigned char) keybuf[bufind++] ^ 32;
 	    }
 	} else {
 	    /*
@@ -1586,7 +1580,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
     Thingy func = t_undefinedkey;
     char *str = NULL;
     int lastlen = 0, lastc = lastchar;
-    int timeout = 0;
+    int timeout = 0, csi = 0;
 
     keybuflen = 0;
     keybuf[0] = 0;
@@ -1636,7 +1630,31 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
 	    }
 #endif
 	}
-	if (!ispfx)
+
+	/* CSI key sequences have a well defined structure so if we currently
+	 * have an incomplete one, loop so the rest of it will be included in
+	 * the key sequence if that arrives within the timeout. */
+	if (!csi && keybuflen >= 3 && keybuf[keybuflen - 3] == '\033' &&
+		keybuf[keybuflen - 2] == '[')
+	    csi = keybuflen - 1;
+	if (csi) {
+	    if (keybuf[keybuflen - 2] == Meta || keybuf[keybuflen - 1] < 0x20
+		   || keybuf[keybuflen - 1] > 0x3f) {
+	    /* If we reach the end of a valid CSI sequence and the matched key
+	     * binding is for part of the CSI introduction, select instead the
+	     * undefined-key widget and consume the full sequence from the
+	     * input buffer. */
+		if (keybuf[keybuflen - 1] >= 0x40 &&
+			keybuf[keybuflen - 1] <= 0x7e && lastlen > csi - 2 &&
+			lastlen <= csi) {
+		    func = t_undefinedkey;
+		    lastlen = keybuflen;
+		}
+		csi = 0;
+	    }
+	}
+
+	if (!ispfx && !csi)
 	    break;
     }
     if(!lastlen && keybuflen)
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 9edf30e01..1afb1bf58 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -737,6 +737,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
 			) {
 			/* Handle the fd. */
 			char *fdbuf;
+			Thingy save_lbindk = refthingy(lbindk);
 			{
 			    char buf[BDIGBUFSIZE];
 			    convbase(buf, lwatch_fd->fd, 10);
@@ -779,6 +780,8 @@ raw_getbyte(long do_keytmout, char *cptr, int full)
 			     */
 			    errtry = 1;
 			}
+			unrefthingy(lbindk);
+			lbindk = save_lbindk;
 		    }
 		}
 		/* Function may have invalidated the display. */
@@ -876,7 +879,7 @@ getbyte(long do_keytmout, int *timeout, int full)
 #endif
 
     if (kungetct)
-	ret = STOUC(kungetbuf[--kungetct]);
+	ret = (unsigned char) kungetbuf[--kungetct];
     else {
 	for (;;) {
 	    int q = queue_signal_level();
@@ -940,7 +943,7 @@ getbyte(long do_keytmout, int *timeout, int full)
 	else if (cc == '\n')
 	    cc = '\r';
 
-	ret = STOUC(cc);
+	ret = (unsigned char) cc;
     }
     /*
      * curvichg.buf is raw bytes, not wide characters, so is dealt
@@ -1230,9 +1233,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 	char *pptbuf;
 	int pptlen;
 
-	pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL,
-				       &pmpt_attr),
+	pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL),
 			  &pptlen);
+	pmpt_attr = txtcurrentattrs;
 	write_loop(2, pptbuf, pptlen);
 	free(pptbuf);
 	return shingetline();
@@ -1267,10 +1270,13 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
     fetchttyinfo = 0;
     trashedzle = 0;
     raw_lp = lp;
-    lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr);
+    txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
+    lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL);
+    pmpt_attr = txtcurrentattrs;
     raw_rp = rp;
-    rpmpt_attr = pmpt_attr;
-    rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL, &rpmpt_attr);
+    rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL);
+    rpmpt_attr = txtcurrentattrs;
+    prompt_attr = mixattrs(pmpt_attr, rpmpt_attr);
     free_prepostdisplay();
 
     zlereadflags = flags;
@@ -2009,17 +2015,18 @@ reexpandprompt(void)
 	    char *new_lprompt, *new_rprompt;
 	    looping = reexpanding;
 
-	    new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL,
-				       &pmpt_attr);
+	    txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
+	    new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL);
+	    pmpt_attr = txtcurrentattrs;
 	    free(lpromptbuf);
 	    lpromptbuf = new_lprompt;
 
 	    if (looping != reexpanding)
 		continue;
 
-	    rpmpt_attr = pmpt_attr;
-	    new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL,
-				       &rpmpt_attr);
+	    new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL);
+	    rpmpt_attr = txtcurrentattrs;
+	    prompt_attr = mixattrs(pmpt_attr, rpmpt_attr);
 	    free(rpromptbuf);
 	    rpromptbuf = new_rprompt;
 	} while (looping != reexpanding);
@@ -2065,6 +2072,8 @@ trashzle(void)
 	trashedzle = 1;
 	zrefresh();
 	showinglist = sl;
+	treplaceattrs(prompt_attr);
+	applytextattributes(0);
 	moveto(nlnct, 0);
 	if (clearflag && tccan(TCCLEAREOD)) {
 	    tcout(TCCLEAREOD);
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 30b5d4447..f076bdd61 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -36,8 +36,8 @@
  * non-zero width followed by an arbitrary (but typically small)
  * number of characters that have zero width (combining characters).
  *
- * The allocated size for each array is given by ?mw_size; nmw_ind
- * is the next free element, i.e. nmwbuf[nmw_ind] will be the next
+ * The allocated size for each array is given by omw_size and nmw_size;
+ * nmw_ind is the next free element, i.e. nmwbuf[nmw_ind] will be the next
  * element to be written (we never insert into omwbuf).  We initialise
  * nmw_ind to 1 to avoid the index stored in the character looking like a
  * NULL.  This wastees a word but it's safer than messing with pointers.
@@ -149,7 +149,7 @@ char *lpromptbuf, *rpromptbuf;
 /* Text attributes after displaying prompts */
 
 /**/
-zattr pmpt_attr, rpmpt_attr;
+zattr pmpt_attr, rpmpt_attr, prompt_attr;
 
 /* number of lines displayed */
 
@@ -203,12 +203,18 @@ int predisplaylen, postdisplaylen;
 
 
 /*
- * Attributes used by default on the command line, and
- * attributes for highlighting special (unprintable) characters
- * displayed on screen.
+ * Attributes used by default on the command line,
+ * for highlighting special (unprintable) characters displayed on screen,
+ * and for ellipsis continuation markers.
  */
 
-static zattr default_atr_on, special_atr_on;
+static zattr default_attr, special_attr, ellipsis_attr;
+
+/*
+ * Layer applied to highlighting for special characters
+ */
+
+static int special_layer;
 
 /*
  * Array of region highlights, no special termination.
@@ -245,20 +251,20 @@ char *tcout_func_name;
 int cost;
 
 # define SELECT_ADD_COST(X)	(cost += X)
-# define zputc(a)		(zwcputc(a, NULL), cost++)
+# define zputc(a)		(zwcputc(a), cost++)
 # define zwrite(a, b)		(zwcwrite((a), (b)), \
 				 cost += ((b) * ZLE_CHAR_SIZE))
 #else
 # define SELECT_ADD_COST(X)
-# define zputc(a)		zwcputc(a, NULL)
+# define zputc(a)		zwcputc(a)
 # define zwrite(a, b)		zwcwrite((a), (b))
 #endif
 
-static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 };
+static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), TXT_ERROR };
 #ifdef MULTIBYTE_SUPPORT
-static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 };
+static const REFRESH_ELEMENT zr_dt = { ZWC('.'), TXT_ERROR };
 #endif
-static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 };
+static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), TXT_ERROR };
 static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 };
 static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
 
@@ -269,10 +275,10 @@ static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 };
 static const REFRESH_ELEMENT zr_end_ellipsis[] = {
     { ZWC(' '), 0 },
     { ZWC('<'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
     { ZWC(' '), 0 },
 };
 #define ZR_END_ELLIPSIS_SIZE	\
@@ -281,16 +287,16 @@ static const REFRESH_ELEMENT zr_end_ellipsis[] = {
 static const REFRESH_ELEMENT zr_mid_ellipsis1[] = {
     { ZWC(' '), 0 },
     { ZWC('<'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
 };
 #define ZR_MID_ELLIPSIS1_SIZE	\
     ((int)(sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0])))
 
 static const REFRESH_ELEMENT zr_mid_ellipsis2[] = {
-    { ZWC('>'), 0 },
+    { ZWC('>'), TXT_ERROR },
     { ZWC(' '), 0 },
 };
 #define ZR_MID_ELLIPSIS2_SIZE	\
@@ -298,10 +304,10 @@ static const REFRESH_ELEMENT zr_mid_ellipsis2[] = {
 
 static const REFRESH_ELEMENT zr_start_ellipsis[] = {
     { ZWC('>'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
-    { ZWC('.'), 0 },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
+    { ZWC('.'), TXT_ERROR },
 };
 #define ZR_START_ELLIPSIS_SIZE	\
     ((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0])))
@@ -316,14 +322,15 @@ static void
 zle_set_highlight(void)
 {
     char **atrs = getaparam("zle_highlight");
-    int special_atr_on_set = 0;
-    int region_atr_on_set = 0;
-    int isearch_atr_on_set = 0;
-    int suffix_atr_on_set = 0;
-    int paste_atr_on_set = 0;
+    int special_attr_set = 0;
+    int region_attr_set = 0;
+    int isearch_attr_set = 0;
+    int suffix_attr_set = 0;
+    int paste_attr_set = 0;
+    int ellipsis_attr_set = 0;
     struct region_highlight *rhp;
 
-    special_atr_on = default_atr_on = 0;
+    special_attr = default_attr = 0;
     if (!region_highlights) {
 	region_highlights = (struct region_highlight *)
 	    zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight));
@@ -336,46 +343,63 @@ zle_set_highlight(void)
 	}
     }
 
+    /* Default layers */
+    region_highlights[0].layer = 20; /* region */
+    region_highlights[1].layer = 20; /* isearch */
+    region_highlights[2].layer = 10; /* suffix */
+    region_highlights[3].layer = 15; /* paste */
+    special_layer = 30;
+
     if (atrs) {
 	for (; *atrs; atrs++) {
 	    if (!strcmp(*atrs, "none")) {
 		/* reset attributes for consistency... usually unnecessary */
-		special_atr_on = default_atr_on = 0;
-		special_atr_on_set = 1;
-		paste_atr_on_set = region_atr_on_set =
-		    isearch_atr_on_set = suffix_atr_on_set = 1;
+		special_attr = default_attr = 0;
+		special_attr_set = 1;
+		paste_attr_set = region_attr_set =
+		    isearch_attr_set = suffix_attr_set = 1;
 	    } else if (strpfx("default:", *atrs)) {
-		match_highlight(*atrs + 8, &default_atr_on);
+		match_highlight(*atrs + 8, &default_attr, NULL);
 	    } else if (strpfx("special:", *atrs)) {
-		match_highlight(*atrs + 8, &special_atr_on);
-		special_atr_on_set = 1;
+		match_highlight(*atrs + 8, &special_attr, &special_layer);
+		special_attr_set = 1;
 	    } else if (strpfx("region:", *atrs)) {
-		match_highlight(*atrs + 7, &region_highlights[0].atr);
-		region_atr_on_set = 1;
+		match_highlight(*atrs + 7, &(region_highlights[0].atr),
+			&(region_highlights[0].layer));
+		region_attr_set = 1;
 	    } else if (strpfx("isearch:", *atrs)) {
-		match_highlight(*atrs + 8, &(region_highlights[1].atr));
-		isearch_atr_on_set = 1;
+		match_highlight(*atrs + 8, &(region_highlights[1].atr),
+			&(region_highlights[1].layer));
+		isearch_attr_set = 1;
 	    } else if (strpfx("suffix:", *atrs)) {
-		match_highlight(*atrs + 7, &(region_highlights[2].atr));
-		suffix_atr_on_set = 1;
+		match_highlight(*atrs + 7, &(region_highlights[2].atr),
+			&(region_highlights[2].layer));
+		suffix_attr_set = 1;
 	    } else if (strpfx("paste:", *atrs)) {
-		match_highlight(*atrs + 6, &(region_highlights[3].atr));
-		paste_atr_on_set = 1;
+		match_highlight(*atrs + 6, &(region_highlights[3].atr),
+			&(region_highlights[3].layer));
+		paste_attr_set = 1;
+	    } else if (strpfx("ellipsis:", *atrs)) {
+		match_highlight(*atrs + 9, &ellipsis_attr, NULL);
+		ellipsis_attr_set = 1;
 	    }
 	}
     }
 
-    /* Defaults */
-    if (!special_atr_on_set)
-	special_atr_on = TXTSTANDOUT;
-    if (!region_atr_on_set)
+    /* Default attributes */
+    if (!special_attr_set)
+	special_attr = TXTSTANDOUT;
+    if (!region_attr_set)
 	region_highlights[0].atr = TXTSTANDOUT;
-    if (!isearch_atr_on_set)
+    if (!isearch_attr_set)
 	region_highlights[1].atr = TXTUNDERLINE;
-    if (!suffix_atr_on_set)
+    if (!suffix_attr_set)
 	region_highlights[2].atr = TXTBOLDFACE;
-    if (!paste_atr_on_set)
+    if (!paste_attr_set)
 	region_highlights[3].atr = TXTSTANDOUT;
+    if (!ellipsis_attr_set)
+	ellipsis_attr = TXTBGCOLOUR | ((zattr) 3 << TXT_ATTR_BG_COL_SHIFT) |
+		TXTFGCOLOUR | ((zattr) 4 << TXT_ATTR_FG_COL_SHIFT);
 
     allocate_colour_buffer();
 }
@@ -400,14 +424,13 @@ zle_free_highlight(void)
 char **
 get_region_highlight(UNUSED(Param pm))
 {
-    int arrsize = n_region_highlights;
+    int arrsize = n_region_highlights - N_SPECIAL_HIGHLIGHTS;
     char **retarr, **arrp;
     struct region_highlight *rhp;
 
     /* region_highlights may not have been set yet */
-    if (!arrsize)
+    if (!n_region_highlights)
 	return hmkarray(NULL);
-    arrsize -= N_SPECIAL_HIGHLIGHTS;
     DPUTS(arrsize < 0, "arrsize is negative from n_region_highlights");
     arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *));
 
@@ -415,20 +438,18 @@ get_region_highlight(UNUSED(Param pm))
     for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS;
 	 arrsize--;
 	 rhp++, arrp++) {
-	char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
-	int atrlen, alloclen;
-	const char memo_equals[] = "memo=";
-
-	sprintf(digbuf1, "%d", rhp->start);
-	sprintf(digbuf2, "%d", rhp->end);
-
-	atrlen = output_highlight(rhp->atr, NULL);
-	alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
-	    3; /* 2 spaces, 1 terminating NUL */
+	char digbuf[2 * DIGBUFSIZE], layerbuf[7 + DIGBUFSIZE];
+	int offset;
+	const char memo_equals[] = " memo=";
+	int alloclen = sprintf(digbuf, "%d %d", rhp->start, rhp->end) +
+	    output_highlight(rhp->atr, NULL) +
+	    2; /* space and terminating NUL */
 	if (rhp->flags & ZRH_PREDISPLAY)
-	    alloclen += 2; /* "P " */
+	    alloclen++; /* "P" */
+	if (rhp->layer != 10)
+	    alloclen += sprintf(layerbuf, ",layer=%d", rhp->layer);
 	if (rhp->memo)
-	    alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo);
+	    alloclen += sizeof(memo_equals) - 1 + strlen(rhp->memo);
 	*arrp = (char *)zhalloc(alloclen * sizeof(char));
 	/*
 	 * On input we allow a space after the flags.
@@ -437,13 +458,14 @@ get_region_highlight(UNUSED(Param pm))
 	 * into three words, and then check the first to
 	 * see if there are flags.  However, it's arguable.
 	 */
-	sprintf(*arrp, "%s%s %s ",
+	offset = sprintf(*arrp, "%s%s ",
 		(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
-		digbuf1, digbuf2);
-	(void)output_highlight(rhp->atr, *arrp + strlen(*arrp));
+		digbuf);
+	(void)output_highlight(rhp->atr, *arrp + offset);
 
+	if (rhp->layer != 10)
+	    strcat(*arrp, layerbuf);
 	if (rhp->memo) {
-	    strcat(*arrp, " ");
 	    strcat(*arrp, memo_equals);
 	    strcat(*arrp, rhp->memo);
 	}
@@ -452,12 +474,10 @@ get_region_highlight(UNUSED(Param pm))
     return retarr;
 }
 
-
 /*
  * The parameter system requires the pm argument, but this
  * may be NULL if called directly.
  */
-
 /**/
 void
 set_region_highlight(UNUSED(Param pm), char **aval)
@@ -516,7 +536,8 @@ set_region_highlight(UNUSED(Param pm), char **aval)
 	while (inblank(*strp))
 	    strp++;
 
-	strp = (char*) match_highlight(strp, &rhp->atr);
+	rhp->layer = 10; /* default */
+	strp = (char*) match_highlight(strp, &rhp->atr, &rhp->layer);
 
 	while (inblank(*strp))
 	    strp++;
@@ -571,22 +592,6 @@ unset_region_highlight(Param pm, int exp)
 }
 
 
-/* The last attributes that were on. */
-static zattr lastatr;
-
-/*
- * Clear the last attributes that we set:  used when we're going
- * to be outputting stuff that shouldn't show up as text.
- */
-static void
-clearattributes(void)
-{
-    if (lastatr) {
-	settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr));
-	lastatr = 0;
-    }
-}
-
 /*
  * Output a termcap capability, clearing any text attributes so
  * as not to mess up the display.
@@ -595,7 +600,7 @@ clearattributes(void)
 static void
 tcoutclear(int cap)
 {
-    clearattributes();
+    cleartextattributes(0);
     tcout(cap);
 }
 
@@ -603,47 +608,20 @@ tcoutclear(int cap)
  * Output the character.  This must come from the new video
  * buffer, nbuf, since we access the multiword buffer nmwbuf
  * directly.
- *
- * curatrp may be NULL, otherwise points to an integer specifying
- * what attributes were turned on for a character output immediately
- * before, in order to optimise output of attribute changes.
  */
 
 /**/
 void
-zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp)
+zwcputc(const REFRESH_ELEMENT *c)
 {
-    /*
-     * Safety: turn attributes off if last heard of turned on.
-     * This differs from *curatrp, which is an optimisation for
-     * writing lots of stuff at once.
-     */
 #ifdef MULTIBYTE_SUPPORT
     mbstate_t mbstate;
     int i;
     VARARR(char, mbtmp, MB_CUR_MAX + 1);
 #endif
 
-    if (lastatr & ~c->atr) {
-	/* Stuff on we don't want, turn it off */
-	settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr));
-	lastatr = 0;
-    }
-
-    /*
-     * Don't output "on" attributes in a string of characters with
-     * the same attributes.  Be careful in case a different colour
-     * needs setting.
-     */
-    if ((c->atr & TXT_ATTR_ON_MASK) &&
-	(!curatrp ||
-	 ((*curatrp & TXT_ATTR_ON_VALUES_MASK) !=
-	  (c->atr & TXT_ATTR_ON_VALUES_MASK)))) {
-	/* Record just the control flags we might need to turn off... */
-	lastatr = c->atr & TXT_ATTR_ON_MASK;
-	/* ...but set including the values for colour attributes */
-	settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK);
-    }
+    treplaceattrs(c->atr);
+    applytextattributes(0);
 
 #ifdef MULTIBYTE_SUPPORT
     if (c->atr & TXT_MULTIWORD_MASK) {
@@ -664,35 +642,15 @@ zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp)
 #else
     fputc(c->chr, shout);
 #endif
-
-    /*
-     * Always output "off" attributes since we only turn off at
-     * the end of a chunk of highlighted text.
-     */
-    if (c->atr & TXT_ATTR_OFF_MASK) {
-	settextattributes(c->atr & TXT_ATTR_OFF_MASK);
-	lastatr &= ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
-    }
-    if (curatrp) {
-	/*
-	 * Remember the current attributes:  those that are turned
-	 * on, less those that are turned off again.  Include
-	 * colour attributes here in case the colour changes to
-	 * another non-default one.
-	 */
-	*curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) &
-	    ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
-    }
 }
 
 static int
 zwcwrite(const REFRESH_STRING s, size_t i)
 {
     size_t j;
-    zattr curatr = 0;
 
     for (j = 0; j < i; j++)
-	zwcputc(s + j, &curatr);
+	zwcputc(s + j);
     return i; /* TODO something better for error indication */
 }
 
@@ -939,29 +897,6 @@ snextline(Rparams rpms)
     rpms->sen = rpms->s + winw;
 }
 
-
-/**/
-static void
-settextattributes(zattr atr)
-{
-    if (txtchangeisset(atr, TXTNOBOLDFACE))
-	tsetcap(TCALLATTRSOFF, 0);
-    if (txtchangeisset(atr, TXTNOSTANDOUT))
-	tsetcap(TCSTANDOUTEND, 0);
-    if (txtchangeisset(atr, TXTNOUNDERLINE))
-	tsetcap(TCUNDERLINEEND, 0);
-    if (txtchangeisset(atr, TXTBOLDFACE))
-	tsetcap(TCBOLDFACEBEG, 0);
-    if (txtchangeisset(atr, TXTSTANDOUT))
-	tsetcap(TCSTANDOUTBEG, 0);
-    if (txtchangeisset(atr, TXTUNDERLINE))
-	tsetcap(TCUNDERLINEBEG, 0);
-    if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR))
-	set_colour_attribute(atr, COL_SEQ_FG, 0);
-    if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR))
-	set_colour_attribute(atr, COL_SEQ_BG, 0);
-}
-
 #ifdef MULTIBYTE_SUPPORT
 /*
  * Add a multiword glyph at the screen location base.
@@ -1043,7 +978,6 @@ zrefresh(void)
     int tmppos;			/* t - tmpline				     */
     int tmpalloced;		/* flag to free tmpline when finished	     */
     int remetafy;		/* flag that zle line is metafied	     */
-    zattr txtchange;		/* attributes set after prompts              */
     int rprompt_off = 1;	/* Offset of rprompt from right of screen    */
     struct rparams rpms;
 #ifdef MULTIBYTE_SUPPORT
@@ -1192,9 +1126,7 @@ zrefresh(void)
 #endif
 	/* we probably should only have explicitly set attributes */
 	tsetcap(TCALLATTRSOFF, 0);
-	tsetcap(TCSTANDOUTEND, 0);
-	tsetcap(TCUNDERLINEEND, 0);
-	txtattrmask = 0;
+	txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
 
 	if (trashedzle && !clearflag)
 	    reexpandprompt(); 
@@ -1218,9 +1150,8 @@ zrefresh(void)
 	    zputs(lpromptbuf, shout);
 	    if (lpromptwof == winw)
 		zputs("\n", shout);	/* works with both hasam and !hasam */
-	} else {
-	    txtchange = pmpt_attr;
-	    settextattributes(txtchange);
+	    /* lpromptbuf includes literal escapes so we need to update for it */
+	    txtcurrentattrs = txtpendingattrs = pmpt_attr;
 	}
 	if (clearflag) {
 	    zputc(&zr_cr);
@@ -1263,45 +1194,40 @@ zrefresh(void)
     rpms.s = nbuf[rpms.ln = 0] + lpromptw;
     rpms.sen = *nbuf + winw;
     for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
-	unsigned ireg;
-	zattr base_atr_on = default_atr_on, base_atr_off = 0;
-	zattr all_atr_on, all_atr_off;
+	zattr base_attr = mixattrs(default_attr, prompt_attr);
+	zattr all_attr;
 	struct region_highlight *rhp;
+	int layer, nextlayer = 0;
 	/*
 	 * Calculate attribute based on region.
 	 */
-	for (ireg = 0, rhp = region_highlights;
-	     ireg < n_region_highlights;
-	     ireg++, rhp++) {
-	    int offset;
-	    if (rhp->flags & ZRH_PREDISPLAY)
-		offset = 0;	/* include predisplay in start end */
-	    else
-		offset = predisplaylen; /* increment over it */
-	    if (rhp->start + offset <= tmppos &&
-		tmppos < rhp->end + offset) {
-		if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
-		    /* override colour with later entry */
-		    base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) |
-			rhp->atr;
-		} else {
-		    /* no colour set yet */
-		    base_atr_on |= rhp->atr;
+	do {
+	    unsigned ireg;
+	    layer = nextlayer;
+	    nextlayer = special_layer;
+	    for (ireg = 0, rhp = region_highlights;
+		ireg < n_region_highlights;
+		ireg++, rhp++) {
+		if (rhp->layer == layer) {
+		    int offset;
+		    if (rhp->flags & ZRH_PREDISPLAY)
+			offset = 0;	/* include predisplay in start end */
+		    else
+			offset = predisplaylen; /* increment over it */
+		    if (rhp->start + offset <= tmppos &&
+			tmppos < rhp->end + offset) {
+			base_attr = mixattrs(rhp->atr, base_attr);
+			if (layer > special_layer)
+			    all_attr = mixattrs(rhp->atr, all_attr);
+		    }
+		} else if (rhp->layer > layer && rhp->layer < nextlayer) {
+		    nextlayer = rhp->layer;
 		}
-		if (tmppos == rhp->end + offset - 1 ||
-		    tmppos == tmpll - 1)
-		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
 	    }
-	}
-	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
-	    /* keep colours from special attributes */
-	    all_atr_on = special_atr_on |
-		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
-	} else {
-	    /* keep colours from standard attributes */
-	    all_atr_on = special_atr_on | base_atr_on;
-	}
-	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+	    if (special_layer == layer) {
+		all_attr = mixattrs(special_attr, base_attr);
+	    }
+	} while (nextlayer > layer);
 
 	if (t == scs)			/* if cursor is here, remember it */
 	    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
@@ -1319,10 +1245,9 @@ zrefresh(void)
 	    } else {
 		do {
 		    rpms.s->chr = ZWC(' ');
-		    rpms.s->atr = base_atr_on;
+		    rpms.s->atr = base_attr;
 		    rpms.s++;
 		} while ((++t0) & 7);
-		rpms.s[-1].atr |= base_atr_off;
 	    }
 	}
 #ifdef MULTIBYTE_SUPPORT
@@ -1341,11 +1266,9 @@ zrefresh(void)
 		    rpms.s->chr = ZWC(' ');
 		    if (!started)
 			started = 1;
-		    rpms.s->atr = all_atr_on;
+		    rpms.s->atr = all_attr;
 		    rpms.s++;
 		} while (rpms.s < rpms.sen);
-		if (started)
-		    rpms.s[-1].atr |= all_atr_off;
 		if (nextline(&rpms, 1))
 		    break;
 		if (t == scs) {
@@ -1369,15 +1292,11 @@ zrefresh(void)
 		 * occurrence.
 		 */
 		rpms.s->chr = ZWC('?');
-		rpms.s->atr = all_atr_on | all_atr_off;
+		rpms.s->atr = all_attr;
 		rpms.s++;
 	    } else {
 		/* We can fit it without reaching the end of the line. */
-		/*
-		 * As we don't actually output the WEOF, we attach
-		 * any off attributes to the character itself.
-		 */
-		rpms.s->atr = base_atr_on | base_atr_off;
+		rpms.s->atr = base_attr;
 		if (ichars > 1) {
 		    /*
 		     * Glyph includes combining characters.
@@ -1393,7 +1312,7 @@ zrefresh(void)
 		while (--width > 0) {
 		    rpms.s->chr = WEOF;
 		    /* Not used, but be consistent... */
-		    rpms.s->atr = base_atr_on | base_atr_off;
+		    rpms.s->atr = base_attr;
 		    rpms.s++;
 		}
 	    }
@@ -1410,17 +1329,16 @@ zrefresh(void)
 #endif
 	    ) {	/* other control character */
 	    rpms.s->chr = ZWC('^');
-	    rpms.s->atr = all_atr_on;
+	    rpms.s->atr = all_attr;
 	    rpms.s++;
 	    if (rpms.s == rpms.sen) {
 		/* text wrapped */
-		rpms.s[-1].atr |= all_atr_off;
 		if (nextline(&rpms, 1))
 		    break;
 	    }
 	    rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ?
 		ZWC('?') : (*t | ZWC('@'));
-	    rpms.s->atr = all_atr_on | all_atr_off;
+	    rpms.s->atr = all_attr;
 	    rpms.s++;
 	}
 #ifdef MULTIBYTE_SUPPORT
@@ -1432,7 +1350,6 @@ zrefresh(void)
 	    char dispchars[11];
 	    char *dispptr = dispchars;
 	    wchar_t wc;
-	    int started = 0;
 
 #ifdef __STDC_ISO_10646__
 	    if (ZSH_INVALID_WCHAR_TEST(*t)) {
@@ -1449,31 +1366,23 @@ zrefresh(void)
 		if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */)
 		{
 		    rpms.s->chr = wc;
-		    if (!started)
-			started = 1;
-		    rpms.s->atr = all_atr_on;
+		    rpms.s->atr = all_attr;
 		    rpms.s++;
 		    if (rpms.s == rpms.sen) {
 			/* text wrapped */
-			if (started) {
-			    rpms.s[-1].atr |= all_atr_off;
-			    started = 0;
-			}
 			if (nextline(&rpms, 1))
 			    break;
 		    }
 		}
 		dispptr++;
 	    }
-	    if (started)
-		rpms.s[-1].atr |= all_atr_off;
 	    if (*dispptr) /* nextline said stop processing */
 		break;
 	}
 #else
 	else {			/* normal character */
 	    rpms.s->chr = *t;
-	    rpms.s->atr = base_atr_on | base_atr_off;
+	    rpms.s->atr = base_attr;
 	    rpms.s++;
 	}
 #endif
@@ -1499,13 +1408,12 @@ zrefresh(void)
 
     if (statusline) {
 	int outll, outsz;
-	zattr all_atr_on, all_atr_off;
+	zattr all_attr;
 	char *statusdup = ztrdup(statusline);
 	ZLE_STRING_T outputline =
 	    stringaszleline(statusdup, 0, &outll, &outsz, NULL); 
 
-	all_atr_on = special_atr_on;
-	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+	all_attr = special_attr;
 
 	rpms.tosln = rpms.ln + 1;
 	nbuf[rpms.ln][winw + 1] = zr_zr;	/* text not wrapped */
@@ -1525,7 +1433,7 @@ zrefresh(void)
 		}
 		if (width > rpms.sen - rpms.s) {
 		    rpms.s->chr = ZWC('?');
-		    rpms.s->atr = all_atr_on | all_atr_off;
+		    rpms.s->atr = all_attr;
 		    rpms.s++;
 		} else {
 		    rpms.s->chr = *u;
@@ -1542,7 +1450,7 @@ zrefresh(void)
 #endif
 	    if (ZC_icntrl(*u)) { /* simplified processing in the status line */
 		rpms.s->chr = ZWC('^');
-		rpms.s->atr = all_atr_on;
+		rpms.s->atr = all_attr;
 		rpms.s++;
 		if (rpms.s == rpms.sen) {
 		    nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */
@@ -1550,7 +1458,7 @@ zrefresh(void)
 		}
 		rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31)
 		    ? ZWC('?') : (*u | ZWC('@'));
-		rpms.s->atr = all_atr_on | all_atr_off;
+		rpms.s->atr = all_attr;
 		rpms.s++;
 	    } else {
 		rpms.s->chr = *u;
@@ -1603,6 +1511,7 @@ zrefresh(void)
 	}
 #endif
 	ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE);
+	rpms.sen[1].atr = ellipsis_attr;
 #ifdef MULTIBYTE_SUPPORT
 	/* Extend to the end if we backed off for a wide character */
 	if (extra_ellipsis) {
@@ -1638,6 +1547,7 @@ zrefresh(void)
 	}
 #endif
 	ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE);
+	rpms.sen[1].atr = ellipsis_attr;
 	rpms.sen += ZR_MID_ELLIPSIS1_SIZE;
 #ifdef MULTIBYTE_SUPPORT
 	/* Extend if we backed off for a wide character */
@@ -1647,6 +1557,7 @@ zrefresh(void)
 	}
 #endif
 	ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE);
+	rpms.sen[1].atr = prompt_attr;
 	nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr;
     }
 
@@ -1679,7 +1590,9 @@ zrefresh(void)
 	t0 = winw - lpromptw;
 	t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0;
 	ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0);
+	(*nbuf + lpromptw)->atr = ellipsis_attr;
 	ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw);
+	(*nbuf + lpromptw + t0)->atr = prompt_attr;
 	nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr;
     }
 
@@ -1725,9 +1638,9 @@ zrefresh(void)
 
     /* output the right-prompt if appropriate */
 	if (put_rpmpt && !iln && !oput_rpmpt) {
-	    zattr attrchange;
-
 	    moveto(0, winw - rprompt_off - rpromptw);
+	    treplaceattrs(pmpt_attr);
+	    applytextattributes(0);
 	    zputs(rpromptbuf, shout);
 	    if (rprompt_off) {
 		vcs = winw - rprompt_off;
@@ -1735,39 +1648,7 @@ zrefresh(void)
 		zputc(&zr_cr);
 		vcs = 0;
 	    }
-	/* reset character attributes to that set by the main prompt */
-	    txtchange = pmpt_attr;
-	    /*
-	     * Keep attributes that have actually changed,
-	     * which are ones off in rpmpt_attr and on in
-	     * pmpt_attr, and vice versa.
-	     */
-	    attrchange = txtchange &
-		(TXT_ATTR_OFF_FROM_ON(rpmpt_attr) |
-		 TXT_ATTR_ON_FROM_OFF(rpmpt_attr));
-	    /*
-	     * Careful in case the colour changed.
-	     */
-	    if (txtchangeisset(txtchange, TXTFGCOLOUR) &&
-		(!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) ||
-		 ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK)))
-	    {
-		attrchange |=
-		    txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK);
-	    }
-	    if (txtchangeisset(txtchange, TXTBGCOLOUR) &&
-		(!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) ||
-		 ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK)))
-	    {
-		attrchange |=
-		    txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK);
-	    }
-	    /*
-	     * Now feed these changes into the usual function,
-	     * if necessary.
-	     */
-	    if (attrchange)
-		settextattributes(attrchange);
+	    txtcurrentattrs = txtpendingattrs = rpmpt_attr;
 	}
     }
 
@@ -1780,11 +1661,6 @@ individually */
 	    refreshline(iln);
     }
 
-/* reset character attributes */
-    if (clearf && postedit) {
-	if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr))
-	    settextattributes(txtchange);
-    }
     clearf = 0;
     oput_rpmpt = put_rpmpt;
 
@@ -1984,8 +1860,6 @@ refreshline(int ln)
 /* 3: main display loop - write out the buffer using whatever tricks we can */
 
     for (;;) {
-	zattr now_off;
-
 #ifdef MULTIBYTE_SUPPORT
 	if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) {
 #endif
@@ -2087,7 +1961,7 @@ refreshline(int ln)
 			     * deletions, so turn off text attributes.
 			     */
 			    if (first) {
-				clearattributes();
+				cleartextattributes(0);
 				first = 0;
 			    }
 			    tc_delchars(i);
@@ -2176,13 +2050,8 @@ refreshline(int ln)
 	    break;
 	do {
 #endif
-	    /*
-	     * If an attribute was on here but isn't any more,
-	     * output the sequence to turn it off.
-	     */
-	    now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK;
-	    if (now_off)
-		settextattributes(TXT_ATTR_OFF_FROM_ON(now_off));
+	    treplaceattrs(nl->atr);
+	    applytextattributes(0);
 
 	    /*
 	     * This is deliberately called if nl->chr is WEOF
@@ -2560,8 +2429,8 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 
     for (t0 = 0; t0 < tmpll; t0++) {
 	unsigned ireg;
-	zattr base_atr_on = 0, base_atr_off = 0;
-	zattr all_atr_on, all_atr_off;
+	zattr base_attr = 0;
+	zattr all_attr;
 	struct region_highlight *rhp;
 	/*
 	 * Calculate attribute based on region.
@@ -2576,38 +2445,28 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 		offset = predisplaylen; /* increment over it */
 	    if (rhp->start + offset <= t0 &&
 		t0 < rhp->end + offset) {
-		if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
+		if (base_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) {
 		    /* keep colour already set */
-		    base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
+		    base_attr |= rhp->atr & ~TXT_ATTR_COLOUR_MASK;
 		} else {
 		    /* no colour set yet */
-		    base_atr_on |= rhp->atr;
+		    base_attr |= rhp->atr;
 		}
-		if (t0 == rhp->end + offset - 1 ||
-		    t0 == tmpll - 1)
-		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
 	    }
 	}
-	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
-	    /* keep colours from special attributes */
-	    all_atr_on = special_atr_on |
-		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
-	} else {
-	    /* keep colours from standard attributes */
-	    all_atr_on = special_atr_on | base_atr_on;
-	}
-	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+	all_attr = mixattrs(special_attr, base_attr);
 
+	if (t0 == tmpcs)
+	    nvcs = vp - vbuf;
 	if (tmpline[t0] == ZWC('\t')) {
 	    for (*vp++ = zr_sp; (vp - vbuf) & 7; )
 		*vp++ = zr_sp;
-	    vp[-1].atr |= base_atr_off;
 	} else if (tmpline[t0] == ZWC('\n')) {
 	    vp->chr = ZWC('\\');
-	    vp->atr = all_atr_on;
+	    vp->atr = all_attr;
 	    vp++;
 	    vp->chr = ZWC('n');
-	    vp->atr = all_atr_on | all_atr_off;
+	    vp->atr = all_attr;
 	    vp++;
 #ifdef MULTIBYTE_SUPPORT
 	} else if (WC_ISPRINT(tmpline[t0]) &&
@@ -2623,7 +2482,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 		}
 	    } else
 		ichars = 1;
-	    vp->atr = base_atr_on | base_atr_off;
+	    vp->atr = base_attr;
 	    if (ichars > 1)
 		addmultiword(vp, tmpline+t0, ichars);
 	    else
@@ -2631,7 +2490,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	    vp++;
 	    while (--width > 0) {
 		vp->chr = WEOF;
-		vp->atr = base_atr_on | base_atr_off;
+		vp->atr = base_attr;
 		vp++;
 	    }
 	    t0 += ichars - 1;
@@ -2641,14 +2500,14 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 		   && (unsigned)tmpline[t0] <= 0xffU
 #endif
 		   ) {
-	    ZLE_INT_T t = tmpline[++t0];
+	    ZLE_INT_T t = tmpline[t0];
 
 	    vp->chr = ZWC('^');
-	    vp->atr = all_atr_on;
+	    vp->atr = all_attr;
 	    vp++;
 	    vp->chr = (((unsigned int)t & ~0x80u) > 31) ?
 		ZWC('?') : (t | ZWC('@'));
-	    vp->atr = all_atr_on | all_atr_off;
+	    vp->atr = all_attr;
 	    vp++;
 	}
 #ifdef MULTIBYTE_SUPPORT
@@ -2656,7 +2515,6 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	    char dispchars[11];
 	    char *dispptr = dispchars;
 	    wchar_t wc;
-	    int started = 0;
 
 	    if ((unsigned)tmpline[t0] > 0xffffU) {
 		sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]);
@@ -2666,25 +2524,19 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	    while (*dispptr) {
 		if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) {
 		    vp->chr = wc;
-		    if (!started)
-			started = 1;
-		    vp->atr = all_atr_on;
+		    vp->atr = all_attr;
 		    vp++;
 		}
 		dispptr++;
 	    }
-	    if (started)
-		vp[-1].atr |= all_atr_off;
 	}
 #else
 	else {
 	    vp->chr = tmpline[t0];
-	    vp->atr = base_atr_on | base_atr_off;
+	    vp->atr = base_attr;
 	    vp++;
 	}
 #endif
-	if (t0 == tmpcs)
-	    nvcs = vp - vbuf - 1;
     }
     if (t0 == tmpcs)
 	nvcs = vp - vbuf;
@@ -2699,11 +2551,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
     }
     if (winpos) {
 	vbuf[winpos].chr = ZWC('<');	/* line continues to the left */
-	vbuf[winpos].atr = 0;
+	vbuf[winpos].atr = ellipsis_attr;
     }
     if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) {
 	vbuf[winpos + winw - hasam - 1].chr = ZWC('>');	/* line continues to right */
-	vbuf[winpos + winw - hasam - 1].atr = 0;
+	vbuf[winpos + winw - hasam - 1].atr = ellipsis_attr;
 	vbuf[winpos + winw - hasam] = zr_zr;
     }
     ZR_strcpy(nbuf[0], vbuf + winpos);
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index cd3f2c356..f71435b01 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -344,7 +344,7 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func))
 {
     static struct opn {
 	char o;
-	int (*func) _((char *, char **, Options, char));
+	int (*func) (char *, char **, Options, char);
 	int min, max;
     } const opns[] = {
 	{ 'l', bin_zle_list, 0, -1 },
@@ -366,10 +366,10 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func))
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
+    for(op = opns; op->o && !OPT_ISSET(ops, (unsigned char) op->o); op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(OPT_ISSET(ops,STOUC(opp->o))) {
+	    if(OPT_ISSET(ops, (unsigned char) opp->o)) {
 		zwarnnam(name, "incompatible operation selection options");
 		return 1;
 	    }
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index fdd168763..aa3c71bc2 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -576,7 +576,7 @@ parambeg(char *s)
 	    while (idigit(*e))
 		e++;
 	else
-	    e = itype_end(e, IIDENT, 0);
+	    e = itype_end(e, INAMESPC, 0);
 
 	/* Now make sure that the cursor is inside the name. */
 	if (offs <= e - s && offs >= b - s && n <= 0) {
@@ -765,7 +765,7 @@ docomplete(int lst)
 			    else if (idigit(*q))
 				do q++; while (idigit(*q));
 			    else
-				q = itype_end(q, IIDENT, 0);
+				q = itype_end(q, INAMESPC, 0);
 			    sav = *q;
 			    *q = '\0';
 			    if (zlemetacs - wb == q - s &&
@@ -1315,6 +1315,8 @@ get_comp_string(void)
 	    ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET));
 	    zsfree(cmdstr);
 	    cmdstr = ztrdup(tokstr);
+	    untokenize(cmdstr);
+	    remnulargs(cmdstr);
 	    cmdtok = tok;
 	    /*
 	     * If everything before is a redirection, or anything
@@ -1497,7 +1499,8 @@ get_comp_string(void)
 	if (varq)
 	    tt = clwords[clwpos];
 
-	s = itype_end(tt, IIDENT, 0);
+	/* The only place we complete namespaces, see IIDENT below */
+	s = itype_end(tt, INAMESPC, 0);
 	sav = *s;
 	*s = '\0';
 	zsfree(varname);
@@ -1568,6 +1571,8 @@ get_comp_string(void)
 
 	i = 0;
 	MB_METACHARINIT();
+	/* All further uses of IIDENT in this file should change to   *
+         * INAMESPC if this case is changed.  Too ugly to risk now.   */
 	if (itype_end(s, IIDENT, 1) == s)
 	    nnb = s + MB_METACHARLEN(s);
 	else
@@ -1641,7 +1646,7 @@ get_comp_string(void)
 	} else {
 	    /* In mathematical expression, we complete parameter names  *
 	     * (even if they don't have a `$' in front of them).  So we *
-	     * have to find that name.                                  */
+	     * have to find that name.  See above regarding INAMESPC    */
 	    char *cspos = zlemetaline + zlemetacs, *wptr, *cptr;
 	    we = itype_end(cspos, IIDENT, 0) - zlemetaline;
 
@@ -2426,7 +2431,7 @@ mod_export int
 printfmt(char *fmt, int n, int dopr, int doesc)
 {
     char *p = fmt, nc[DIGBUFSIZE];
-    int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
+    int l = 0, cc = 0;
 
     MB_METACHARINIT();
     for (; *p; ) {
@@ -2437,48 +2442,45 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 	    if (idigit(*++p))
 		arg = zstrtol(p, &p, 10);
 	    if (*p) {
-		m = 0;
 		switch (*p) {
 		case '%':
-		    if (dopr)
+		    if (dopr) {
+			applytextattributes(0);
 			putc('%', shout);
+		    }
 		    cc++;
 		    break;
 		case 'n':
 		    sprintf(nc, "%d", n);
-		    if (dopr)
+		    if (dopr) {
+			applytextattributes(0);
 			fputs(nc, shout);
+		    }
 		    cc += MB_METASTRWIDTH(nc);
 		    break;
 		case 'B':
-		    b = 1;
 		    if (dopr)
-			tcout(TCBOLDFACEBEG);
+			tsetattrs(TXTBOLDFACE);
 		    break;
 		case 'b':
-		    b = 0; m = 1;
 		    if (dopr)
-			tcout(TCALLATTRSOFF);
+			tunsetattrs(TXTBOLDFACE);
 		    break;
 		case 'S':
-		    s = 1;
 		    if (dopr)
-			tcout(TCSTANDOUTBEG);
+			tsetattrs(TXTSTANDOUT);
 		    break;
 		case 's':
-		    s = 0; m = 1;
 		    if (dopr)
-			tcout(TCSTANDOUTEND);
+			tunsetattrs(TXTSTANDOUT);
 		    break;
 		case 'U':
-		    u = 1;
 		    if (dopr)
-			tcout(TCUNDERLINEBEG);
+			tsetattrs(TXTUNDERLINE);
 		    break;
 		case 'u':
-		    u = 0; m = 1;
 		    if (dopr)
-			tcout(TCUNDERLINEEND);
+			tunsetattrs(TXTUNDERLINE);
 		    break;
 		case 'F':
 		case 'K':
@@ -2491,18 +2493,27 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 		    } else
 			atr = match_colour(NULL, is_fg, arg);
 		    if (atr != TXT_ERROR)
-			set_colour_attribute(atr, is_fg ? COL_SEQ_FG :
-					     COL_SEQ_BG, 0);
+			tsetattrs(atr);
 		    break;
 		case 'f':
-		    set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0);
+		    tunsetattrs(TXTFGCOLOUR);
 		    break;
 		case 'k':
-		    set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0);
+		    tunsetattrs(TXTBGCOLOUR);
+		    break;
+		case 'H':
+		    if (p[1] == '{') {
+			p = parsehighlight(p + 2, '}', &atr);
+			--p;
+			if (atr != TXT_ERROR)
+			    treplaceattrs(atr);
+		    }
 		    break;
 		case '{':
 		    if (arg)
 			cc += arg;
+		    if (dopr)
+			applytextattributes(0);
 		    for (p++; *p && (*p != '%' || p[1] != '}'); p++) {
 			if (*p == Meta) {
 			    p++;
@@ -2518,14 +2529,6 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 			p--;
 		    break;
 		}
-		if (dopr && m) {
-		    if (b)
-			tcout(TCBOLDFACEBEG);
-		    if (s)
-			tcout(TCSTANDOUTBEG);
-		    if (u)
-			tcout(TCUNDERLINEBEG);
-		}
 	    } else
 		break;
 	    p++;
@@ -2533,6 +2536,7 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 	    if (*p == '\n') {
 		cc++;
 		if (dopr) {
+		    applytextattributes(0);
 		    if (tccan(TCCLEAREOL))
 			tcout(TCCLEAREOL);
 		    else {
@@ -2551,6 +2555,7 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 		convchar_t cchar;
 		int clen = MB_METACHARLENCONV(p, &cchar);
 		if (dopr) {
+		    applytextattributes(0);
 		    while (clen--) {
 			if (*p == Meta) {
 			    p++;
@@ -2568,6 +2573,8 @@ printfmt(char *fmt, int n, int dopr, int doesc)
 	}
     }
     if (dopr) {
+	treplaceattrs(0);
+	applytextattributes(0);
         if (!(cc % zterm_columns))
             fputs(" \010", shout);
 	if (tccan(TCCLEAREOL))
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 526216fa7..e2b86e863 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -512,12 +512,13 @@ stringaszleline(char *instr, int incs, int *outll, int *outsz, int *outcs)
 	    *outcs = outptr - outstr;
 	*outll = outptr - outstr;
     } else {
+	*outstr = ZWC('\0');
 	*outll = 0;
 	if (outcs)
 	    *outcs = 0;
     }
 #else
-    memcpy(outstr, instr, ll);
+    strcpy(outstr, instr);
     *outll = ll;
     if (outcs)
 	*outcs = incs;
@@ -580,7 +581,7 @@ struct zle_region;
 struct zle_region  {
     struct zle_region *next;
     /* Entries of region_highlight, as needed */
-    int atr;
+    zattr atr;
     int start;
     int end;
     int flags;
@@ -799,7 +800,7 @@ spaceinline(int ct)
 		if (rhp->start_meta - sub >= zlemetacs) {
 		    rhp->start_meta += ct;
 		}
-		if (rhp->end_meta - sub >= zlemetacs) {
+		if (rhp->end_meta - sub >= zlemetacs && (!predisplaylen || zlemetacs)) {
 		    rhp->end_meta += ct;
 		}
 	    }
@@ -827,7 +828,7 @@ spaceinline(int ct)
 		if (rhp->start - sub >= zlecs) {
 		    rhp->start += ct;
 		}
-		if (rhp->end - sub >= zlecs) {
+		if (rhp->end - sub >= zlecs && (!predisplaylen || zlecs)) {
 		    rhp->end += ct;
 		}
 	    }
@@ -866,13 +867,13 @@ shiftchars(int to, int cnt)
 		    if (rhp->start_meta - sub > to + cnt)
 			rhp->start_meta -= cnt;
 		    else
-			rhp->start_meta = to;
+			rhp->start_meta = to + sub;
 		}
 		if (rhp->end_meta - sub > to) {
 		    if (rhp->end_meta - sub > to + cnt)
 			rhp->end_meta -= cnt;
 		    else
-			rhp->end_meta = to;
+			rhp->end_meta = to + sub;
 		}
 	    }
 	}
@@ -896,13 +897,13 @@ shiftchars(int to, int cnt)
 		    if (rhp->start - sub > to + cnt)
 			rhp->start -= cnt;
 		    else
-			rhp->start = to;
+			rhp->start = to + sub;
 		}
 		if (rhp->end - sub > to) {
 		    if (rhp->end - sub > to + cnt)
 			rhp->end -= cnt;
 		    else
-			rhp->end = to;
+			rhp->end = to + sub;
 		}
 	    }
 	}
@@ -936,6 +937,28 @@ cut(int i, int ct, int flags)
   cuttext(zleline + i, ct, flags);
 }
 
+static char*
+base64_encode(const char *src, size_t len) {
+    static const char* base64_table =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    const unsigned char *end = (unsigned char *)src + len;
+    const unsigned char *in = (unsigned char *)src;
+    char *ret = zhalloc(1 + 4 * ((len + 2) / 3)); /* 4 bytes out for 3 in */
+    char *cur = ret;
+
+    for (; end - in > 0; in += 3, cur += 4) {
+        unsigned int n = *in << 16;
+        cur[3] = end - in > 2 ? base64_table[(n |= in[2]) & 0x3f] : '=';
+        cur[2] = end - in > 1 ? base64_table[((n |= in[1]<<8) >> 6) & 0x3f] : '=';
+        cur[1] = base64_table[(n >> 12) & 0x3f];
+        cur[0] = base64_table[n >> 18];
+    }
+    *cur = '\0';
+
+    return ret;
+}
+
 /*
  * As cut, but explicitly supply the text together with its length.
  */
@@ -948,7 +971,15 @@ cuttext(ZLE_STRING_T line, int ct, int flags)
 	return;
 
     UNMETACHECK();
-    if (zmod.flags & MOD_VIBUF) {
+    if (zmod.flags & MOD_OSSEL) {
+	int cutll;
+	char *mbcut = zlelineasstring(line, ct, 0, &cutll, NULL, 1);
+	unmetafy(mbcut, &cutll);
+	mbcut = base64_encode(mbcut, cutll);
+
+	fprintf(shout, "\033]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p',
+		mbcut);
+    } else if (zmod.flags & MOD_VIBUF) {
 	struct cutbuffer *b = &vibuf[zmod.vibuf];
 
 	if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
@@ -1220,7 +1251,7 @@ getzlequery(void)
 	REFRESH_ELEMENT re;
 	re.chr = c;
 	re.atr = 0;
-	zwcputc(&re, NULL);
+	zwcputc(&re);
     }
     return c == ZWC('y');
 }
@@ -1235,7 +1266,7 @@ bindztrdup(char *str)
     char *buf, *ptr, *ret;
 
     for(ptr = str; *ptr; ptr++) {
-	c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr);
+	c = *ptr == Meta ? (unsigned char) *++ptr ^ 32 : (unsigned char) *ptr;
 	if(c & 0x80) {
 	    len += 3;
 	    c &= 0x7f;
@@ -1249,7 +1280,7 @@ bindztrdup(char *str)
     }
     ptr = buf = zalloc(len);
     for(; *str; str++) {
-	c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str);
+	c = *str == Meta ? (unsigned char) *++str ^ 32 : (unsigned char) *str;
 	if(c & 0x80) {
 	    *ptr++ = '\\';
 	    *ptr++ = 'M';
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 0f198d0e8..6692df830 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -1014,6 +1014,9 @@ int
 visetbuffer(char **args)
 {
     ZLE_INT_T ch;
+    const ZLE_CHAR_T *match = ZWS("_*+");
+    int registermod[] = { MOD_NULL, MOD_PRI, MOD_CLIP };
+    ZLE_CHAR_T *found;
 
     if (*args) {
 	ch = **args;
@@ -1022,12 +1025,12 @@ visetbuffer(char **args)
     } else {
 	ch = getfullchar(0);
     }
-    if (ch == ZWC('_')) {
-	zmod.flags |= MOD_NULL;
+    if ((found = ZS_strchr(match, ch))) {
+	zmod.flags |= registermod[found - match];
 	prefixflag = 1;
 	return 0;
     } else
-	zmod.flags &= ~MOD_NULL;
+	zmod.flags &= ~(MOD_NULL | MOD_OSSEL);
     if ((ch < ZWC('0') || ch > ZWC('9')) &&
 	 (ch < ZWC('a') || ch > ZWC('z')) &&
 	 (ch < ZWC('A') || ch > ZWC('Z')))
diff --git a/Src/builtin.c b/Src/builtin.c
index a7b7755a7..7bfb1ce1d 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -55,7 +55,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
     BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL),
+    BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmnp:%rtuxz", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -88,7 +88,7 @@ static struct builtin builtins[] =
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL),
+    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lnp:%rtux", NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
 #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG)
@@ -121,12 +121,12 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, 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, "ampfsSw", "v"),
-    BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL),
+    BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmnz", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
     BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL),
-    BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL),
+    BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmvn", NULL),
     BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
     BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
     BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL),
@@ -2030,6 +2030,25 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0;
     char *subscript;
 
+    if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF) &&
+	(pm->level == locallevel || !(on & PM_LOCAL))) {
+	if ((pm = (Param)resolve_nameref(pm, NULL)))
+	    pname = pm->node.nam;
+	if (pm && (pm->node.flags & PM_NAMEREF) &&
+	    (on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) {
+	    /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error.  *
+	     * Should this be a fatal error as well, rather than warning? */
+	    if (pm->width)
+		zwarnnam(cname,
+			 "%s: can't change type via subscript reference",
+			 pm->u.str);
+	    else
+		zwarnnam(cname, "%s: can't change type of a named reference",
+			 pname);
+	    return NULL;
+	}
+    }
+
     /*
      * Do we use the existing pm?  Note that this isn't the end of the
      * story, because if we try and create a new pm at the same
@@ -2040,7 +2059,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
      * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export'
      * flags of an unset parameter.
      */
-    usepm = pm && (!(pm->node.flags & PM_UNSET) ||
+    usepm = pm && (!(pm->node.flags & PM_UNSET) || OPT_ISSET(ops, 'p') ||
 		   (isset(POSIXBUILTINS) &&
 		    (pm->node.flags & (PM_READONLY|PM_EXPORTED))));
 
@@ -2088,15 +2107,27 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	    return NULL;
 	}
 	tc = 1;
-	usepm = 0;
+	if (OPT_MINUS(ops,'p'))
+	    usepm = (on & pm->node.flags);
+	else if (OPT_PLUS(ops,'p'))
+	    usepm = (off & pm->node.flags);
+	else
+	    usepm = 0;
     }
     else if (usepm || newspecial != NS_NONE) {
 	int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
 	    (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
 	     PM_ARRAY|PM_TIED|PM_AUTOLOAD);
 	/* keep the parameter if just switching between floating types */
-	if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
-	    usepm = 0;
+	if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) {
+	    if (OPT_MINUS(ops,'p'))
+		usepm = (on & pm->node.flags);
+	    else if (OPT_PLUS(ops,'p'))
+		usepm = (off & pm->node.flags);
+	    else
+		usepm = 0;
+	}
+
     }
 
     /*
@@ -2148,20 +2179,23 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	    }
 	    if (err)
 	    {
-		zerrnam(cname, "%s: can't change type of a special parameter",
-			pname);
+		if (!OPT_ISSET(ops,'p'))
+		    zerrnam(cname,
+			    "%s: can't change type of a special parameter",
+			    pname);
 		return NULL;
 	    }
 	} else if (pm->node.flags & PM_AUTOLOAD) {
-	    zerrnam(cname, "%s: can't change type of autoloaded parameter",
-		    pname);
+	    if (!OPT_ISSET(ops,'p'))
+		zerrnam(cname, "%s: can't change type of autoloaded parameter",
+			pname);
 	    return NULL;
 	}
     }
     else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0)
 	newspecial = NS_SECONDS;
 
-    if (isset(POSIXBUILTINS)) {
+    if (isset(POSIXBUILTINS) && !OPT_ISSET(ops,'p')) {
 	/*
 	 * Stricter rules about retaining readonly attribute in this case.
 	 */
@@ -2190,6 +2224,11 @@ typeset_single(char *cname, char *pname, Param pm, int func,
      *   ii. we are creating a new local parameter
      */
     if (usepm) {
+	if (OPT_MINUS(ops,'p') && on &&
+	    !((on & pm->node.flags) || ((on & PM_LOCAL) && pm->level)))
+	    return NULL;
+	else if (OPT_PLUS(ops,'p') && off && !(off & pm->node.flags))
+	    return NULL;
 	if ((asg->flags & ASG_ARRAY) ?
 	    !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) :
 	    (asg->value.scalar && (PM_TYPE(pm->node.flags &
@@ -2199,17 +2238,27 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	}
 	on &= ~PM_LOCAL;
 	if (!on && !roff && !ASG_VALUEP(asg)) {
+	    int with_ns = OPT_ISSET(ops,'m') ? PRINT_WITH_NAMESPACE : 0;
 	    if (OPT_ISSET(ops,'p'))
-		paramtab->printnode(&pm->node, PRINT_TYPESET);
+		paramtab->printnode(&pm->node, PRINT_TYPESET|with_ns);
 	    else if (!OPT_ISSET(ops,'g') &&
 		     (unset(TYPESETSILENT) || OPT_ISSET(ops,'m')))
-		paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE);
+		paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE|with_ns);
 	    return pm;
 	}
 	if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
 	    zerrnam(cname, "%s: restricted", pname);
 	    return pm;
 	}
+	if ((pm->node.flags & PM_READONLY) && !(off & PM_READONLY) &&
+	    /* It seems as though these checks should not be specific to
+	     * PM_NAMEREF, but changing that changes historic behavior */
+	    ((on & PM_NAMEREF) != (pm->node.flags & PM_NAMEREF) ||
+	     (asg && (pm->node.flags & PM_NAMEREF)))) {
+	    zerrnam(cname, "%s: read-only %s", pname,
+		    (pm->node.flags & PM_NAMEREF) ? "reference" : "variable");
+	    return NULL;
+	}
 	if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) {
 	    Param apm;
 	    char **x;
@@ -2231,6 +2280,10 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 		    arrfixenv(pm->node.nam, x);
 	    }
 	}
+	if (OPT_ISSET(ops,'p')) {
+	    paramtab->printnode(&pm->node, PRINT_TYPESET|PRINT_WITH_NAMESPACE);
+	    return pm;
+	}
 	if (usepm == 2)		/* do not change the PM_UNSET flag */
 	    pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off;
 	else {
@@ -2286,10 +2339,9 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	if (errflag)
 	    return NULL;
 	pm->node.flags |= (on & PM_READONLY);
-	if (OPT_ISSET(ops,'p'))
-	    paramtab->printnode(&pm->node, PRINT_TYPESET);
 	return pm;
-    }
+    } else if (OPT_ISSET(ops,'p'))
+	return NULL;	/* Nothing to print */
 
     if ((asg->flags & ASG_ARRAY) ?
 	!(on & (PM_ARRAY|PM_HASHED)) :
@@ -2304,7 +2356,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
      * or we're converting the type of a parameter.  In the
      * last case only, we need to delete the old parameter.
      */
-    if (tc) {
+    if (tc && !OPT_ISSET(ops,'p')) {
 	/* Maintain existing readonly/exported status... */
 	on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags;
 	/* ...but turn off existing readonly so we can delete it */
@@ -2406,6 +2458,11 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 		return NULL;
 	}
     } else if ((subscript = strchr(pname, '['))) {
+	if (on & PM_NAMEREF) {
+	    zerrnam(cname,
+		    "%s: reference variable cannot be an array", pname);
+	    return NULL;
+	}
 	if (on & PM_READONLY) {
 	    zerrnam(cname,
 		    "%s: can't create readonly array elements", pname);
@@ -2452,6 +2509,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 		    "%s: inconsistent array element or slice assignment", pname);
 	    return NULL;
 	}
+    } else if (!pm && OPT_ISSET(ops,'p')) {
+	return NULL;
     }
     /*
      * As we can hide existing parameters, we allow a name if
@@ -2613,7 +2672,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
     char *optstr = TYPESET_OPTSTR;
     int on = 0, off = 0, roff, bit = PM_ARRAY;
     int i;
-    int returnval = 0, printflags = 0;
+    int returnval = 0, printflags = PRINT_WITH_NAMESPACE;
     int hasargs = *argv != NULL || (assigns && firstnode(assigns));
 
     /* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */
@@ -2629,17 +2688,34 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
     if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g'))
 	ops->ind['g'] = 1;
 
+#if 0
+    /* "local" rejects -m, this should too ... what about +m ? */
+    if (locallevel && OPT_MINUS(ops, 'm') &&
+	!(OPT_MINUS(ops, 'g') || OPT_ISSET(ops, 'p'))) {
+	zerrnam(name, "bad option: -m");
+	return 1;
+    }
+#endif
+
     /* Translate the options into PM_* flags.   *
      * Unfortunately, this depends on the order *
      * these flags are defined in zsh.h         */
     for (; *optstr; optstr++, bit <<= 1)
     {
-	int optval = STOUC(*optstr);
+	int optval = (unsigned char) *optstr;
 	if (OPT_MINUS(ops,optval))
 	    on |= bit;
 	else if (OPT_PLUS(ops,optval))
 	    off |= bit;
     }
+    if (OPT_MINUS(ops,'n')) {
+	if ((on|off) & ~(PM_READONLY|PM_UPPER)) {
+	    zwarnnam(name, "no other attributes allowed with -n");
+	    return 1;
+	}
+	on |= PM_NAMEREF;
+    } else if (OPT_PLUS(ops,'n'))
+	off |= PM_NAMEREF;
     roff = off;
 
     /* Sanity checks on the options.  Remove conflicting options. */
@@ -2673,7 +2749,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 
     queue_signals();
 
-    /* Given no arguments, list whatever the options specify. */
     if (OPT_ISSET(ops,'p')) {
 
 	if (isset(POSIXBUILTINS) && SHELL_EMULATION() != EMULATE_KSH) {
@@ -2699,8 +2774,14 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	    /* -p0 treated as -p for consistency */
 	}
     }
+
+    /* Given no arguments, list whatever the options specify. */
     if (!hasargs) {
 	int exclude = 0;
+
+	if (!OPT_ISSET(ops,'m'))
+	    printflags &= ~PRINT_WITH_NAMESPACE;
+	
 	if (!OPT_ISSET(ops,'p')) {
 	    if (!(on|roff))
 		printflags |= PRINT_TYPE;
@@ -2718,10 +2799,18 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	return 0;
     }
 
-    if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) ||
-	OPT_PLUS(ops,'g') || *name == 'l' ||
-	(!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g')))
-	on |= PM_LOCAL;
+    /* Using *name here is cheating, "local" allows no -g option */
+    if ((*name == 'l' || OPT_PLUS(ops,'g')))
+        on |= PM_LOCAL;
+    else if (!OPT_ISSET(ops,'g')) {
+        if (OPT_MINUS(ops, 'x')) {
+	    if (isset(GLOBALEXPORT))
+		ops->ind['g'] = 1;
+	    else if (locallevel)
+		on |= PM_LOCAL;
+        } else if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')))
+            on |= PM_LOCAL;
+    }
 
     if ((on & PM_TIED) && !OPT_ISSET(ops, 'p')) {
 	Param apm;
@@ -2956,6 +3045,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
     /* With the -m option, treat arguments as glob patterns */
     if (OPT_ISSET(ops,'m')) {
 	if (!OPT_ISSET(ops,'p')) {
+	    if (on & PM_NAMEREF) {
+		/* It's generally unwise to mass-change the types of
+		 * parameters, but for namerefs it would be fatal */
+		unqueue_signals();
+		zerrnam(name, "invalid reference");
+		return 1;
+	    }
 	    if (!(on|roff))
 		printflags |= PRINT_TYPE;
 	    if (!on)
@@ -3022,6 +3118,47 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	    }
 	    continue;
 	}
+
+	if (on & PM_NAMEREF) {
+	    if (asg->value.scalar &&
+		((pm = (Param)resolve_nameref((Param)hn, asg)) &&
+		 (pm->node.flags & PM_NAMEREF))) {
+		if (pm->node.flags & PM_SPECIAL) {
+		    zwarnnam(name, "%s: invalid reference", pm->node.nam);
+		    returnval = 1;
+		    continue;
+		} else if (pm->u.str && strcmp(pm->u.str, asg->name) == 0) {
+		    zwarnnam(name, "%s: invalid self reference", asg->name);
+		    returnval = 1;
+		    continue;
+		}
+	    }
+	    if (hn) {
+		/* namerefs always start over fresh */
+		if (((Param)hn)->level >= locallevel ||
+		    (!(on & PM_LOCAL) && ((Param)hn)->level < locallevel)) {
+		    Param oldpm = (Param)hn;
+		    if (!asg->value.scalar &&
+			PM_TYPE(oldpm->node.flags) == PM_SCALAR &&
+			oldpm->u.str)
+			asg->value.scalar = dupstring(oldpm->u.str);
+		    /* Defer read-only error to typeset_single() */
+		    if (!(hn->flags & PM_READONLY)) {
+			unsetparam_pm(oldpm, 0, 1);
+			hn = NULL;
+		    }
+		}
+		/* Passing a NULL pm to typeset_single() makes the
+		 * nameref read-only before assignment, which breaks
+		 *   typeset -rn ref=var
+		 * so this is special-cased to permit that action
+		 * like assign-at-create for other parameter types.
+		 */
+		if (hn && !(hn->flags & PM_READONLY))
+		    hn = NULL;
+	    }
+	}
+
 	if (!typeset_single(name, asg->name, (Param)hn,
 			    func, on, off, roff, asg, NULL,
 			    ops, 0))
@@ -3308,16 +3445,16 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    newsh->sticky = sticky_emulation_dup(shf->sticky, 0);
 	/* is newsh a signal trap? (adapted from exec.c) */
 	if (!strncmp(s, "TRAP", 4)) {
-	    int signum = getsignum(s + 4);
-	    if (signum != -1) {
-		if (settrap(signum, NULL, ZSIG_FUNC)) {
+	    int sigidx = getsigidx(s + 4);
+	    if (sigidx != -1) {
+		if (settrap(sigidx, NULL, ZSIG_FUNC)) {
 		    freeeprog(newsh->funcdef);
 		    dircache_set(&newsh->filename, NULL);
 		    zfree(newsh, sizeof(*newsh));
 		    return 1;
 		}
 		/* Remove any old node explicitly */
-		removetrapnode(signum);
+		removetrapnode(sigidx);
 	    }
 	}
 	shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node);
@@ -3596,15 +3733,15 @@ bin_functions(char *name, char **argv, Options ops, int func)
 		/* no flags, so just print */
 		printshfuncexpand(&shf->node, pflags, expand);
 	} else if (on & PM_UNDEFINED) {
-	    int signum = -1, ok = 1;
+	    int sigidx = -1, ok = 1;
 
 	    if (!strncmp(*argv, "TRAP", 4) &&
-		(signum = getsignum(*argv + 4)) != -1) {
+		(sigidx = getsigidx(*argv + 4)) != -1) {
 		/*
 		 * Because of the possibility of alternative names,
 		 * we must remove the trap explicitly.
 		 */
-		removetrapnode(signum);
+		removetrapnode(sigidx);
 	    }
 
 	    if (**argv == '/') {
@@ -3640,8 +3777,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    shfunc_set_sticky(shf);
 	    add_autoload_function(shf, *argv);
 
-	    if (signum != -1) {
-		if (settrap(signum, NULL, ZSIG_FUNC)) {
+	    if (sigidx != -1) {
+		if (settrap(sigidx, NULL, ZSIG_FUNC)) {
 		    shfunctab->removenode(shfunctab, *argv);
 		    shfunctab->freenode(&shf->node);
 		    returnval = 1;
@@ -3715,7 +3852,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
 			if ((!(pm->node.flags & PM_RESTRICTED) ||
 			     unset(RESTRICTED)) &&
 			    pattry(pprog, pm->node.nam)) {
-			    unsetparam_pm(pm, 0, 1);
+			    if (!OPT_ISSET(ops,'n') &&
+				(pm->node.flags & PM_NAMEREF) && pm->u.str)
+				unsetparam(pm->u.str);
+			    else
+				unsetparam_pm(pm, 0, 1);
 			    match++;
 			}
 		    }
@@ -3767,6 +3908,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
 	    zerrnam(name, "%s: restricted", pm->node.nam);
 	    returnval = 1;
 	} else if (ss) {
+	    if ((pm->node.flags & PM_NAMEREF) &&
+		(!(pm = (Param)resolve_nameref(pm, NULL)) || pm->width)) {
+		/* warning? */
+		continue;
+	    }
 	    if (PM_TYPE(pm->node.flags) == PM_HASHED) {
 		HashTable tht = paramtab;
 		if ((paramtab = pm->gsu.h->getfn(pm)))
@@ -3805,6 +3951,18 @@ bin_unset(char *name, char **argv, Options ops, int func)
 		returnval = 1;
 	    }
 	} else {
+	    if (!OPT_ISSET(ops,'n')) {
+		int ref = (pm->node.flags & PM_NAMEREF);
+		if (!(pm = (Param)resolve_nameref(pm, NULL)))
+		    continue;
+		if (ref && pm->level < locallevel &&
+		    !(pm->node.flags & PM_READONLY)) {
+		    /* Just mark unset, do not remove from table */
+		    stdunsetfn(pm, 0);
+		    pm->node.flags |= PM_DECLARED;
+		    continue;
+		}
+	    }
 	    if (unsetparam_pm(pm, 0, 1))
 		returnval = 1;
 	}
@@ -3941,6 +4099,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
     /* Take arguments literally -- do not glob */
     queue_signals();
     for (; *argv; argv++) {
+	informed = 0;
 	if (!OPT_ISSET(ops,'p') && !allmatched) {
 	    char *suf;
 
@@ -4603,6 +4762,8 @@ bin_print(char *name, char **args, Options ops, int func)
     /* compute lengths, and interpret according to -P, -D, -e, etc. */
     argc = arrlen(args);
     len = (int *) hcalloc(argc * sizeof(int));
+    if (OPT_ISSET(ops, 'P'))
+	txtunknownattrs = TXT_ATTR_ALL;
     for (n = 0; n < argc; n++) {
 	/* first \ sequences */
 	if (fmt ||
@@ -4633,7 +4794,7 @@ bin_print(char *name, char **args, Options ops, int func)
 	     */
 	    char *str = unmetafy(
 		promptexpand(metafy(args[n], len[n], META_NOALLOC),
-			     0, NULL, NULL, NULL),
+			     0, NULL, NULL),
 		&len[n]);
 	    args[n] = dupstrpfx(str, len[n]);
 	    free(str);
@@ -4752,7 +4913,7 @@ bin_print(char *name, char **args, Options ops, int func)
 		     */
 		    if (*aptr == '\033' || *aptr == '\233') {
 			for (aptr++, l--;
-			     l && !isalpha(STOUC(*aptr));
+			     l && !isalpha((unsigned char) (*aptr));
 			     aptr++, l--)
 			    ;
 			aptr++;
@@ -5114,7 +5275,8 @@ bin_print(char *name, char **args, Options ops, int func)
 		    }
 		}
 		if (*argp) {
-		    width = (int)mathevali(*argp++);
+		    width = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC));
+		    argp++;
 		    if (errflag) {
 			errflag &= ~ERRFLAG_ERROR;
 			ret = 1;
@@ -5148,7 +5310,8 @@ bin_print(char *name, char **args, Options ops, int func)
 		    }
 
 		    if (*argp) {
-			prec = (int)mathevali(*argp++);
+			prec = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC));
+			argp++;
 			if (errflag) {
 			    errflag &= ~ERRFLAG_ERROR;
 			    ret = 1;
@@ -5222,20 +5385,21 @@ bin_print(char *name, char **args, Options ops, int func)
 #ifdef MULTIBYTE_SUPPORT
 			if (isset(MULTIBYTE)) {
 			    chars = mbrlen(ptr, lleft, &mbs);
-			    if (chars < 0) {
-				/*
-				 * Invalid/incomplete character at this
-				 * point.  Assume all the rest are a
-				 * single byte.  That's about the best we
-				 * can do.
-				 */
-				lchars += lleft;
-				lbytes = (ptr - b) + lleft;
-				break;
-			    } else if (chars == 0) {
-				/* NUL, handle as real character */
+			    /*
+			     * chars <= 0 means one of
+			     *
+			     * 0: NUL, handle as real character
+			     *
+			     * -1: MB_INVALID: Assume this is
+			     *     a single character as we do
+			     *     elsewhere in the code.
+			     *
+			     * -2: MB_INCOMPLETE: We're not waiting
+			     *     for input on this occasion, so
+			     *     just treat this as invalid.
+			     */
+			    if (chars <= 0)
 				chars = 1;
-			    }
 			}
 			else	/* use the non-multibyte code below */
 #endif
@@ -5259,7 +5423,7 @@ bin_print(char *name, char **args, Options ops, int func)
 		break;
 	    case 'q':
 		stringval = curarg ?
-		    quotestring(metafy(curarg, curlen, META_USEHEAP),
+		    quotestring(metafy(curarg, curlen, META_NOALLOC),
 				QT_BACKSLASH_SHOWNULL) : &nullstr;
 		*d = 's';
 		print_val(unmetafy(stringval, &curlen));
@@ -5313,9 +5477,9 @@ bin_print(char *name, char **args, Options ops, int func)
 		    else
 			cc = WEOF;
 		    if (cc == WEOF)
-			cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
+			cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0;
 #else
-		    cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
+		    cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0;
 #endif
 		    if (type == 2) {
 			doubleval = cc;
@@ -5331,7 +5495,7 @@ bin_print(char *name, char **args, Options ops, int func)
  		    	*d++ = 'l';
 #endif
 		    	*d++ = 'l', *d++ = *c, *d = '\0';
-			zlongval = (curarg) ? mathevali(curarg) : 0;
+			zlongval = (curarg) ? mathevali(metafy(curarg, curlen, META_NOALLOC)) : 0;
 			if (errflag) {
 			    zlongval = 0;
 			    errflag &= ~ERRFLAG_ERROR;
@@ -5382,7 +5546,7 @@ bin_print(char *name, char **args, Options ops, int func)
 			if (!curarg)
 			    zulongval = (zulong)0;
 			else if (!zstrtoul_underscore(curarg, &zulongval))
-			    zulongval = mathevali(curarg);
+			    zulongval = mathevali(metafy(curarg, curlen, META_NOALLOC));
 			if (errflag) {
 			    zulongval = 0;
 			    errflag &= ~ERRFLAG_ERROR;
@@ -6282,11 +6446,12 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
     long izle_timeout = 0;
 #ifdef MULTIBYTE_SUPPORT
     wchar_t delim = L'\n', wc;
+    int rawbyte = 0;
     mbstate_t mbs;
     char *laststart;
     size_t ret;
 #else
-    char delim = '\n';
+    int delim = '\n';
 #endif
 
     if (OPT_HASARG(ops,c='k')) {
@@ -6373,9 +6538,10 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
     } else
 	readfd = izle = 0;
 
-    if (OPT_ISSET(ops,'s') && SHTTY != -1) {
+    if (OPT_ISSET(ops,'s') && isatty(readfd)) {
 	struct ttyinfo ti;
-	gettyinfo(&ti);
+	memset(&ti, 0, sizeof(struct ttyinfo));
+	fdgettyinfo(readfd, &ti);
 	saveti = ti;
 	resettty = 1;
 #ifdef HAS_TIO
@@ -6383,7 +6549,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 #else
 	ti.sgttyb.sg_flags &= ~ECHO;
 #endif
-	settyinfo(&ti);
+	fdsettyinfo(readfd, &ti);
     }
 
     /* handle prompt */
@@ -6412,15 +6578,18 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 	    wi = WEOF;
 	if (wi != WEOF)
 	    delim = (wchar_t)wi;
-	else
-	    delim = (wchar_t)((delimstr[0] == Meta) ?
+	else {
+	    delim = (wchar_t) (unsigned char) ((delimstr[0] == Meta) ?
 			      delimstr[1] ^ 32 : delimstr[0]);
+	    rawbyte = 1;
+	}
 #else
-        delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0];
+        delim = (unsigned char) ((delimstr[0] == Meta) ?
+			delimstr[1] ^ 32 : delimstr[0]);
 #endif
-	if (SHTTY != -1) {
+	if (isatty(readfd)) {
 	    struct ttyinfo ti;
-	    gettyinfo(&ti);
+	    fdgettyinfo(readfd, &ti);
 	    if (! resettty) {
 	      saveti = ti;
 	      resettty = 1;
@@ -6432,7 +6601,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 #else
 	    ti.sgttyb.sg_flags |= CBREAK;
 #endif
-	    settyinfo(&ti);
+	    fdsettyinfo(readfd, &ti);
 	}
     }
     if (OPT_ISSET(ops,'t')) {
@@ -6467,10 +6636,11 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 			   timeout)) {
 		if (keys && !zleactive && !isem)
 		    settyinfo(&shttyinfo);
-		else if (resettty && SHTTY != -1)
-		    settyinfo(&saveti);
+		else if (resettty)
+		    fdsettyinfo(readfd, &saveti);
 		if (haso) {
-		    fclose(shout);
+		    if (shout)
+			fclose(shout);
 		    shout = oshout;
 		    SHTTY = -1;
 		}
@@ -6578,8 +6748,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 	    /* dispose of result appropriately, etc. */
 	    if (isem)
 		while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
-	    else {
-		settyinfo(&shttyinfo);
+	    else if (resettty) {
+		fdsettyinfo(readfd, &saveti);
 		resettty = 0;
 	    }
 	    if (haso) {
@@ -6608,8 +6778,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 	    setsparam(reply, metafy(buf, bptr - buf, META_REALLOC));
 	else
 	    zfree(buf, bptr - buf + 1);
-	if (resettty && SHTTY != -1)
-	    settyinfo(&saveti);
+	if (resettty)
+	    fdsettyinfo(readfd, &saveti);
 	return eof;
     }
 
@@ -6685,7 +6855,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 		    continue;
 		first = 0;
 	    }
-	    if (imeta(STOUC(*bptr))) {
+	    if (imeta((unsigned char) *bptr)) {
 		bptr[1] = bptr[0] ^ 32;
 		bptr[0] = Meta;
 		bptr += 2;
@@ -6819,8 +6989,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 	    *pp++ = NULL;
 	    setaparam(reply, p);
 	}
-	if (resettty && SHTTY != -1)
-	    settyinfo(&saveti);
+	if (resettty)
+	    fdsettyinfo(readfd, &saveti);
 	return c == EOF;
     }
     buf = bptr = (char *)zalloc(bsiz = 64);
@@ -6841,7 +7011,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 		break;
 	    }
 	    *bptr = (char)c;
-	    if (isset(MULTIBYTE)) {
+	    if (isset(MULTIBYTE) && !rawbyte) {
 		ret = mbrtowc(&wc, bptr, 1, &mbs);
 		if (!ret)	/* NULL */
 		    ret = 1;
@@ -6878,7 +7048,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 		if (bslash)
 		    continue;
 	    }
-	    if (imeta(STOUC(*bptr))) {
+	    if (imeta((unsigned char) *bptr)) {
 		bptr[1] = bptr[0] ^ 32;
 		bptr[0] = Meta;
 		bptr += 2;
@@ -6948,8 +7118,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func))
 	    break;
     }
     *bptr = '\0';
-    if (resettty && SHTTY != -1)
-	settyinfo(&saveti);
+    if (resettty)
+	fdsettyinfo(readfd, &saveti);
     /* final assignment of reply, etc. */
     if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
 	zputs(buf, stdout);
@@ -7000,14 +7170,14 @@ zread(int izle, int *readchar, long izle_timeout)
 	   buffer.  This may be a null byte to indicate EOF.  If reading from the
 	   buffer, move on the buffer pointer. */
 	if (*zbuf == Meta)
-	    return zbuf++, STOUC(*zbuf++ ^ 32);
+	    return zbuf++, (unsigned char) (*zbuf++ ^ 32);
 	else
-	    return (*zbuf) ? STOUC(*zbuf++) : EOF;
+	    return (*zbuf) ? (unsigned char) *zbuf++ : EOF;
     }
     if (*readchar >= 0) {
 	cc = *readchar;
 	*readchar = -1;
-	return STOUC(cc);
+	return (unsigned char) cc;
     }
     for (;;) {
 	/* read a character from readfd */
@@ -7015,7 +7185,7 @@ zread(int izle, int *readchar, long izle_timeout)
 	switch (ret) {
 	case 1:
 	    /* return the character read */
-	    return STOUC(cc);
+	    return (unsigned char) cc;
 	case -1:
 #if defined(EAGAIN) || defined(EWOULDBLOCK)
 	    if (!retry && readfd == 0 && (
@@ -7204,7 +7374,7 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     /* If given no arguments, list all currently-set traps */
     if (!*argv) {
 	queue_signals();
-	for (sig = 0; sig < VSIGCOUNT; sig++) {
+	for (sig = 0; sig < TRAPCOUNT; sig++) {
 	    if (sigtrapped[sig] & ZSIG_FUNC) {
 		HashNode hn;
 
@@ -7230,13 +7400,13 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 
     /* If we have a signal number, unset the specified *
      * signals.  With only -, remove all traps.        */
-    if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
+    if ((getsigidx(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
 	if (!*argv) {
-	    for (sig = 0; sig < VSIGCOUNT; sig++)
+	    for (sig = 0; sig < TRAPCOUNT; sig++)
 		unsettrap(sig);
 	} else {
 	    for (; *argv; argv++) {
-		sig = getsignum(*argv);
+		sig = getsigidx(*argv);
 		if (sig == -1) {
 		    zwarnnam(name, "undefined signal: %s", *argv);
 		    break;
@@ -7261,12 +7431,12 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	Eprog t;
 	int flags;
 
-	sig = getsignum(*argv);
+	sig = getsigidx(*argv);
 	if (sig == -1) {
 	    zwarnnam(name, "undefined signal: %s", *argv);
 	    break;
 	}
-	if (idigit(**argv) ||
+	if (idigit(**argv) || (sig >= VSIGCOUNT) ||
 	    !strcmp(sigs[sig], *argv) ||
 	    (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) {
 	    /* The signal was specified by number or by canonical name (with
diff --git a/Src/compat.c b/Src/compat.c
index 817bb4aaf..8b31ad9f4 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -342,36 +342,69 @@ zopenmax(void)
 mod_export char *
 zgetdir(struct dirsav *d)
 {
-    char nbuf[PATH_MAX+3];
     char *buf;
+#if defined(HAVE_GETCWD) || defined(__CYGWIN__)
+    char *cwdbuf;
+#endif
+#if defined(USE_GETCWD) || defined(__CYGWIN__)
+#ifdef GETCWD_CALLS_MALLOC
+    if ((cwdbuf = getcwd(NULL, 0))) {
+	buf = dupstring(cwdbuf);
+	free(cwdbuf);
+    } else
+	buf = NULL;
+#else
+    cwdbuf = zalloc(PATH_MAX+1);
+    if ((buf = getcwd(cwdbuf, PATH_MAX)))
+	buf = dupstring(buf);
+    zfree(cwdbuf, PATH_MAX+1);
+#endif /* GETCWD_CALLS_MALLOC */
+    if (d && buf)
+	return d->dirname = ztrdup(buf);
+    else
+	return buf;
+#else /* !USE_GETCWD && !__CYGWIN__ */
     int bufsiz, pos;
-    struct stat sbuf;
-    ino_t pino;
-    dev_t pdev;
-#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
+    char nbuf[PATH_MAX+3];
     struct dirent *de;
     DIR *dir;
-    dev_t dev;
-    ino_t ino;
+    struct stat sbuf;
+    ino_t pino, ino;
+    dev_t pdev, dev;
     int len;
-#endif
 
+    strcpy(nbuf, "../");
+    if (stat(".", &sbuf) == 0) {
+	/* Record the initial inode and device */
+	pino = sbuf.st_ino;
+	pdev = sbuf.st_dev;
+	if (d)
+	    d->ino = pino, d->dev = pdev;
+    }
+#ifdef HAVE_GETCWD
+    else {
+#ifdef GETCWD_CALLS_MALLOC
+	if ((cwdbuf = getcwd(NULL, 0))) {
+	    buf = dupstring(cwdbuf);
+	    free(cwdbuf);
+	} else
+	    buf = NULL;
+#else
+	cwdbuf = zalloc(PATH_MAX+1);
+	if ((buf = getcwd(cwdbuf, PATH_MAX)))
+	    buf = dupstring(buf);
+	zfree(cwdbuf, PATH_MAX+1);
+#endif /* GETCWD_CALLS_MALLOC */
+	return buf;    /* NULL when stat() and getcwd() both failed */
+    }
+#endif
+    /* stat() succeeded */
     buf = zhalloc(bufsiz = PATH_MAX+1);
     pos = bufsiz - 1;
     buf[pos] = '\0';
-    strcpy(nbuf, "../");
-    if (stat(".", &sbuf) < 0) {
-	return NULL;
-    }
 
-    /* Record the initial inode and device */
-    pino = sbuf.st_ino;
-    pdev = sbuf.st_dev;
-    if (d)
-	d->ino = pino, d->dev = pdev;
-#if !defined(__CYGWIN__) && !defined(USE_GETCWD)
 #ifdef HAVE_FCHDIR
-    else
+    if (!d)
 #endif
 	holdintr();
 
@@ -487,18 +520,6 @@ zgetdir(struct dirsav *d)
 	zchdir(buf + pos + 1);
     noholdintr();
 
-#else  /* __CYGWIN__, USE_GETCWD cases */
-
-    if (!getcwd(buf, bufsiz)) {
-	if (d) {
-	    return NULL;
-	}
-    } else {
-	if (d) {
-	    return d->dirname = ztrdup(buf);
-	}
-	return buf;
-    }
 #endif
 
     /*
@@ -526,23 +547,6 @@ mod_export char *
 zgetcwd(void)
 {
     char *ret = zgetdir(NULL);
-#ifdef HAVE_GETCWD
-    if (!ret) {
-#ifdef GETCWD_CALLS_MALLOC
-	char *cwd = getcwd(NULL, 0);
-	if (cwd) {
-	    ret = dupstring(cwd);
-	    free(cwd);
-	}
-#else
-	char *cwdbuf = zalloc(PATH_MAX+1);
-	ret = getcwd(cwdbuf, PATH_MAX);
-	if (ret)
-	    ret = dupstring(ret);
-	zfree(cwdbuf, PATH_MAX+1);
-#endif /* GETCWD_CALLS_MALLOC */
-    }
-#endif /* HAVE_GETCWD */
     if (!ret)
 	ret = unmeta(pwd);
     if (!ret || *ret == '\0')
diff --git a/Src/exec.c b/Src/exec.c
index f2911807c..e955e85df 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -56,14 +56,56 @@ struct funcsave {
 typedef struct funcsave *Funcsave;
 
 /*
- * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT.
+ * Used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT in the
+ * evaluation of sub-commands of the command under evaluation. The
+ * variable must be updated before the evaluation of the sub-commands
+ * starts and restored to its previous state right after that
+ * evaluation ends. The variable is read and acted upon in execlist.
+ *
+ * A good usage example can be found in execwhile in loop.c, which
+ * evaluates while statements. The variable is updated to disable
+ * ERREXIT just before evaluating the while's condition and restored
+ * to its previous state right after the evaluation of the condition.
+ *
  * Bits from noerrexit_bits.
  */
 
 /**/
 int noerrexit;
 
-/* used to suppress ERREXIT or ERRRETURN for one occurrence: 0 or 1 */
+/*
+ * Used to suppress ERREXIT and ERRRETURN for the command under
+ * evaluation.  The variable must be enabled (set to 1) at the very
+ * end of the evaluation of the command. It must come after the
+ * evaluation of any sub-commands of the command under evaluation. The
+ * variable is read and acted upon in execlist, which also takes care
+ * of initialising and resetting it to 0.
+ *
+ * Unlike the variable noerrexit, whose state applies to the
+ * evaluation of whole sub-commands (and their direct and indirect
+ * sub-commands), the scope of the variable this_noerrexit is much
+ * more localized. ERREXIT and ERRRETURN are triggered at the end of
+ * the function execlist after the evaluation of some or all of the
+ * list's sub-commands. The role of the variable this_noerrexit is to
+ * give to the functions evaluating the list's sub-commands the
+ * possibility to tell the calling execlist not to trigger ERREXIT and
+ * ERRRETURN. In other words, the variable acts as an additional
+ * return value between the called evaluation functions and the
+ * calling execlist. For that reason the variable must always be set
+ * as late as possible and in particular after any sub-command
+ * evaluation. If the variable is set before the evaluation of a
+ * sub-command, if may affect the wrong execlist, if the sub-command
+ * evaluation involves another execlist call, and/or the variable may
+ * get modified by the sub-command evaluation and thus wouldn't return
+ * the desired value to the calling execlist.
+ *
+ * Good usage examples can be found in the exec functions in loop.c,
+ * which evaluate compound commands. The variable is enabled right
+ * before returning from the functions, after all the sub-commands of
+ * the compound commands have already been evaluated.
+ *
+ * 0 or 1
+ */
 
 /**/
 int this_noerrexit;
@@ -225,7 +267,7 @@ static char *blank_env[] = { NULL };
 
 /* Execution functions. */
 
-static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
+static int (*execfuncs[WC_COUNT-WC_CURSH]) (Estate, int) = {
     execcursh, exectime, NULL /* execfuncdef handled specially */,
     execfor, execselect,
     execwhile, execrepeat, execcase, execif, execcond,
@@ -477,7 +519,7 @@ zexecve(char *pth, char **argv, char **newenvp)
     if (*pth == '/')
 	strcpy(buf + 2, pth);
     else
-	sprintf(buf + 2, "%s/%s", pwd, pth);
+	sprintf(buf + 2, "%s/%s", unmeta(pwd), pth);
     zputenv(buf);
 #ifndef FD_CLOEXEC
     closedumps();
@@ -509,7 +551,7 @@ zexecve(char *pth, char **argv, char **newenvp)
 			    break;
 		    if (t0 == ct)
 			zerr("%s: bad interpreter: %s: %e", pth,
-			     execvebuf + 2, eno);
+			     metafy(execvebuf + 2, -1, META_STATIC), eno);
 		    else {
 			while (inblank(execvebuf[t0]))
 			    execvebuf[t0--] = '\0';
@@ -532,8 +574,8 @@ zexecve(char *pth, char **argv, char **newenvp)
 				    execve(pprog, argv - 2, newenvp);
 				}
 			    }
-			    zerr("%s: bad interpreter: %s: %e", pth, ptr2,
-				 eno);
+			    zerr("%s: bad interpreter: %s: %e", pth,
+				 metafy(ptr2, -1, META_STATIC), eno);
 			} else if (*ptr) {
 			    *ptr = '\0';
 			    argv[-2] = ptr2;
@@ -561,7 +603,7 @@ zexecve(char *pth, char **argv, char **newenvp)
                         isbinary = 1;
                         hasletter = 0;
                         for (ptr = execvebuf; ptr < ptr2; ptr++) {
-                            if (islower(STOUC(*ptr)) || *ptr == '$' || *ptr == '`')
+			    if (islower((unsigned char) *ptr) || *ptr == '$' || *ptr == '`')
                                 hasletter = 1;
                             if (hasletter && *ptr == '\n') {
                                 isbinary = 0;
@@ -570,9 +612,22 @@ zexecve(char *pth, char **argv, char **newenvp)
                         }
                     }
 		    if (!isbinary) {
-			argv[-1] = "sh";
+		        char** args = argv;
+			if (argv[0][0] == '-' || argv[0][0] == '+') {
+			    /*
+			     * guard against +foo or -bar script paths being
+			     * taken as options. POSIX says the script path
+			     * must be passed as an *operand*. "--" would also
+			     * make sure the next argument is treated as an
+			     * operand with POSIX compliant sh implementations
+			     * but "-" is more portable (to the Bourne shell in
+			     * particular) and shorter.
+			     */
+			    *--args = "-";
+			}
+			*--args = "sh";
 			winch_unblock();
-			execve("/bin/sh", argv - 1, newenvp);
+			execve("/bin/sh", args, newenvp);
 		    }
 		}
 	    } else
@@ -1373,7 +1428,7 @@ execlist(Estate state, int dont_change_job, int exiting)
 	    int oerrexit_opt = opts[ERREXIT];
 	    Param pm;
 	    opts[ERREXIT] = 0;
-	    noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+	    noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
 	    if (ltype & Z_SIMPLE) /* skip the line number */
 		pc2++;
 	    pm = assignsparam("ZSH_DEBUG_CMD",
@@ -1424,17 +1479,13 @@ execlist(Estate state, int dont_change_job, int exiting)
 	    goto sublist_done;
 	}
 	while (wc_code(code) == WC_SUBLIST) {
-	    int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END);
+	    this_noerrexit = 0;
+	    int isandor = WC_SUBLIST_TYPE(code) != WC_SUBLIST_END;
+	    int isnot = WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT;
 	    next = state->pc + WC_SUBLIST_SKIP(code);
-	    if (!oldnoerrexit)
-		noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN;
-	    if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) {
-		/* suppress errexit for "! this_command" */
-		if (isend)
-		    this_noerrexit = 1;
-		/* suppress errexit for ! <list-of-shell-commands> */
-		noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
-	    }
+	    /* suppress errexit for commands before && and || and after ! */
+	    if (isandor || isnot)
+		noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
 	    switch (WC_SUBLIST_TYPE(code)) {
 	    case WC_SUBLIST_END:
 		/* End of sublist; just execute, ignoring status. */
@@ -1443,6 +1494,9 @@ execlist(Estate state, int dont_change_job, int exiting)
 		else
 		    execpline(state, code, ltype, (ltype & Z_END) && exiting);
 		state->pc = next;
+		/* suppress errexit for the command "! ..." */
+		if (isnot)
+		  this_noerrexit = 1;
 		goto sublist_done;
 		break;
 	    case WC_SUBLIST_AND:
@@ -1450,7 +1504,7 @@ execlist(Estate state, int dont_change_job, int exiting)
 		 * we find a sublist followed by ORNEXT.                   */
 		if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
 			    execsimple(state) :
-			    execpline(state, code, Z_SYNC, 0)))) {
+			    execpline(state, code, Z_SYNC, 0))) || breaks) {
 		    state->pc = next;
 		    code = *state->pc++;
 		    next = state->pc + WC_SUBLIST_SKIP(code);
@@ -1483,7 +1537,7 @@ execlist(Estate state, int dont_change_job, int exiting)
 		 * we find a sublist followed by ANDNEXT.              */
 		if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
 			     execsimple(state) :
-			     execpline(state, code, Z_SYNC, 0)))) {
+			     execpline(state, code, Z_SYNC, 0))) || breaks) {
 		    state->pc = next;
 		    code = *state->pc++;
 		    next = state->pc + WC_SUBLIST_SKIP(code);
@@ -1518,14 +1572,7 @@ execlist(Estate state, int dont_change_job, int exiting)
 	state->pc--;
 sublist_done:
 
-	/*
-	 * See hairy code near the end of execif() for the
-	 * following.  "noerrexit " only applies until
-	 * we hit execcmd on the way down.  We're now
-	 * on the way back up, so don't restore it.
-	 */
-	if (!(oldnoerrexit & NOERREXIT_UNTIL_EXEC))
-	    noerrexit = oldnoerrexit;
+	noerrexit = oldnoerrexit;
 
 	if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) {
 	    /*
@@ -1534,7 +1581,7 @@ sublist_done:
 	     */
 	    int oerrexit_opt = opts[ERREXIT];
 	    opts[ERREXIT] = 0;
-	    noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+	    noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
 	    exiting = donetrap;
 	    ret = lastval;
 	    dotrap(SIGDEBUG);
@@ -1564,6 +1611,7 @@ sublist_done:
 			       (isset(ERRRETURN) && !errreturn)) &&
 		    !(noerrexit & NOERREXIT_EXIT);
 		if (errexit) {
+		    errflag = 0;
 		    if (sigtrapped[SIGEXIT])
 			dotrap(SIGEXIT);
 		    if (mypid != getpid())
@@ -1581,6 +1629,7 @@ sublist_done:
 	    break;
 	code = *state->pc++;
     }
+    this_noerrexit = 0;
     pline_level = old_pline_level;
     list_pipe = old_list_pipe;
     list_pipe_job = old_list_pipe_job;
@@ -1595,9 +1644,12 @@ sublist_done:
 	thisjob = cj;
 
     if (exiting && sigtrapped[SIGEXIT]) {
+	int eflag = errflag;
+	errflag = 0;	/* Clear the context for trap */
 	dotrap(SIGEXIT);
 	/* Make sure this doesn't get executed again. */
 	sigtrapped[SIGEXIT] = 0;
+	errflag = eflag;
     }
 
     unqueue_signals();
@@ -1899,8 +1951,12 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 			break;
 		    }
 		}
-		else if (subsh && jn->stat & STAT_STOPPED)
-		    thisjob = newjob;
+		else if (subsh && jn->stat & STAT_STOPPED) {
+		    if (thisjob == newjob)
+			makerunning(jn);
+		    else
+			thisjob = newjob;
+		}
 		else
 		    break;
 	    }
@@ -1922,7 +1978,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 	}
 	else
 	    unqueue_signals();
-	if ((slflags & WC_SUBLIST_NOT) && !errflag)
+	if ((slflags & WC_SUBLIST_NOT) && !errflag && !retflag)
 	    lastval = !lastval;
     }
     if (!pline_level)
@@ -2247,6 +2303,8 @@ closemn(struct multio **mfds, int fd, int type)
 	    return;
 	}
 	/* pid == 0 */
+	opts[INTERACTIVE] = 0;
+	dont_queue_signals();
 	child_unblock();
 	closeallelse(mn);
 	if (mn->rflag) {
@@ -2259,19 +2317,21 @@ closemn(struct multio **mfds, int fd, int type)
 			break;
 		}
 		for (i = 0; i < mn->ct; i++)
-		    write_loop(mn->fds[i], buf, len);
+		    if (write_loop(mn->fds[i], buf, len) < 0)
+			break;
 	    }
 	} else {
 	    /* cat process */
 	    for (i = 0; i < mn->ct; i++)
 		while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) {
 		    if (len < 0) {
-			if (errno == EINTR)
+			if (errno == EINTR && !isatty(mn->fds[i]))
 			    continue;
 			else
 			    break;
 		    }
-		    write_loop(mn->pipe, buf, len);
+		    if (write_loop(mn->pipe, buf, len) < 0)
+			break;
 		}
 	}
 	_exit(0);
@@ -2345,7 +2405,7 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag,
 	/* fd will be over 10, don't touch mfds */
 	fd1 = movefd(fd2);
 	if (fd1 == -1) {
-	    zerr("cannot moved fd %d: %e", fd2, errno);
+	    zerr("cannot move fd %d: %e", fd2, errno);
 	    return;
 	} else {
 	    fdtable[fd1] = FDT_EXTERNAL;
@@ -2544,6 +2604,17 @@ addvars(Estate state, Wordcode pc, int addflags)
 		opts[ALLEXPORT] = allexp;
 	    } else
 	    	pm = assignsparam(name, val, myflags);
+	    if (!pm) {
+		lastval = 1;
+		/*
+		 * This is cheating but some exec functions propagate
+		 * assignment status only from command substitution
+		 *
+		 * zerr("%s: assignment failed", name);
+		 */
+		if (!cmdoutval)
+		    cmdoutval = 1;
+	    }
 	    if (errflag) {
 		state->pc = opc;
 		return;
@@ -2568,7 +2639,16 @@ addvars(Estate state, Wordcode pc, int addflags)
 	    }
 	    fprintf(xtrerr, ") ");
 	}
-	assignaparam(name, arr, myflags);
+	if (!assignaparam(name, arr, myflags)) {
+	    lastval = 1;
+	    /*
+	     * See above RE "cheating"
+	     *
+	     * zerr("%s: array assignment failed", name);
+	     */
+	    if (!cmdoutval)
+		cmdoutval = 1;
+	}
 	if (errflag) {
 	    state->pc = opc;
 	    return;
@@ -3200,10 +3280,6 @@ execcmd_exec(Estate state, Execcmd_params eparams,
     } else
 	preargs = NULL;
 
-    /* if we get this far, it is OK to pay attention to lastval again */
-    if (noerrexit & NOERREXIT_UNTIL_EXEC)
-	noerrexit = 0;
-
     /* Do prefork substitutions.
      *
      * Decide if we need "magic" handling of ~'s etc. in
@@ -3712,7 +3788,7 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid);
 		/* If this is 'exec < file', read from stdin, *
 		 * not terminal, unless `file' is a terminal. */
-		if (nullexec == 1 && fn->fd1 == 0 &&
+		if (nullexec == 1 && fn->fd1 == 0 && !fn->varid &&
 		    isset(SHINSTDIN) && interact && !zleactive)
 		    init_io(NULL);
 		break;
@@ -4301,10 +4377,13 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	}
     }
     if (newxtrerr) {
+	int eno = errno;
 	fil = fileno(newxtrerr);
 	fclose(newxtrerr);
 	xtrerr = oxtrerr;
+	/* Call zclose() to clean up internal tables, ignore EBADF */
 	zclose(fil);
+	errno = eno;
     }
 
     zsfree(STTYval);
@@ -4636,6 +4715,9 @@ getoutput(char *cmd, int qt)
     if (!prog)
 	return NULL;
 
+    if (!isset(EXECOPT))
+	return newlinklist();
+
     if ((s = simple_redir_name(prog, REDIR_READ))) {
 	/* $(< word) */
 	int stream;
@@ -4852,7 +4934,6 @@ getoutputfile(char *cmd, char **eptr)
 
     if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) {
 	zerr("process substitution failed: %e", errno);
-	free(nam);
 	if (!s)
 	    child_unblock();
 	return NULL;
@@ -5052,7 +5133,7 @@ getpipe(char *cmd, int nullexec)
 	procsubstpid = pid;
 	return pipes[!out];
     }
-    entersubsh(ESUB_ASYNC|ESUB_PGRP, NULL);
+    entersubsh(ESUB_ASYNC|ESUB_PGRP|ESUB_NOMONITOR, NULL);
     redup(pipes[out], out);
     closem(FDT_UNUSED, 0);	/* this closes pipes[!out] as well */
     cmdpush(CS_CMDSUBST);
@@ -5366,7 +5447,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 	} else {
 	    /* is this shell function a signal trap? */
 	    if (!strncmp(s, "TRAP", 4) &&
-		(signum = getsignum(s + 4)) != -1) {
+		(signum = getsigidx(s + 4)) != -1) {
 		if (settrap(signum, NULL, ZSIG_FUNC)) {
 		    freeeprog(shf->funcdef);
 		    dircache_set(&shf->filename, NULL);
@@ -5734,12 +5815,25 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
     char *name = shfunc->node.nam;
     int flags = shfunc->node.flags;
     char *fname = dupstring(name);
-    Eprog prog;
+    Eprog prog, marked_prog;
     static int oflags;
     static int funcdepth;
     Heap funcheap;
 
     queue_signals();	/* Lots of memory and global state changes coming */
+    /*
+     * In case this is a special function such as a trap, mark it
+     * as in use right now, so it doesn't get freed early.  The
+     * worst that can happen is this hangs around in memory a little
+     * longer than strictly needed.
+     *
+     * Classic example of this happening is running TRAPEXIT directly.
+     *
+     * Because the shell function's contents may change, we'll ensure
+     * we use a consistent structure for use / free.
+     */
+    marked_prog = shfunc->funcdef;
+    useeprog(marked_prog);
 
     NEWHEAPS(funcheap) {
 	/*
@@ -5773,6 +5867,22 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	    memcpy(funcsave->pipestats, pipestats, bytes);
 	}
 
+	if (!strcmp(fname, "TRAPEXIT")) {
+	    /*
+	     * If we are executing TRAPEXIT directly, starttrapscope()
+	     * will pull the rug out from under us to ensure the
+	     * exit trap isn't run inside the function.  We just need
+	     * the information locally here, so copy it on the heap.
+	     *
+	     * The funcdef is separately handled by reference counting.
+	     */
+	    Shfunc shcopy = (Shfunc)zhalloc(sizeof(struct shfunc));
+	    memcpy(shcopy, shfunc, sizeof(struct shfunc));
+	    shcopy->node.nam = dupstring(shfunc->node.nam);
+	    shfunc = shcopy;
+	    name = shfunc->node.nam;
+	}
+
 	starttrapscope();
 	startpatternscope();
 
@@ -5897,6 +6007,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	funcsave->fstack.filename = getshfuncfile(shfunc);
 
 	prog = shfunc->funcdef;
+	DPUTS1(!prog->nref, "function definition %s has zero reference count",
+	       (fname && *fname) ? fname : "<anon>");
 	if (prog->flags & EF_RUN) {
 	    Shfunc shf;
 
@@ -5925,15 +6037,6 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	     * This function is forced to return.
 	     */
 	    retflag = 0;
-	    /*
-	     * The calling function isn't necessarily forced to return,
-	     * but it should be made sensitive to ERR_EXIT and
-	     * ERR_RETURN as the assumptions we made at the end of
-	     * constructs within this function no longer apply.  If
-	     * there are cases where this is not true, they need adding
-	     * to C03traps.ztst.
-	     */
-	    this_noerrexit = 0;
 	    breaks = funcsave->breaks;
 	}
 	freearray(pparams);
@@ -5961,11 +6064,23 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	    emulation = funcsave->emulation;
 	    sticky = funcsave->sticky;
 	} else if (isset(LOCALOPTIONS)) {
+	    /* we need to call inittyptab() if these options change */
+	    int init_typtab =
+#ifdef MULTIBYTE_SUPPORT
+			funcsave->opts[MULTIBYTE] != opts[MULTIBYTE] ||
+#endif
+			funcsave->opts[BANGHIST] != opts[BANGHIST] ||
+			funcsave->opts[SHINSTDIN] != opts[SHINSTDIN];
+	    /* take care of SUNKEYBOARDHACK but not of EMACS/VI */
+	    if (funcsave->opts[SUNKEYBOARDHACK] != opts[SUNKEYBOARDHACK])
+		keyboardhackchar = funcsave->opts[SUNKEYBOARDHACK] ? '`' : '\0';
 	    /* restore all shell options except PRIVILEGED and RESTRICTED */
 	    funcsave->opts[PRIVILEGED] = opts[PRIVILEGED];
 	    funcsave->opts[RESTRICTED] = opts[RESTRICTED];
 	    memcpy(opts, funcsave->opts, sizeof(opts));
 	    emulation = funcsave->emulation;
+	    if (init_typtab)
+		inittyptab();
 	} else {
 	    /* just restore a couple. */
 	    opts[XTRACE] = funcsave->opts[XTRACE];
@@ -5998,6 +6113,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	}
     } OLDHEAPS;
 
+    freeeprog(marked_prog);
     unqueue_signals();
 
     /*
diff --git a/Src/glob.c b/Src/glob.c
index 400be12d5..3e34f708e 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -133,7 +133,7 @@ typedef struct stat *Statptr;	 /* This makes the Ultrix compiler happy.  Go figu
 #define TT_TERABYTES 5
 
 
-typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *));
+typedef int (*TestMatchFunc) (char *, struct stat *, off_t, char *);
 
 struct qual {
     struct qual *next;		/* Next qualifier, must match                */
@@ -1264,7 +1264,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	int sense, qualsfound;
 	off_t data;
 	char *sdata, *newcolonmod, *ptr;
-	int (*func) _((char *, Statptr, off_t, char *));
+	int (*func) (char *, Statptr, off_t, char *);
 
 	/*
 	 * Initialise state variables for current file pattern.
@@ -1310,14 +1310,15 @@ zglob(LinkList list, LinkNode np, int nountok)
 	    if (*ptr == Dash)
 		*ptr = '-';
 	while (*s && !newcolonmod) {
-	    func = (int (*) _((char *, Statptr, off_t, char *)))0;
+	    func = (int (*) (char *, Statptr, off_t, char *)) 0;
 	    if (*s == ',') {
 		/* A comma separates alternative sets of qualifiers */
 		s++;
 		sense = 0;
 		if (qualct) {
 		    qn = (struct qual *)hcalloc(sizeof *qn);
-		    qo->or = qn;
+		    if (qo)
+			qo->or = qn;
 		    qo = qn;
 		    qualorct++;
 		    qualct = 0;
@@ -1481,7 +1482,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 			    sav = *tt;
 			    *tt = '\0';
 
-			    if ((pw = getpwnam(s + arglen)))
+			    if ((pw = getpwnam(unmeta(s + arglen))))
 				data = pw->pw_uid;
 			    else {
 				zerr("unknown username '%s'", s + arglen);
@@ -1960,7 +1961,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	/* Sort arguments in to lexical (and possibly numeric) order. *
 	 * This is reversed to facilitate insertion into the list.    */
 	qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
-	      (int (*) _((const void *, const void *)))gmatchcmp);
+	      (int (*) (const void *, const void *)) gmatchcmp);
     }
 
     if (first < 0) {
@@ -2418,11 +2419,11 @@ xpandbraces(LinkList list, LinkNode *np)
 	memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0]));
 	for (p = str + 1; p < str2;) {
 	    if (itok(c1 = *p++))
-		c1 = ztokens[c1 - STOUC(Pound)];
+		c1 = ztokens[c1 - (unsigned char) Pound];
 	    if ((char) c1 == Meta)
 		c1 = 32 ^ *p++;
 	    if (itok(c2 = *p))
-		c2 = ztokens[c2 - STOUC(Pound)];
+		c2 = ztokens[c2 - (unsigned char) Pound];
 	    if ((char) c2 == Meta)
 		c2 = 32 ^ p[1];
 	    if (IS_DASH((char)c1) && lastch >= 0 &&
diff --git a/Src/hashtable.c b/Src/hashtable.c
index bb165505e..96675a393 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -836,10 +836,10 @@ static HashNode
 removeshfuncnode(UNUSED(HashTable ht), const char *nam)
 {
     HashNode hn;
-    int signum;
+    int sigidx;
 
-    if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1)
-	hn = removetrap(signum);
+    if (!strncmp(nam, "TRAP", 4) && (sigidx = getsigidx(nam + 4)) != -1)
+	hn = removetrap(sigidx);
     else
 	hn = removehashnode(shfunctab, nam);
 
@@ -856,10 +856,10 @@ disableshfuncnode(HashNode hn, UNUSED(int flags))
 {
     hn->flags |= DISABLED;
     if (!strncmp(hn->nam, "TRAP", 4)) {
-	int signum = getsignum(hn->nam + 4);
-	if (signum != -1) {
-	    sigtrapped[signum] &= ~ZSIG_FUNC;
-	    unsettrap(signum);
+	int sigidx = getsigidx(hn->nam + 4);
+	if (sigidx != -1) {
+	    sigtrapped[sigidx] &= ~ZSIG_FUNC;
+	    unsettrap(sigidx);
 	}
     }
 }
@@ -876,9 +876,9 @@ enableshfuncnode(HashNode hn, UNUSED(int flags))
 
     shf->node.flags &= ~DISABLED;
     if (!strncmp(shf->node.nam, "TRAP", 4)) {
-	int signum = getsignum(shf->node.nam + 4);
-	if (signum != -1) {
-	    settrap(signum, NULL, ZSIG_FUNC);
+	int sigidx = getsigidx(shf->node.nam + 4);
+	if (sigidx != -1) {
+	    settrap(sigidx, NULL, ZSIG_FUNC);
 	}
     }
 }
@@ -1397,6 +1397,14 @@ histstrcmp(const char *str1, const char *str2)
 {
     while (inblank(*str1)) str1++;
     while (inblank(*str2)) str2++;
+
+    /* If insignificant whitespace has already been eliminated,
+     * there is no reason to expend similar effort here.  Also,
+     * this is more accurate in cases of quoted whitespace.
+     */
+    if (isset(HISTREDUCEBLANKS))
+	return strcmp(str1, str2);
+
     while (*str1 && *str2) {
 	if (inblank(*str1)) {
 	    if (!inblank(*str2))
diff --git a/Src/hist.c b/Src/hist.c
index bff0abe61..1a00c30ed 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -34,25 +34,25 @@
  * word control. */
 
 /**/
-mod_export int (*hgetc) _((void));
+mod_export int (*hgetc) (void);
 
 /**/
-void (*hungetc) _((int));
+void (*hungetc) (int);
 
 /**/
-void (*hwaddc) _((int));
+void (*hwaddc) (int);
 
 /**/
-void (*hwbegin) _((int));
+void (*hwbegin) (int);
 
 /**/
-void (*hwabort) _((void));
+void (*hwabort) (void);
 
 /**/
-void (*hwend) _((void));
+void (*hwend) (void);
 
 /**/
-void (*addtoline) _((int));
+void (*addtoline) (int);
 
 /* != 0 means history substitution is turned off */
  
@@ -163,6 +163,11 @@ char *hsubl;
 /**/
 char *hsubr;
  
+/* state of histsubstpattern at last substitution */
+
+/**/
+int hsubpatopt;
+
 /* pointer into the history line */
  
 /**/
@@ -624,7 +629,7 @@ histsubchar(int c)
 	    return substfailed();
 	if (!hsubl)
 	    return -1;
-	if (subst(&sline, hsubl, hsubr, gbal))
+	if (subst(&sline, hsubl, hsubr, gbal, 0))
 	    return substfailed();
     } else {
 	/* Line doesn't begin ^foo^bar */
@@ -831,7 +836,7 @@ histsubchar(int c)
 	    if ((c = ingetc()) == 'g') {
 		gbal = 1;
 		c = ingetc();
-		if (c != 's' && c != '&') {
+		if (c != 's' && c != 'S' && c != '&') {
 		    zerr("'s' or '&' modifier expected after 'g'");
 		    return -1;
 		}
@@ -891,11 +896,13 @@ histsubchar(int c)
 		}
 		break;
 	    case 's':
+	    case 'S':
+		hsubpatopt = (c == 'S');
 		if (getsubsargs(sline, &gbal, &cflag))
 		    return -1; /* fall through */
 	    case '&':
 		if (hsubl && hsubr) {
-		    if (subst(&sline, hsubl, hsubr, gbal))
+		    if (subst(&sline, hsubl, hsubr, gbal, hsubpatopt))
 			return substfailed();
 		} else {
 		    herrflush();
@@ -1352,7 +1359,8 @@ putoldhistentryontop(short keep_going)
 	do {
 	    if (max_unique_ct-- <= 0 || he == hist_ring) {
 		max_unique_ct = 0;
-		he = hist_ring->down;
+		if (hist_ring)
+		    he = hist_ring->down;
 		next = hist_ring;
 		break;
 	    }
@@ -1360,12 +1368,16 @@ putoldhistentryontop(short keep_going)
 	    next = he->down;
 	} while (!(he->node.flags & HIST_DUP));
     }
-    if (he != hist_ring->down) {
+    /* Is it really possible for hist_ring to be NULL here? */
+    if (he && (!hist_ring || he != hist_ring->down)) {
 	he->up->down = he->down;
 	he->down->up = he->up;
 	he->up = hist_ring;
-	he->down = hist_ring->down;
-	hist_ring->down = he->down->up = he;
+	if (hist_ring) {
+	    he->down = hist_ring->down;
+	    hist_ring->down = he;
+	}
+	he->down->up = he;
     }
     hist_ring = he;
 }
@@ -1461,7 +1473,7 @@ should_ignore_line(Eprog prog)
 mod_export int
 hend(Eprog prog)
 {
-    int flag, hookret, stack_pos = histsave_stack_pos;
+    int flag, hookret = 0, stack_pos = histsave_stack_pos;
     /*
      * save:
      * 0: don't save
@@ -1643,12 +1655,17 @@ hend(Eprog prog)
 void
 ihwbegin(int offset)
 {
+    int pos = hptr - chline + offset;
     if (stophist == 2 || (histactive & HA_INWORD) ||
 	(inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS)
 	return;
     if (chwordpos%2)
 	chwordpos--;	/* make sure we're on a word start, not end */
-    chwords[chwordpos++] = hptr - chline + offset;
+    DPUTS1(pos < 0, "History word position < 0 in %s",
+	   dupstrpfx(chline, hptr-chline));
+    if (pos < 0)
+	pos = 0;
+    chwords[chwordpos++] = pos;
 }
 
 /* Abort current history word, not needed */
@@ -2235,7 +2252,7 @@ casemodify(char *str, int how)
 		char *mbptr;
 
 		for (mbptr = mbstr; mbptr < mbstr + len2; mbptr++) {
-		    if (imeta(STOUC(*mbptr))) {
+		    if (imeta((unsigned char) *mbptr)) {
 			*ptr2++ = Meta;
 			*ptr2++ = *mbptr ^ 32;
 		    } else
@@ -2254,10 +2271,10 @@ casemodify(char *str, int how)
 	    int c;
 	    int mod = 0;
 	    if (*str == Meta) {
-		c = STOUC(str[1] ^ 32);
+		c = (unsigned char) (str[1] ^ 32);
 		str += 2;
 	    } else
-		c = STOUC(*str++);
+		c = (unsigned char) *str++;
 	    switch (how) {
 	    case CASMOD_LOWER:
 		if (isupper(c)) {
@@ -2310,7 +2327,7 @@ casemodify(char *str, int how)
 
 /**/
 int
-subst(char **strptr, char *in, char *out, int gbal)
+subst(char **strptr, char *in, char *out, int gbal, int forcepat)
 {
     char *str = *strptr, *substcut, *sptr;
     int off, inlen, outlen;
@@ -2318,7 +2335,7 @@ subst(char **strptr, char *in, char *out, int gbal)
     if (!*in)
 	in = str, gbal = 0;
 
-    if (isset(HISTSUBSTPATTERN)) {
+    if (isset(HISTSUBSTPATTERN) || forcepat) {
 	int fl = SUB_LONG|SUB_REST|SUB_RETFAIL;
 	char *oldin = in;
 	if (gbal)
@@ -3791,8 +3808,14 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
 			zrealloc(words, nwords*sizeof(*words));
 		}
 		words[nwordpos++] = lineptr - start;
-		while (*lineptr && !inblank(*lineptr))
-		    lineptr++;
+		while (*lineptr) {
+		    if (*lineptr == Meta && lineptr[1])
+			lineptr += 2;
+		    else if (!inblank(*lineptr))
+			lineptr++;
+		    else
+			break;
+		}
 		words[nwordpos++] = lineptr - start;
 	    }
 	} while (*lineptr);
diff --git a/Src/init.c b/Src/init.c
index 871d46b12..ec21521b1 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -246,6 +246,9 @@ loop(int toplevel, int justonce)
 
 static int restricted;
 
+/* original argv[0]. This is already metafied */
+static char *argv0;
+
 /**/
 static void
 parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
@@ -257,7 +260,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
     if (**argv == '-')
 	flags |= PARSEARGS_LOGIN;
 
-    argzero = posixzero = *argv++;
+    argv0 = argzero = posixzero = *argv++;
     SHIN = 0;
 
     /*
@@ -500,10 +503,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
 		    }
 		}
               break;
-	    } else if (isspace(STOUC(**argv))) {
+	    } else if (isspace((unsigned char) **argv)) {
 		/* zsh's typtab not yet set, have to use ctype */
 		while (*++*argv)
-		    if (!isspace(STOUC(**argv))) {
+		    if (!isspace((unsigned char) **argv)) {
 		     badoptionstring:
 			WARN_OPTION("bad option string: '%s'", args);
 			return 1;
@@ -697,11 +700,14 @@ init_io(char *cmd)
      * process group leader.
      */
     mypid = (zlong)getpid();
-    if (opts[MONITOR] && (SHTTY != -1)) {
-	origpgrp = GETPGRP();
-        acquire_pgrp(); /* might also clear opts[MONITOR] */
-    } else
-	opts[MONITOR] = 0;
+    if (opts[MONITOR]) {
+	if (SHTTY == -1)
+	    opts[MONITOR] = 0;
+	else if (!origpgrp) {
+	    origpgrp = GETPGRP();
+	    acquire_pgrp(); /* might also clear opts[MONITOR] */
+	}
+    }
 #else
     opts[MONITOR] = 0;
 #endif
@@ -747,7 +753,7 @@ init_shout(void)
 static char *tccapnams[TC_COUNT] = {
     "cl", "le", "LE", "nd", "RI", "up", "UP", "do",
     "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta",
-    "md", "so", "us", "me", "se", "ue", "ch",
+    "md", "mh", "so", "us", "ZH", "me", "se", "ue", "ZR", "ch",
     "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB"
 };
 
@@ -872,10 +878,127 @@ init_term(void)
 	/* The following is an attempt at a heuristic,
 	 * but it fails in some cases */
 	/* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */
+
+	/* if there's no termcap entry for italics, use CSI 3 m */
+	if (!tccan(TCITALICSBEG)) {
+	    zsfree(tcstr[TCITALICSBEG]);
+	    tcstr[TCITALICSBEG] = ztrdup("\033[3m");
+	    tclen[TCITALICSBEG] = 4;
+	}
+	if (!tccan(TCITALICSEND)) {
+	    zsfree(tcstr[TCITALICSEND]);
+	    tcstr[TCITALICSEND] = ztrdup("\033[23m");
+	    tclen[TCITALICSEND] = 5;
+	}
+	if (!tccan(TCFAINTBEG)) {
+	    zsfree(tcstr[TCFAINTBEG]);
+	    tcstr[TCFAINTBEG] = ztrdup("\033[2m");
+	    tclen[TCFAINTBEG] = 4;
+	}
     }
     return 1;
 }
 
+/*
+ * Get (or guess) the absolute pathname of the current zsh exeutable.
+ * Try OS-specific method, and if it fails, guess the absolute pathname
+ * from argv0, pwd, and PATH. 'name' and 'cwd' are unmetefied versions of
+ * argv0 and pwd.
+ * Returns a zalloc()ed string (not metafied), or NULL if failed.
+ */
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
+/**/
+static char *
+getmypath(const char *name, const char *cwd)
+{
+    char *buf;
+    int namelen;
+
+    if (!name)
+	return NULL;
+    if (*name == '-')
+	++name;
+    if ((namelen = strlen(name)) == 0)
+	return NULL;
+#if defined(__APPLE__)
+    {
+	uint32_t n = PATH_MAX;
+	int ret;
+	buf = (char *)zalloc(PATH_MAX);
+	if ((ret = _NSGetExecutablePath(buf, &n)) < 0) {
+	    /* try again with increased buffer size */
+	    buf = (char *)zrealloc(buf, n);
+	    ret = _NSGetExecutablePath(buf, &n);
+	}
+	if (ret == 0 && strlen(buf) > 0)
+	    return buf;
+	else
+	    free(buf);
+    }
+#elif defined(PROC_SELF_EXE)
+    {
+	ssize_t n;
+	buf = (char *)zalloc(PATH_MAX);
+	n = readlink(PROC_SELF_EXE, buf, PATH_MAX);
+	if (n > 0 && n < PATH_MAX) {
+	    buf[n] = '\0';
+	    return buf;
+	}
+	else
+	    free(buf);
+    }
+#endif
+    /* guess the absolute pathname of 'name' */
+    if (name[namelen-1] == '/')    /* name should not end with '/' */
+	return NULL;
+    else if (name[0] == '/') {
+	/* name is already an absolute pathname */
+	return ztrdup(name);
+    }
+    else if (strchr(name, '/')) {
+	/* relative path */
+	if (!cwd)
+	    return NULL;
+	buf = (char *)zalloc(strlen(cwd) + namelen + 2);
+	sprintf(buf, "%s/%s", cwd, name);
+	return buf;
+    }
+#ifdef HAVE_REALPATH
+    else {
+	/* search each dir in PARH */
+	const char *path, *sep;
+	char *real, *try;
+	int pathlen, dirlen;
+
+	path = getenv("PATH");
+	if (!path || (pathlen = strlen(path)) == 0)
+	    return NULL;
+	/* for simplicity, allocate buf even if REALPATH_ACCEPTS_NULL is on */
+	buf = (char *)zalloc(PATH_MAX);
+	try = (char *)zalloc(pathlen + namelen + 2);
+	do {
+	    sep = strchr(path, ':');
+	    dirlen = sep ? sep - path : strlen(path);
+	    strncpy(try, path, dirlen);
+	    try[dirlen] = '/';
+	    try[dirlen+1] = '\0';
+	    strcat(try, name);
+	    real = realpath(try, buf);
+	    if (sep)
+		path = sep + 1;
+	} while (!real && sep);
+	free(try);
+	if (!real)
+	    free(buf);
+	return real;	/* this may be NULL */
+    }
+#endif
+    return NULL;
+}
+
 /* Initialize lots of global variables and hash tables */
 
 /**/
@@ -1067,9 +1190,12 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
 	ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS);
     wordchars   = ztrdup(DEFAULT_WORDCHARS);
     postedit    = ztrdup("");
-    zunderscore  = (char *) zalloc(underscorelen = 32);
-    underscoreused = 1;
-    *zunderscore = '\0';
+    /* If _ is set in environment then initialize our $_ by copying it */
+    zunderscore = getenv("_");
+    zunderscore = zunderscore ? metafy(zunderscore, -1, META_DUP) : ztrdup("");
+    underscoreused = strlen(zunderscore) + 1;
+    underscorelen = (underscoreused + 31) & ~31;
+    zunderscore = (char *)zrealloc(zunderscore, underscorelen);
 
     zoptarg = ztrdup("");
     zoptind = 1;
@@ -1089,8 +1215,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
 #ifdef USE_GETPWUID
     if ((pswd = getpwuid(cached_uid))) {
 	if (EMULATION(EMULATE_ZSH))
-	    home = metafy(pswd->pw_dir, -1, META_DUP);
-	cached_username = ztrdup(pswd->pw_name);
+	    home = ztrdup_metafy(pswd->pw_dir);
+	cached_username = ztrdup_metafy(pswd->pw_name);
     }
     else
 #endif /* USE_GETPWUID */
@@ -1175,6 +1301,18 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
     /* Colour sequences for outputting colours in prompts and zle */
     set_default_colour_sequences();
 
+    /* ZSH_EXEPATH */
+    {
+	char *mypath, *exename, *cwd;
+	exename = unmetafy(ztrdup(argv0), NULL);
+	cwd = pwd ? unmetafy(ztrdup(pwd), NULL) : NULL;
+	mypath = getmypath(exename, cwd);
+	free(exename);
+	free(cwd);
+	if (mypath) {
+	    setsparam("ZSH_EXEPATH", metafy(mypath, -1, META_REALLOC));
+	}
+    }
     if (cmd)
 	setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd));
     if (runscript)
@@ -1244,6 +1382,9 @@ setupshin(char *runscript)
 void
 init_signals(void)
 {
+    sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int));
+    siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog));
+
     if (interact) {
 	int i;
 	signal_setmask(signal_mask(0));
@@ -1654,8 +1795,7 @@ VA_DCL
 
 	lp = va_arg(ap, char **);
 
-	pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL,
-				       NULL),
+	pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL),
 			  &pptlen);
 	write_loop(2, pptbuf, pptlen);
 	free(pptbuf);
@@ -1724,9 +1864,9 @@ zsh_main(UNUSED(int argc), char **argv)
      * interactive
      */
     typtab['\0'] |= IMETA;
-    typtab[STOUC(Meta)  ] |= IMETA;
-    typtab[STOUC(Marker)] |= IMETA;
-    for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++)
+    typtab[(unsigned char) Meta  ] |= IMETA;
+    typtab[(unsigned char) Marker] |= IMETA;
+    for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) Nularg; t0++)
 	typtab[t0] |= ITOK | IMETA;
 
     for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++);
diff --git a/Src/input.c b/Src/input.c
index 9898a7177..d8ac2c0e7 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -220,7 +220,7 @@ shingetchar(void)
     int nread, rsize = isset(SHINSTDIN) ? 1 : SHINBUFSIZE;
 
     if (shinbufptr < shinbufendptr)
-	return STOUC(*shinbufptr++);
+	return (unsigned char) *shinbufptr++;
 
     shinbufreset();
 #ifdef USE_LSEEK
@@ -242,7 +242,7 @@ shingetchar(void)
 		zerr("lseek(%d, %d): %e", SHIN, -(nread - rsize), errno);
 	} else
 	    shinbufendptr = shinbuffer + nread;
-	return STOUC(*shinbufptr++);
+	return (unsigned char) *shinbufptr++;
     }
 #endif
     for (;;) {
@@ -259,7 +259,7 @@ shingetchar(void)
     }
     if (shinbufendptr == shinbuffer)
         return -1;
-    return STOUC(*shinbufptr++);
+    return (unsigned char) *shinbufptr++;
 }
 
 /* Read a line from SHIN.  Convert tokens and   *
@@ -328,7 +328,7 @@ ingetc(void)
 	if (inbufleft) {
 	    inbufleft--;
 	    inbufct--;
-	    if (itok(lastc = STOUC(*inbufptr++)))
+	    if (itok(lastc = (unsigned char) *inbufptr++))
 		continue;
 	    if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
 		lineno++;
@@ -402,7 +402,7 @@ inputline(void)
 	    char *pptbuf;
 	    int pptlen;
 	    pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL,
-					   0, NULL, NULL, NULL), &pptlen);
+					   0, NULL, NULL), &pptlen);
 	    write_loop(2, pptbuf, pptlen);
 	    free(pptbuf);
 	}
@@ -610,11 +610,11 @@ inungetc(int c)
     }
 }
 
-/* stuff a whole file into the input queue and print it */
+/* stuff a whole file into memory and return it */
 
 /**/
-int
-stuff(char *fn)
+off_t
+zstuff(char **out, const char *fn)
 {
     FILE *in;
     char *buf;
@@ -622,20 +622,39 @@ stuff(char *fn)
 
     if (!(in = fopen(unmeta(fn), "r"))) {
 	zerr("can't open %s", fn);
-	return 1;
+	return -1;
     }
+    queue_signals();
     fseek(in, 0, SEEK_END);
     len = ftell(in);
     fseek(in, 0, SEEK_SET);
     buf = (char *)zalloc(len + 1);
-    if (!(fread(buf, len, 1, in))) {
+    if (len && !(fread(buf, len, 1, in))) {
 	zerr("read error on %s", fn);
 	fclose(in);
 	zfree(buf, len + 1);
-	return 1;
+	unqueue_signals();
+	return -1;
     }
     fclose(in);
     buf[len] = '\0';
+    *out = buf;
+    unqueue_signals();
+    return len;
+}
+
+/* stuff a whole file into the input queue and print it */
+
+/**/
+int
+stuff(char *fn)
+{
+    char *buf;
+    off_t len = zstuff(&buf, fn);
+
+    if (len < 0)
+	return 1;
+    
     fwrite(buf, len, 1, stderr);
     fflush(stderr);
     inputsetline(metafy(buf, len, META_REALLOC), INP_FREE);
@@ -816,6 +835,7 @@ char *input_hasalias(void)
 {
     int flags = inbufflags;
     struct instacks *instackptr = instacktop;
+    char *alias_nam = NULL;
 
     for (;;)
     {
@@ -824,9 +844,9 @@ char *input_hasalias(void)
 	DPUTS(instackptr == instack, "BUG: continuation at bottom of instack");
 	instackptr--;
 	if (instackptr->alias)
-	    return instackptr->alias->node.nam;
+	    alias_nam = instackptr->alias->node.nam;
 	flags = instackptr->flags;
     }
 
-    return NULL;
+    return alias_nam;
 }
diff --git a/Src/jobs.c b/Src/jobs.c
index e0e453ed8..07facc60b 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -427,11 +427,17 @@ storepipestats(Job jn, int inforeground, int fixlastval)
     }
 
     if (fixlastval) {
-      if (jn->stat & STAT_CURSH) {
-	if (!lastval && isset(PIPEFAIL))
-	  lastval = pipefail;
-      } else if (isset(PIPEFAIL))
-	lastval = pipefail;
+	if (jn->stat & STAT_CURSH) {
+	    if (!lastval && isset(PIPEFAIL)) {
+		if (inforeground)
+		    this_noerrexit = 0;
+		lastval = pipefail;
+	    }
+	} else if (isset(PIPEFAIL)) {
+	    if (inforeground)
+		this_noerrexit = 0;
+	    lastval = pipefail;
+	}
     }
 }
 
@@ -544,16 +550,14 @@ update_job(Job jn)
 
     if (isset(MONITOR)) {
 	pid_t pgrp = gettygrp();           /* get process group of tty      */
+	int deadpgrp = (mypgrp != pgrp && inforeground && pgrp > 1 &&
+			kill(-pgrp, 0) == -1 && errno == ESRCH);
 
 	/* is this job in the foreground of an interactive shell? */
 	if (mypgrp != pgrp && inforeground &&
-	    (jn->gleader == pgrp ||
-	     (pgrp > 1 &&
-	      (kill(-pgrp, 0) == -1 && errno == ESRCH)))) {
+	    ((jn->gleader == pgrp && signalled) || deadpgrp)) {
 	    if (list_pipe) {
-		if (somestopped || (pgrp > 1 &&
-				    kill(-pgrp, 0) == -1 &&
-				    errno == ESRCH)) {
+		if (somestopped || deadpgrp) {
 		    attachtty(mypgrp);
 		    /* check window size and adjust if necessary */
 		    adjustwinsize(0);
@@ -656,6 +660,25 @@ update_job(Job jn)
     }
 }
 
+/**/
+void
+update_bg_job(Job jn, pid_t pid, int status)
+{
+    /*
+     * Accumulate a list of older jobs.  We only do this for
+     * background jobs, which is something in the job table
+     * that's not marked as in the current shell or as shell builtin
+     * and is not equal to the current foreground job.
+     */
+    if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
+	jn - jobtab != thisjob) {
+	if (WIFEXITED(status))
+	    addbgstatus(pid, WEXITSTATUS(status));
+	else if (WIFSIGNALED(status))
+	    addbgstatus(pid, 0200 | WTERMSIG(status));
+    }
+}
+
 /* set the previous job to something reasonable */
 
 /**/
@@ -890,8 +913,13 @@ printtime(struct timeval *real, child_times_t *ti, char *desc)
 		break;
 #endif
 #ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS
+#ifdef RU_MAXRSS_IS_IN_BYTES
+# define MAXRSS_IN_KB(x) ((x)/1024)
+#else
+# define MAXRSS_IN_KB(x) (x)
+#endif
 	    case 'M':
-		fprintf(stderr, "%ld", ti->ru_maxrss / 1024);
+		fprintf(stderr, "%ld", MAXRSS_IN_KB(ti->ru_maxrss));
 		break;
 #endif
 #ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT
@@ -1029,7 +1057,7 @@ should_report_time(Job j)
 	    return 1;
 #else
 	{
-	    clktck = get_clktck();
+	    long clktck = get_clktck();
 	    if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime)
 		return 1;
 	}
@@ -1038,13 +1066,28 @@ should_report_time(Job j)
 
 #ifdef HAVE_GETRUSAGE
     if (reportmemory >= 0 &&
-	j->procs->ti.ru_maxrss / 1024 > reportmemory)
+	MAXRSS_IN_KB(j->procs->ti.ru_maxrss) > reportmemory)
 	return 1;
 #endif
 
     return 0;
 }
 
+/**/
+char *
+sigmsg(int sig)
+{
+    static char *unknown = "unknown signal";
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    static char rtmsg[] = "real-time event XXX";
+    if (sig >= SIGRTMIN && sig <= SIGRTMAX) {
+	sprintf(rtmsg + sizeof(rtmsg) - 4, "%u", sig - SIGRTMIN + 1);
+	return rtmsg;
+    }
+#endif
+    return sig <= SIGCOUNT ? sig_msg[sig] : unknown;
+}
+
 /* !(lng & 3) means jobs    *
  *  (lng & 1) means jobs -l *
  *  (lng & 2) means jobs -p
@@ -1374,8 +1417,10 @@ cleanfilelists(void)
 
     DPUTS(shell_exiting >= 0, "BUG: cleanfilelists() before exit");
  
-    for (i = 1; i <= maxjob; i++)
+    for (i = 1; i <= maxjob; i++) {
 	deletefilelist(jobtab[i].filelist, 0);
+	jobtab[i].filelist = 0;
+    }
 }
 
 /**/
@@ -1488,10 +1533,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime,
 	 * set it for that, too.
 	 */
 	if (gleader != -1) {
-	    if (jobtab[thisjob].stat & STAT_CURSH)
-		jobtab[thisjob].gleader = gleader;
-	    else
-		jobtab[thisjob].gleader = pid;
+	    jobtab[thisjob].gleader = gleader;
 	    if (list_pipe_job_used != -1)
 		jobtab[list_pipe_job_used].gleader = gleader;
 	    /*
@@ -1500,7 +1542,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime,
 	     */
 	    last_attached_pgrp = gleader;
 	} else if (!jobtab[thisjob].gleader)
-		jobtab[thisjob].gleader = pid;
+	    jobtab[thisjob].gleader = pid;
 	/* attach this process to end of process list of current job */
 	pnlist = &jobtab[thisjob].procs;
     }
@@ -1536,8 +1578,10 @@ havefiles(void)
     int i;
 
     for (i = 1; i <= maxjob; i++)
-	if (jobtab[i].stat && jobtab[i].filelist)
+	if (jobtab[i].stat && jobtab[i].filelist &&
+	    peekfirst(jobtab[i].filelist)) {
 	    return 1;
+	}
     return 0;
 
 }
@@ -2402,7 +2446,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
 	    int curmaxjob, ignorejob;
 	    if (unset(MONITOR) && oldmaxjob) {
 		jobptr = oldjobtab;
-		curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0;
+		curmaxjob = oldmaxjob;
 		ignorejob = 0;
 	    } else {
 		jobptr = jobtab;
@@ -2506,6 +2550,7 @@ bin_fg(char *name, char **argv, Options ops, int func)
 		jobtab[job].stat &= ~STAT_CURSH;
 	    }
 	    if ((stopped = (jobtab[job].stat & STAT_STOPPED))) {
+		/* WIFCONTINUED will makerunning() again at killjb() */
 		makerunning(jobtab + job);
 		if (func == BIN_BG) {
 		    /* Set $! to indicate this was backgrounded */
@@ -2664,11 +2709,37 @@ static const struct {
 int
 bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
-    int sig = SIGTERM;
+    int status, sig = SIGTERM;
     int returnval = 0;
+#ifdef HAVE_SIGQUEUE
+    union sigval sigqueue_info;
+#endif
+    int use_sigqueue = 0, got_sig = 0;
+
+    while (*argv && **argv == '-') {
+	if (!use_sigqueue && (*argv)[1] == 'q' && (*argv)[2] == '\0') {
+	    char *endp;
+
+	    if (!*++argv) {
+		zwarnnam(nam, "-q: argument expected");
+		return 1;
+	    }
+#ifdef HAVE_SIGQUEUE
+	    sigqueue_info.sival_int =
+#endif
+		    zstrtol(*argv, &endp, 10);
+	    if (*endp) {
+		zwarnnam(nam, "invalid number: %s", *argv);
+		return 1;
+	    }
+	    use_sigqueue = 1;
+	    argv++;
+	    continue;
+	}
+	if (got_sig)
+	    break;
 
     /* check for, and interpret, a signal specifier */
-    if (*argv && **argv == '-') {
 	if (idigit((*argv)[1])) {
 	    char *endp;
 	    /* signal specified by number */
@@ -2684,23 +2755,29 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
 		if (argv[1]) {
 		    while (*++argv) {
-			sig = zstrtol(*argv, &signame, 10);
+			status = zstrtol(*argv, &signame, 10);
 			if (signame == *argv) {
+			    signame = casemodify(signame, CASMOD_UPPER);
 			    if (!strncmp(signame, "SIG", 3))
 				signame += 3;
 			    for (sig = 1; sig <= SIGCOUNT; sig++)
-				if (!strcasecmp(sigs[sig], signame))
+				if (!strcmp(sigs[sig], signame))
 				    break;
 			    if (sig > SIGCOUNT) {
 				int i;
 
 				for (i = 0; alt_sigs[i].name; i++)
-				    if (!strcasecmp(alt_sigs[i].name, signame))
+				    if (!strcmp(alt_sigs[i].name, signame))
 				    {
 					sig = alt_sigs[i].num;
 					break;
 				    }
 			    }
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+			    if (sig > SIGCOUNT && (sig = rtsigno(signame))) {
+				printf("%d\n", sig);
+			    } else
+#endif
 			    if (sig > SIGCOUNT) {
 				zwarnnam(nam, "unknown signal: SIG%s",
 					 signame);
@@ -2713,14 +2790,15 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 					 signame);
 				returnval++;
 			    } else {
-				if (WIFSIGNALED(sig))
-				    sig = WTERMSIG(sig);
-				else if (WIFSTOPPED(sig))
-				    sig = WSTOPSIG(sig);
+				sig = status & ~0200;
 				if (1 <= sig && sig <= SIGCOUNT)
 				    printf("%s\n", sigs[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+				else if ((signame = rtsigname(sig, 0)))
+				    printf("%s\n", signame);
+#endif
 				else
-				    printf("%d\n", sig);
+				    printf("%d\n", status);
 			    }
 			}
 		    }
@@ -2729,10 +2807,45 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 		printf("%s", sigs[1]);
 		for (sig = 2; sig <= SIGCOUNT; sig++)
 		    printf(" %s", sigs[sig]);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+		for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+		    printf(" %s", rtsigname(sig, 0));
+#endif
 		putchar('\n');
 		return 0;
 	    }
 
+	    /* with argument "-L" list signals with their numbers in a table */
+	    if ((*argv)[1] == 'L' && (*argv)[2] == '\0') {
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+		const int width = SIGRTMAX >= 100 ? 3 : 2;
+#else
+		const int width = SIGCOUNT >= 100 ? 3 : 2;
+#endif
+		const int cols = zterm_columns >= 30 ?
+		    (zterm_columns < 90 ? zterm_columns / 15 : 6) : 1;
+
+		for (sig = 1; sig < SIGCOUNT
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+			+ 1
+#endif
+			; sig++)
+		{
+		    printf("%*d %-10s%c", width, sig, sigs[sig],
+			    sig % cols ? ' ' : '\n');
+		}
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+		for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) {
+		    printf("%*d %-10s%c", width, sig, rtsigname(sig, 0),
+			    (sig - SIGRTMIN + SIGCOUNT + 1) % cols ? ' ' : '\n');
+		}
+		printf("%*d RTMAX\n", width, sig);
+#else
+		printf("%*d %s\n", width, sig, sigs[sig]);
+#endif
+		return 0;
+	    }
+
     	    if ((*argv)[1] == 'n' && (*argv)[2] == '\0') {
 	    	char *endp;
 
@@ -2777,14 +2890,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 			    break;
 			}
 		}
-		if (sig > SIGCOUNT) {
+		if (sig > SIGCOUNT
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+			&& !(sig = rtsigno(signame))
+#endif
+			) {
 		    zwarnnam(nam, "unknown signal: SIG%s", signame);
-		    zwarnnam(nam, "type kill -l for a list of signals");
+		    zwarnnam(nam, "type kill -L for a list of signals");
 		    return 1;
 		}
 	    }
 	}
 	argv++;
+	got_sig = 1;
     }
 
     /* Discard the standard "-" and "--" option breaks */
@@ -2833,7 +2951,12 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    returnval++;
 	} else {
 	    int pid = atoi(*argv);
-	    if (kill(pid, sig) == -1) {
+	    if (
+#ifdef HAVE_SIGQUEUE
+		use_sigqueue ? sigqueue(pid, sig, sigqueue_info) :
+#endif
+		kill(pid, sig) == -1)
+	    {
 		zwarnnam("kill", "kill %s failed: %e", *argv, errno);
 		returnval++;
 	    } 
@@ -2854,18 +2977,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 
     return returnval < 126 ? returnval : 1;
 }
-/* Get a signal number from a string */
+
+/* Get index into table of traps from a string describing a signal */
 
 /**/
 mod_export int
-getsignum(const char *s)
+getsigidx(const char *s)
 {
     int x, i;
 
     /* check for a signal specified by number */
     x = atoi(s);
-    if (idigit(*s) && x >= 0 && x < VSIGCOUNT)
-	return x;
+    if (idigit(*s) && x >= 0)
+	return SIGIDX(x);
 
     /* search for signal by name */
     if (!strncmp(s, "SIG", 3))
@@ -2881,11 +3005,16 @@ getsignum(const char *s)
 	    return alt_sigs[i].num;
     }
 
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if ((x = rtsigno(s)))
+	return SIGIDX(x);
+#endif
+
     /* no matching signal */
     return -1;
 }
 
-/* Get the name for a signal. */
+/* Get the name for a signal given the index into the traps table. */
 
 /**/
 mod_export const char *
@@ -2899,6 +3028,11 @@ getsigname(int sig)
 		return alt_sigs[i].name;
     }
     else
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if (sig >= VSIGCOUNT)
+	return rtsigname(SIGNUM(sig), 0);
+    else
+#endif
 	return sigs[sig];
 
     /* shouldn't reach here */
@@ -2923,10 +3057,22 @@ gettrapnode(int sig, int ignoredisable)
     else
 	getptr = shfunctab->getnode;
 
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if (sig >= VSIGCOUNT)
+	sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 0));
+    else
+#endif
     sprintf(fname, "TRAP%s", sigs[sig]);
     if ((hn = getptr(shfunctab, fname)))
 	return hn;
 
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    if (sig >= VSIGCOUNT) {
+	sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 1));
+	return getptr(shfunctab, fname);
+    }
+#endif
+
     for (i = 0; alt_sigs[i].name; i++) {
 	if (alt_sigs[i].num == sig) {
 	    sprintf(fname, "TRAP%s", alt_sigs[i].name);
diff --git a/Src/lex.c b/Src/lex.c
index ece02659e..700af2da1 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -423,7 +423,7 @@ initlextabs(void)
     for (t0 = 0; lx2[t0]; t0++)
 	lexact2[(int)lx2[t0]] = t0;
     lexact2['&'] = LX2_BREAK;
-    lexact2[STOUC(Meta)] = LX2_META;
+    lexact2[(unsigned char) Meta] = LX2_META;
     lextok2['*'] = Star;
     lextok2['?'] = Quest;
     lextok2['{'] = Inbrace;
@@ -722,7 +722,7 @@ gettok(void)
 	}
 	return peek;
     }
-    switch (lexact1[STOUC(c)]) {
+    switch (lexact1[(unsigned char) c]) {
     case LX1_BKSLASH:
 	d = hgetc();
 	if (d == '\n')
@@ -937,8 +937,8 @@ static enum lextok
 gettokstr(int c, int sub)
 {
     int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0;
-    int intpos = 1, in_brace_param = 0;
-    int inquote, unmatched = 0;
+    int intpos = 1, in_brace_param = 0, cmdsubst = 0;
+    int inquote, unmatched = 0, in_pattern = 0;
     enum lextok peek;
 #ifdef DEBUG
     int ocmdsp = cmdsp;
@@ -960,8 +960,8 @@ gettokstr(int c, int sub)
 	if (inbl && !in_brace_param && !pct)
 	    act = LX2_BREAK;
 	else {
-	    act = lexact2[STOUC(c)];
-	    c = lextok2[STOUC(c)];
+	    act = lexact2[(unsigned char) c];
+	    c = lextok2[(unsigned char) c];
 	}
 	switch (act) {
 	case LX2_BREAK:
@@ -1135,7 +1135,7 @@ gettokstr(int c, int sub)
 	    c = Inpar;
 	    break;
 	case LX2_INBRACE:
-	    if (isset(IGNOREBRACES) || sub)
+	    if ((isset(IGNOREBRACES) && !cmdsubst) || sub)
 		c = '{';
 	    else {
 		if (!lexbuf.len && incmdpos) {
@@ -1157,8 +1157,11 @@ gettokstr(int c, int sub)
 	    if (in_brace_param) {
 		cmdpop();
 	    }
-	    if (bct-- == in_brace_param)
-		in_brace_param = 0;
+	    if (bct-- == in_brace_param) {
+		if (cmdsubst)
+		    cmdpop();
+		in_brace_param = cmdsubst = in_pattern = 0;
+	    }
 	    c = Outbrace;
 	    break;
 	case LX2_COMMA:
@@ -1230,7 +1233,7 @@ gettokstr(int c, int sub)
 		    else {
 			int sav = *lexbuf.ptr;
 			*lexbuf.ptr = '\0';
-			t = itype_end(t, IIDENT, 0);
+			t = itype_end(t, INAMESPC, 0);
 			if (t < lexbuf.ptr) {
 			    skipparens(Inbrack, Outbrack, &t);
 			} else {
@@ -1263,7 +1266,7 @@ gettokstr(int c, int sub)
 		    continue;
 	    } else {
 		add(Bnull);
-		if (c == STOUC(Meta)) {
+		if (c == (unsigned char) Meta) {
 		    c = hgetc();
 #ifdef DEBUG
 		    if (lexstop) {
@@ -1306,7 +1309,8 @@ gettokstr(int c, int sub)
 			    lexbuf.ptr--, lexbuf.len--;
 			else
 			    break;
-		    }
+		    } else if (in_pattern && c == '/')
+			add(Bnull);
 		    add(c);
 		}
 		ALLOWHIST
@@ -1394,27 +1398,45 @@ gettokstr(int c, int sub)
 	     */
 	    c = Dash;
            break;
-       case LX2_BANG:
-           /*
-            * Same logic as Dash, for ! to perform negation in range.
-            */
-           if (seen_brct)
-               c = Bang;
-           else
-               c = '!';
-       }
-       add(c);
-       c = hgetc();
+	case LX2_BANG:
+	    /*
+	     * Same logic as Dash, for ! to perform negation in range.
+	     */
+	    if (seen_brct)
+		c = Bang;
+	    else
+		c = '!';
+	case LX2_OTHER:
+	    if (in_brace_param) {
+		if (c == '/') {
+		    if (in_pattern == 0)
+			in_pattern = 2;
+		    else
+			--in_pattern;
+		}
+	    }
+	}
+	add(c);
+	c = hgetc();
 	if (intpos)
 	    intpos--;
 	if (lexstop)
 	    break;
+	if (!cmdsubst && in_brace_param && act == LX2_STRING &&
+	    (c == '|' || c == Bar || c == '{' || c == Inbrace || inblank(c))) {
+	    cmdsubst = in_brace_param;
+	    cmdpush(CS_CURSH);
+	} else if (in_pattern == 2 && c != '/')
+	    in_pattern = 1;
     }
   brk:
     if (errflag) {
 	if (in_brace_param) {
-	    while(bct-- >= in_brace_param)
+	    while(bct >= in_brace_param) {
+		if (bct-- == cmdsubst)
+		    cmdpop();
 		cmdpop();
+	    }
 	}
 	return LEXERR;
     }
@@ -1422,17 +1444,28 @@ gettokstr(int c, int sub)
     if (unmatched && !(lexflags & LEXFLAGS_ACTIVE))
 	zerr("unmatched %c", unmatched);
     if (in_brace_param) {
-	while(bct-- >= in_brace_param)
+	while(bct >= in_brace_param) {
+	    if (bct-- == cmdsubst)
+		cmdpop();
 	    cmdpop();
+	}
 	zerr("closing brace expected");
     } else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 &&
 	       peek == STRING && lexbuf.ptr[-1] == '}' &&
 	       lexbuf.ptr[-2] != Bnull) {
 	/* hack to get {foo} command syntax work */
+	/*
+	 * Alias expansion when parsing command substitution means that
+	 * the case for raw lexical analysis may not be the same.
+	 * (Just go with it, OK?)
+	 */
+	int lar = lex_add_raw;
+	lex_add_raw = lexbuf_raw.len > 0 && lexbuf_raw.ptr[-1] == '}';
 	lexbuf.ptr--;
 	lexbuf.len--;
 	lexstop = 0;
 	hungetc('}');
+	lex_add_raw = lar;
     }
     *lexbuf.ptr = '\0';
     DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed.");
@@ -1451,8 +1484,8 @@ gettokstr(int c, int sub)
 static int
 dquote_parse(char endchar, int sub)
 {
-    int pct = 0, brct = 0, bct = 0, intick = 0, err = 0;
-    int c;
+    int pct = 0, brct = 0, bct = 0, intick = 0, err = 0, cmdsubst = 0;
+    int c, bskip = 0;
     int math = endchar == ')' || endchar == ']' || infor;
     int zlemath = math && zlemetacs > zlemetall + addedx - inbufct;
 
@@ -1521,11 +1554,25 @@ dquote_parse(char endchar, int sub)
 		c = Qstring;
 	    }
 	    break;
+	case '{':
+	    if (cmdsubst && !intick) {
+		/* In nofork substitution, tokenize as if unquoted */
+		c = Inbrace;
+		bskip++;
+	    }
+	    break;
 	case '}':
 	    if (intick || !bct)
 		break;
 	    c = Outbrace;
-	    bct--;
+	    if (bskip) {
+		bskip--;
+		break;
+	    }
+	    if (bct-- == cmdsubst) {
+		cmdsubst = 0;
+		cmdpop();
+	    }
 	    cmdpop();
 	    break;
 	case '`':
@@ -1580,14 +1627,34 @@ dquote_parse(char endchar, int sub)
 	if (err || lexstop)
 	    break;
 	add(c);
+	if (!cmdsubst && c == Inbrace) {
+	    /* Check for ${|...} nofork command substitution */
+	    if ((c = hgetc()) && !lexstop) {
+		if (c == '|' || inblank(c)) {
+		    cmdsubst = bct;
+		    cmdpush(CS_CURSH);
+		}
+		hungetc(c);
+	    }
+	}
     }
     if (intick == 2)
 	ALLOWHIST
     if (intick) {
 	cmdpop();
     }
-    while (bct--)
+    while (bct) {
+	if (bct-- == cmdsubst) {
+	    /*
+	     * You would think this is an error, but if we call it one,
+	     * parsestrnoerr() returns nonzero to subst_parse_str() and
+	     * subsequently "bad substitution" is not reported
+	     */
+	    /* err = 1 */
+	    cmdpop();
+	}
 	cmdpop();
+    }
     if (lexstop)
 	err = intick || endchar || err;
     else if (err == 1) {
diff --git a/Src/loop.c b/Src/loop.c
index db5b3e097..0f3847541 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -53,7 +53,7 @@ execfor(Estate state, int do_exec)
     wordcode code = state->pc[-1];
     int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0;
     int last = 0;
-    char *name, *str, *cond = NULL, *advance = NULL;
+    char *str, *cond = NULL, *advance = NULL;
     zlong val = 0;
     LinkList vars = NULL, args = NULL;
     int old_simple_pline = simple_pline;
@@ -151,7 +151,7 @@ execfor(Estate state, int do_exec)
 	    int count = 0;
 	    for (node = firstnode(vars); node; incnode(node))
 	    {
-		name = (char *)getdata(node);
+		char *name = (char *)getdata(node);
 		if (!args || !(str = (char *) ugetnode(args)))
 		{
 		    if (count) { 
@@ -165,7 +165,7 @@ execfor(Estate state, int do_exec)
 		    fprintf(xtrerr, "%s=%s\n", name, str);
 		    fflush(xtrerr);
 		}
-		setsparam(name, ztrdup(str));
+		setloopvar(name, ztrdup(str));
 		count++;
 	    }
 	    if (!count)
@@ -282,7 +282,7 @@ execselect(Estate state, UNUSED(int do_exec))
 		    /* Keep any user interrupt error status */
 		    errflag = oef | (errflag & ERRFLAG_INT);
 	    	} else {
-		    str = promptexpand(prompt3, 0, NULL, NULL, NULL);
+		    str = promptexpand(prompt3, 0, NULL, NULL);
 		    zputs(str, stderr);
 		    free(str);
 		    fflush(stderr);
@@ -428,7 +428,7 @@ execwhile(Estate state, UNUSED(int do_exec))
     } else {
         for (;;) {
             state->pc = loop;
-            noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
+            noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN;
 
 	    /* In case the test condition is a functional no-op,
 	     * make sure signal handlers recognize ^C to end the loop. */
@@ -569,23 +569,14 @@ execif(Estate state, int do_exec)
 	s = 1;
 	state->pc = next;
     }
+    noerrexit = olderrexit;
 
     if (run) {
-	/* we need to ignore lastval until we reach execcmd() */
-	if (olderrexit || run == 2)
-	    noerrexit = olderrexit;
-	else if (lastval)
-	    noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC;
-	else
-	    noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN);
 	cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN));
 	execlist(state, 1, do_exec);
 	cmdpop();
-    } else {
-	noerrexit = olderrexit;
-	if (!retflag && !errflag)
-	    lastval = 0;
-    }
+    } else if (!retflag && !errflag)
+	lastval = 0;
     state->pc = end;
     this_noerrexit = 1;
 
@@ -793,6 +784,7 @@ exectry(Estate state, int do_exec)
     cmdpop();
     popheap();
     state->pc = end;
+    this_noerrexit = 1;
 
     return endval;
 }
diff --git a/Src/makepro.awk b/Src/makepro.awk
index f69660531..56c4f4595 100644
--- a/Src/makepro.awk
+++ b/Src/makepro.awk
@@ -121,7 +121,7 @@ BEGIN {
 		# initialiser.
 		dcltor = substr(line, 1, RLENGTH-1)
 		line = substr(line, RLENGTH+1)
-		sub(/=.*$/, "", dcltor)
+		sub(/\075.*$/, "", dcltor)
 		match(dcltor, /^([^_0-9A-Za-z]| const )*/)
 		dcltor = substr(dcltor, 1, RLENGTH) "@+" substr(dcltor, RLENGTH+1)
 		match(dcltor, /^.*@\+[_0-9A-Za-z]+/)
@@ -131,8 +131,8 @@ BEGIN {
 		sub(/@-.*$/, "", dnam)
 
 		# Put parens etc. back
-		gsub(/@[{]/, " _((", dcltor)
-		gsub(/@}/, "))", dcltor)
+		gsub(/@[{]/, " (", dcltor)
+		gsub(/@}/, ")", dcltor)
 		gsub(/@</, "(", dcltor)
 		gsub(/@>/, ")", dcltor)
 		gsub(/@!/, ",", dcltor)
diff --git a/Src/math.c b/Src/math.c
index 777ad9c31..d97dae238 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -352,6 +352,8 @@ getmathparam(struct mathvalue *mptr)
 	    }
 	    return zero_mnumber;
 	}
+	if (errflag)
+	    return zero_mnumber;
     }
     result = getnumvalue(mptr->pval);
     if (isset(FORCEFLOAT) && result.type == MN_INTEGER) {
@@ -641,7 +643,9 @@ zzlex(void)
 		return MINUSEQ;
 	    }
 	    if (unary) {
-		if (idigit(*ptr) || *ptr == '.') {
+		if (idigit(*ptr) ||
+		    (*ptr == '.' &&
+		     (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) {
 		    int ctype = lexconstant();
 		    if (ctype == NUM)
 		    {
@@ -835,7 +839,9 @@ zzlex(void)
 	case Dnull:
 	    break;
 	default:
-	    if (idigit(*--ptr) || *ptr == '.')
+	    if (idigit(*--ptr) ||
+		(*ptr == '.' &&
+		 (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0))))
 		return lexconstant();
 	    if (*ptr == '#') {
 		if (*++ptr == '\\' || *ptr == '#') {
@@ -857,7 +863,7 @@ zzlex(void)
 		}
 		cct = 1;
 	    }
-	    if ((ie = itype_end(ptr, IIDENT, 0)) != ptr) {
+	    if ((ie = itype_end(ptr, INAMESPC, 0)) != ptr) {
 		int func = 0;
 		char *p;
 
@@ -955,7 +961,7 @@ getcvar(char *s)
 	    }
 	}
 #endif
-	mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t);
+	mn.u.l = (unsigned char) (*t == Meta ? t[1] ^ 32 : *t);
     }
     unqueue_signals();
     return mn;
@@ -1363,8 +1369,11 @@ op(int what)
     }
 
     spval = &stack[sp].val;
-    if (stack[sp].val.type == MN_UNSET)
+    if (stack[sp].val.type == MN_UNSET) {
 	*spval = getmathparam(stack + sp);
+	if (errflag)
+	    return;
+    }
     switch (what) {
     case NOT:
 	if (spval->type & MN_FLOAT) {
@@ -1548,21 +1557,32 @@ checkunary(int mtokc, char *mptr)
 	    errmsg = 2;
     }
     if (errmsg) {
-	int len, over = 0;
+	int len = 0, over = 0;
 	char *errtype = errmsg == 2 ? "operator" : "operand";
 	while (inblank(*mptr))
 	    mptr++;
-	len = ztrlen(mptr);
-	if (len > 10) {
-	    len = 10;
-	    over = 1;
+	if (isset(MULTIBYTE))
+	    MB_CHARINIT();
+	while (over < 10 && mptr[len]) {
+	    if (isset(MULTIBYTE))
+		len += MB_METACHARLEN(mptr+len);
+	    else
+		len += (mptr[len] == Meta ? 2 : 1);
+	    ++over;
+	}
+	if ((over = mptr[len])) {
+	    mptr = dupstring(mptr);
+	    if (mptr[len] == Meta)
+		mptr[len+1] = 0;
+	    else
+		mptr[len] = 0;
 	}
 	if (!*mptr)
 	    zerr("bad math expression: %s expected at end of string",
 		errtype);
 	else
-	    zerr("bad math expression: %s expected at `%l%s'",
-		 errtype, mptr, len, over ? "..." : "");
+	    zerr("bad math expression: %s expected at `%s%s'",
+		 errtype, mptr, over ? "..." : "");
     }
     unary = !(tp & OP_OPF);
 }
diff --git a/Src/mem.c b/Src/mem.c
index fb4be47bf..0b6f76e46 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -1057,17 +1057,17 @@ zrealloc(void *ptr, size_t size)
 #if !defined(__hpux) && !defined(DGUX) && !defined(__osf__)
 # if defined(_BSD)
 #  ifndef HAVE_BRK_PROTO
-   extern int brk _((caddr_t));
+   extern int brk (caddr_t);
 #  endif
 #  ifndef HAVE_SBRK_PROTO
-   extern caddr_t sbrk _((int));
+   extern caddr_t sbrk (int);
 #  endif
 # else
 #  ifndef HAVE_BRK_PROTO
-   extern int brk _((void *));
+   extern int brk (void *);
 #  endif
 #  ifndef HAVE_SBRK_PROTO
-   extern void *sbrk _((int));
+   extern void *sbrk (int);
 #  endif
 # endif
 #endif
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index c4611d8b3..1994ace60 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -76,6 +76,30 @@ for x_mod in $x_mods; do
     test "x$linked" = xno && echo "#endif"
 done
 
+# if dynamic module 'mod' with load=no has moddeps in its .mdd,
+# then output add_dep(mod, dep) for each 'dep' in moddeps.
+dyn_mods="`grep ' link=dynamic .* load=no ' $CFMOD | \
+          sed -e '/^#/d' -e 's/ .*/ /' -e 's/^name=/ /'`"
+
+for mod in $dyn_mods; do
+    modfile="`grep '^name='$mod' ' $CFMOD | \
+              sed -e 's/^.* modfile=//' -e 's/ .*//'`"
+    if test "x$modfile" = x; then
+	echo >&2 "WARNING: no name for \`$mod' in $CFMOD (ignored)"
+	continue
+    fi
+    unset moddeps
+    . $srcdir/../$modfile
+    if test -n "$moddeps"; then
+        echo '#ifdef DYNAMIC'
+        echo "/* non-linked-in known module \`$mod' */"
+        for dep in $moddeps; do
+          echo "  add_dep(\"$mod\", \"$dep\");"
+        done
+        echo '#endif'
+    fi
+done
+
 echo
 done_mods=" "
 for bin_mod in $bin_mods; do
@@ -98,12 +122,12 @@ for bin_mod in $bin_mods; do
 	esac
     done
     echo "    {"
-    echo "        extern int setup_${q_bin_mod} _((Module));"
-    echo "        extern int boot_${q_bin_mod} _((Module));"
-    echo "        extern int features_${q_bin_mod} _((Module,char***));"
-    echo "        extern int enables_${q_bin_mod} _((Module,int**));"
-    echo "        extern int cleanup_${q_bin_mod} _((Module));"
-    echo "        extern int finish_${q_bin_mod} _((Module));"
+    echo "        extern int setup_${q_bin_mod} (Module);"
+    echo "        extern int boot_${q_bin_mod} (Module);"
+    echo "        extern int features_${q_bin_mod} (Module,char***);"
+    echo "        extern int enables_${q_bin_mod} (Module,int**);"
+    echo "        extern int cleanup_${q_bin_mod} (Module);"
+    echo "        extern int finish_${q_bin_mod} (Module);"
     echo
     echo "        register_module(\"$bin_mod\","
     echo "                        setup_${q_bin_mod},"
diff --git a/Src/modentry.c b/Src/modentry.c
index 4d8217f43..23c499d94 100644
--- a/Src/modentry.c
+++ b/Src/modentry.c
@@ -1,10 +1,10 @@
 #include "zsh.mdh"
 
-int setup_ _((Module));
-int boot_ _((Module));
-int cleanup_ _((Module));
-int finish_ _((Module));
-int modentry _((int boot, Module m, void *ptr));
+int setup_ (Module);
+int boot_ (Module);
+int cleanup_ (Module);
+int finish_ (Module);
+int modentry (int boot, Module m, void *ptr);
 
 /**/
 int
diff --git a/Src/module.c b/Src/module.c
index bab4d8d73..b4b5d0a2c 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -356,7 +356,7 @@ finish_(UNUSED(Module m))
 
 /**/
 void
-register_module(char *n, Module_void_func setup,
+register_module(const char *n, Module_void_func setup,
 		Module_features_func features,
 		Module_enables_func enables,
 		Module_void_func boot,
@@ -846,7 +846,7 @@ Hookdef hooktab;
 
 /**/
 Hookdef
-gethookdef(char *n)
+gethookdef(const char *n)
 {
     Hookdef p;
 
@@ -974,7 +974,7 @@ deletehookdeffunc(Hookdef h, Hookfn f)
 
 /**/
 mod_export int
-deletehookfunc(char *n, Hookfn f)
+deletehookfunc(const char *n, Hookfn f)
 {
     Hookdef h = gethookdef(n);
 
@@ -1198,6 +1198,7 @@ add_autoparam(const char *module, const char *pnam, int flags)
 {
     Param pm;
     int ret;
+    int ne = noerrs;
 
     queue_signals();
     if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) {
@@ -1212,14 +1213,18 @@ add_autoparam(const char *module, const char *pnam, int flags)
 	return ret == 2 ? 0 : -1;
     }
 
-    pm = setsparam(dupstring(pnam), ztrdup(module));
-
-    pm->node.flags |= PM_AUTOLOAD;
-    if (flags & FEAT_AUTOALL)
-	pm->node.flags |= PM_AUTOALL;
+    noerrs = 2;
+    if ((pm = setsparam(dupstring(pnam), ztrdup(module)))) {
+	pm->node.flags |= PM_AUTOLOAD;
+	if (flags & FEAT_AUTOALL)
+	    pm->node.flags |= PM_AUTOALL;
+	ret = 0;
+    } else
+	ret = -1;
+    noerrs = ne;
     unqueue_signals();
 
-    return 0;
+    return ret;
 }
 
 /* Remove a parameter added with add_autoparam() */
@@ -1761,7 +1766,7 @@ dyn_finish_module(Module m)
 #else
 
 static Module_generic_func
-module_func(Module m, char *name)
+module_func(Module m, const char *name)
 {
 #ifdef DYNAMIC_NAME_CLASH_OK
     return (Module_generic_func) dlsym(m->u.handle, name);
@@ -2438,7 +2443,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
     int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u');
     int ret = 1, autoopts;
     /* options only allowed with -F */
-    char *fonly = "lP", *fp;
+    const char *fonly = "lP", *fp;
 
     if (ops_bcpf && !ops_au) {
 	zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u");
@@ -2474,7 +2479,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func))
 	return 1;
     }
     for (fp = fonly; *fp; fp++) {
-	if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) {
+	if (OPT_ISSET(ops,(unsigned char) *fp) && !OPT_ISSET(ops,'F')) {
 	    zwarnnam(nam, "-%c is only allowed with -F", *fp);
 	    return 1;
 	}
@@ -3177,7 +3182,7 @@ bin_zmodload_features(const char *nam, char **args, Options ops)
 	} else if (OPT_ISSET(ops, 'L'))
 	    printf("zmodload -F %s ", m->node.nam);
 	for (fp = features, ep = enables; *fp; fp++, ep++) {
-	    char *onoff;
+	    const char *onoff;
 	    int term;
 	    if (*args) {
 		char **argp;
@@ -3447,7 +3452,8 @@ autofeatures(const char *cmdnam, const char *module, char **features,
 	defm = NULL;
 
     for (; *features; features++) {
-	char *fnam, *typnam, *feature;
+	char *fnam, *feature;
+	const char *typnam;
 	int add, fchar, flags = defflags;
 	autofeaturefn_t fn;
 
diff --git a/Src/options.c b/Src/options.c
index a1fe918fc..a0e1aa024 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -881,11 +881,12 @@ dosetopt(int optno, int value, int force, char *new_opts)
     } else if (!force && optno == MONITOR && value) {
 	if (new_opts[optno] == value)
 	    return 0;
-	if (SHTTY != -1) {
+	if (SHTTY == -1)
+	    return -1;
+	if (!origpgrp) {
 	    origpgrp = GETPGRP();
 	    acquire_pgrp();
-	} else
-	    return -1;
+	}
 #else
     } else if(optno == MONITOR && value) {
 	    return -1;
@@ -904,7 +905,11 @@ dosetopt(int optno, int value, int force, char *new_opts)
 	keyboardhackchar = (value ? '`' : '\0');
     }
     new_opts[optno] = value;
-    if (optno == BANGHIST || optno == SHINSTDIN)
+    if (
+#ifdef MULTIBYTE_SUPPORT
+	optno == MULTIBYTE ||
+#endif
+	optno == BANGHIST || optno == SHINSTDIN)
 	inittyptab();
     return 0;
 }
diff --git a/Src/params.c b/Src/params.c
index 27ea82298..f65aa1e80 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -475,6 +475,15 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \
 	((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \
 		 !(V)->pm->node.nam || !*(V)->pm->node.nam))
 
+/*
+ * For named references.  Simple named references are just like scalars
+ * for efficiency, but special named references need get/set functions.
+ */
+#define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ?	\
+			(PM)->gsu.s->getfn(PM) : (PM)->u.str)
+#define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ?		\
+			  (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S)))
+
 static Param argvparam;
 
 /* "parameter table" - hash table containing the parameters
@@ -520,7 +529,7 @@ getparamnode(HashTable ht, const char *nam)
     HashNode hn = gethashnode2(ht, nam);
     Param pm = (Param) hn;
 
-    if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) {
+    if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) {
 	char *mn = dupstring(pm->u.str);
 
 	(void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL :
@@ -536,6 +545,9 @@ getparamnode(HashTable ht, const char *nam)
 		 nam);
 	}
     }
+
+    if (hn && ht == realparamtab && !(hn->flags & PM_UNSET))
+	hn = resolve_nameref((Param)hn, NULL);
     return hn;
 }
 
@@ -732,7 +744,7 @@ split_env_string(char *env, char **name, char **value)
 
     tenv = strcpy(zhalloc(strlen(env) + 1), env);
     for (str = tenv; *str && *str != '='; str++) {
-	if (STOUC(*str) >= 128) {
+	if ((unsigned char) *str >= 128) {
 	    /*
 	     * We'll ignore environment variables with names not
 	     * from the portable character set since we don't
@@ -838,12 +850,11 @@ createparamtable(void)
     setsparam("HOST", ztrdup_metafy(hostnam));
     zfree(hostnam, 256);
 
-    setsparam("LOGNAME", ztrdup_metafy(
+    setsparam("LOGNAME",
 #ifndef DISABLE_DYNAMIC_NSS
-			(str = getlogin()) && *str ?  str :
+	      (str = getlogin()) && *str ? ztrdup_metafy(str) :
 #endif
-				cached_username
-			));
+	      ztrdup(cached_username));
 
 #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
     /* Copy the environment variables we are inheriting to dynamic *
@@ -935,8 +946,18 @@ createparamtable(void)
     setsparam("ZSH_ARGZERO", ztrdup(posixzero));
     setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
     setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
-    setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
-    for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
+    setaparam("signals", sigptr = zalloc((TRAPCOUNT + 1) * sizeof(char *)));
+    t = sigs;
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    while (t - sigs <= SIGCOUNT)
+	*sigptr++ = ztrdup_metafy(*t++);
+    {
+	int sig;
+	for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
+	    *sigptr++ = ztrdup_metafy(rtsigname(sig, 0));
+    }
+#endif
+    while ((*sigptr++ = ztrdup_metafy(*t++))) /* empty */ ;
 
     noerrs = 0;
 }
@@ -993,6 +1014,62 @@ createparam(char *name, int flags)
 			 gethashnode2(paramtab, name) :
 			 paramtab->getnode(paramtab, name));
 
+	if (oldpm && (oldpm->node.flags & PM_RO_BY_DESIGN)) {
+	    if (!(flags & PM_LOCAL)) {
+		/* Must call the API for namerefs and specials to work */
+		pm = (Param) paramtab->getnode2(paramtab, oldpm->node.nam);
+		if (!pm || ((pm->node.flags & PM_NAMEREF) &&
+			    pm->level != locallevel)) {
+		    zerr("%s: can't modify read-only parameter", name);
+		    return NULL;
+		}
+	    }
+	    /**
+	     * Implementation note: In the case of a readonly nameref,
+	     * the right thing might be to insert a new global into
+	     * the paramtab and point the local pm->old at it, rather
+	     * than error.  That is why gethashnode2() is called
+	     * first, to avoid skipping up the stack prematurely.
+	     **/
+	}
+
+	if (oldpm && !(flags & PM_NAMEREF) &&
+	    (oldpm->level == locallevel ?
+	     !(oldpm->node.flags & PM_RO_BY_DESIGN) : !(flags & PM_LOCAL)) &&
+	    (oldpm->node.flags & PM_NAMEREF) &&
+	    (oldpm = upscope(oldpm, oldpm->base))) {
+	    Param lastpm;
+	    struct asgment stop;
+	    stop.flags = PM_NAMEREF | (flags & PM_LOCAL);
+	    stop.name = oldpm->node.nam;
+	    stop.value.scalar = GETREFNAME(oldpm);
+	    lastpm = (Param)resolve_nameref(oldpm, &stop);
+	    if (lastpm) {
+		if (lastpm->node.flags & PM_NAMEREF) {
+		    char *refname = GETREFNAME(lastpm);
+		    if (refname && *refname) {
+			name = refname;
+			oldpm = NULL;
+		    } else {
+			if (!(lastpm->node.flags & PM_READONLY)) {
+			    if (flags) {
+				/* Only plain scalar assignment allowed */
+				zerr("%s: can't change type of named reference",
+				     name);	/* Differs from ksh93u+ */
+				return NULL;
+			    }
+			}
+			return lastpm;
+		    }
+		} else {
+		    /* nameref pointing to an unset local */
+		    DPUTS(!(lastpm->node.flags & PM_UNSET),
+			  "BUG: local parameter is not unset");
+		    oldpm = lastpm;
+		}
+	    }
+	}
+
 	DPUTS(oldpm && oldpm->level > locallevel,
 	      "BUG: old local parameter not deleted");
 	if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
@@ -1009,7 +1086,7 @@ createparam(char *name, int flags)
 		/* POSIXBUILTINS horror: we need to retain 'export' flags */
 		(isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) {
 		if (oldpm->node.flags & PM_RO_BY_DESIGN) {
-		    zerr("%s: can't change parameter attribute",
+		    zerr("%s: can't modify read-only parameter",
 			 name);
 		    return NULL;
 		}
@@ -1185,6 +1262,26 @@ isident(char *s)
     if (!*s)			/* empty string is definitely not valid */
 	return 0;
 
+    /* This partly duplicates code in itype_end(), but we need to
+     * distinguish the leading namespace at this point to check the
+     * correctness of the identifier that follows
+     */
+    if (*s == '.') {
+	if (idigit(s[1]))
+	    return 0;	/* Namespace must not start with a digit */
+	/* Reject identifiers beginning with a digit in namespaces.
+	 * Move this out below this block to also reject v.1x form.
+	 */
+	if ((ss = itype_end(s + (*s == '.'), IIDENT, 0))) {
+	    if (*ss == '.') {
+		if (!ss[1])
+		    return 0;
+		if (idigit(ss[1]))
+		    s = ss + 1;
+	    }
+	}
+    }
+
     if (idigit(*s)) {
 	/* If the first character is `s' is a digit, then all must be */
 	for (ss = ++s; *ss; ss++)
@@ -1192,7 +1289,7 @@ isident(char *s)
 		break;
     } else {
 	/* Find the first character in `s' not in the iident type table */
-	ss = itype_end(s, IIDENT, 0);
+	ss = itype_end(s, INAMESPC, 0);
     }
 
     /* If the next character is not [, then it is *
@@ -1262,7 +1359,6 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
     /* first parse any subscription flags */
     if (v->pm && (*s == '(' || *s == Inpar)) {
 	int escapes = 0;
-	int waste;
 	for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
 	    switch (*s) {
 	    case 'r':
@@ -1339,8 +1435,13 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
 		sav = *t;
 		*t = '\0';
 		s += arglen;
-		sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL)
-		    : dupstring(s);
+		if (escapes) {
+		    int len;
+		    sep = getkeystring(s, &len, GETKEYS_SEP, NULL);
+		    sep = metafy(sep, len, META_HREALLOC);
+		}
+		else
+		    sep = dupstring(s);
 		*t = sav;
 		s = t + arglen - 1;
 		break;
@@ -1432,10 +1533,14 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
     if (ishash && (keymatch || !rev))
 	remnulargs(s);
     if (needtok) {
+	char exe = opts[EXECOPT];
 	s = dupstring(s);
 	if (parsestr(&s))
 	    return 0;
+	if (flags & SCANPM_NOEXEC)
+	    opts[EXECOPT] = 0;
 	singsub(&s);
+	opts[EXECOPT] = exe;
     } else if (rev)
 	remnulargs(s);	/* This is probably always a no-op, but ... */
     if (!rev) {
@@ -1665,7 +1770,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
 	    /* Searching characters */
 	    int slen;
 	    d = getstrvalue(v);
-	    if (!d || !*d)
+	    if (!d)
 		return 0;
 	    /*
 	     * beg and len are character counts, not raw offsets.
@@ -2047,6 +2152,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
     char *s, *t, *ie;
     char sav, c;
     int ppar = 0;
+    int itype = (flags & SCANPM_NONAMESPC) ? IIDENT : INAMESPC;
 
     s = t = *pptr;
 
@@ -2056,7 +2162,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	else
 	    ppar = *s++ - '0';
     }
-    else if ((ie = itype_end(s, IIDENT, 0)) != s)
+    else if ((ie = itype_end(s, itype, 0)) != s)
 	s = ie;
     else if (c == Quest)
 	*s++ = '?';
@@ -2094,8 +2200,16 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	int isvarat;
 
         isvarat = (t[0] == '@' && !t[1]);
-	pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
-	if (sav)
+	if (flags & SCANPM_NONAMEREF)
+	    pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t);
+	else
+	    pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
+	if (!pm && *t == '.' && !isident(t)) {
+	    /* badly formed namespace reference */
+	    if (sav)
+		*s = sav;
+	    return NULL;
+	} else if (sav)
 	    *s = sav;
 	*pptr = s;
 	if (!pm || ((pm->node.flags & PM_UNSET) &&
@@ -2105,6 +2219,29 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	    memset(v, 0, sizeof(*v));
 	else
 	    v = (Value) hcalloc(sizeof *v);
+	if ((pm->node.flags & PM_NAMEREF) && !(flags & SCANPM_NONAMEREF)) {
+	    char *refname = GETREFNAME(pm);
+	    if (refname && *refname) {
+		/* only happens for namerefs pointing to array elements */
+		char *ref = dupstring(refname);
+		char *ss = pm->width ? ref + pm->width : NULL;
+		if (ss) {
+		    sav = *ss;
+		    *ss = 0;
+		}
+		Param p1 = (Param)gethashnode2(paramtab, ref);
+		if (!(p1 && (pm = upscope(p1, pm->base))) ||
+		    ((pm->node.flags & PM_UNSET) &&
+		     !(pm->node.flags & PM_DECLARED)))
+		    return NULL;
+		if (ss) {
+		    flags |= SCANPM_NOEXEC;
+		    *ss = sav;
+		    s = dyncat(ss,*pptr);
+		} else
+		    s = *pptr;
+	    }
+	}
 	if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) {
 	    /* Overload v->isarr as the flag bits for hashed arrays. */
 	    v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0);
@@ -2124,7 +2261,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 		return v;
 	    }
 	} else if (!(flags & SCANPM_ASSIGNING) && v->isarr &&
-		   itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS))
+		   itype_end(t, INAMESPC, 1) != t && isset(KSHARRAYS))
 	    v->end = 1, v->isarr = 0;
     }
     if (!bracks && *s)
@@ -2673,6 +2810,7 @@ assignstrvalue(Value v, char *val, int flags)
         }
 	break;
     }
+    setscope(v->pm);
     if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) &&
 	 !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) ||
 	(v->pm->node.flags & PM_ARRAY) || v->pm->ename)
@@ -3008,7 +3146,7 @@ check_warn_pm(Param pm, const char *pmtype, int created,
     } else
 	return;
 
-    if (pm->node.flags & PM_SPECIAL)
+    if (pm->node.flags & (PM_SPECIAL|PM_NAMEREF))
 	return;
 
     for (i = funcstack; i; i = i->prev) {
@@ -3080,11 +3218,20 @@ assignsparam(char *s, char *val, int flags)
 	}
     }
     if (!v && !(v = getvalue(&vbuf, &t, 1))) {
-	unqueue_signals();
 	zsfree(val);
+	unqueue_signals();
 	/* errflag |= ERRFLAG_ERROR; */
 	return NULL;
     }
+    if (*val && (v->pm->node.flags & PM_NAMEREF)) {
+	if (!valid_refname(val)) {
+	    zerr("invalid variable name: %s", val);
+	    zsfree(val);
+	    unqueue_signals();
+	    errflag |= ERRFLAG_ERROR;
+	    return NULL;
+	}
+    }
     if (flags & ASSPM_WARN)
 	check_warn_pm(v->pm, "scalar", created, 1);
     v->pm->node.flags &= ~PM_DEFAULTED;
@@ -3111,8 +3258,8 @@ assignsparam(char *s, char *val, int flags)
 			lhs.u.l = lhs.u.l + (zlong)rhs.u.d;
 		}
 		setnumvalue(v, lhs);
-    	    	unqueue_signals();
 		zsfree(val);
+    	    	unqueue_signals();
 		return v->pm; /* avoid later setstrvalue() call */
 	    case PM_ARRAY:
 	    	if (unset(KSHARRAYS)) {
@@ -3137,9 +3284,9 @@ assignsparam(char *s, char *val, int flags)
 	    case PM_INTEGER:
 	    case PM_EFLOAT:
 	    case PM_FFLOAT:
+		zsfree(val);
 		unqueue_signals();
 		zerr("attempt to add to slice of a numeric variable");
-		zsfree(val);
 		return NULL;
 	    case PM_ARRAY:
 	      kshappend:
@@ -3214,7 +3361,7 @@ assignaparam(char *s, char **val, int flags)
 	} else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
 		 !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) {
 	    int uniq = v->pm->node.flags & PM_UNIQUE;
-	    if (flags & ASSPM_AUGMENT) {
+	    if ((flags & ASSPM_AUGMENT) && !(v->pm->node.flags & PM_UNSET)) {
 	    	/* insert old value at the beginning of the val array */
 		char **new;
 		int lv = arrlen(val);
@@ -3505,9 +3652,18 @@ assignnparam(char *s, mnumber val, int flags)
 	pm = createparam(t, ss ? PM_ARRAY :
 			 isset(POSIXIDENTIFIERS) ? PM_SCALAR :
 			 (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT);
-	if (!pm)
-	    pm = (Param) paramtab->getnode(paramtab, t);
-	DPUTS(!pm, "BUG: parameter not created");
+	if (errflag) {
+	    /* assume error message already output */
+	    unqueue_signals();
+	    return NULL;
+	}
+	if (!pm && !(pm = (Param) paramtab->getnode(paramtab, t))) {
+	    DPUTS(!pm, "BUG: parameter not created");
+	    if (!errflag)
+		zerr("%s: parameter not found", t);
+	    unqueue_signals();
+	    return NULL;
+	}
 	if (ss) {
 	    *ss = '[';
 	} else if (val.type & MN_INTEGER) {
@@ -3578,7 +3734,7 @@ mod_export Param
 setiparam_no_convert(char *s, zlong val)
 {
     /*
-     * If the target is already an integer, thisgets converted
+     * If the target is already an integer, this gets converted
      * back.  Low technology rules.
      */
     char buf[BDIGBUFSIZE];
@@ -3598,7 +3754,8 @@ unsetparam(char *s)
     if ((pm = (Param) (paramtab == realparamtab ?
 		       /* getnode2() to avoid autoloading */
 		       paramtab->getnode2(paramtab, s) :
-		       paramtab->getnode(paramtab, s))))
+		       paramtab->getnode(paramtab, s))) &&
+	!(pm->node.flags & PM_NAMEREF))
 	unsetparam_pm(pm, 0, 1);
     unqueue_signals();
 }
@@ -3617,7 +3774,9 @@ unsetparam_pm(Param pm, int altflag, int exp)
     char *altremove;
 
     if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) {
-	zerr("read-only variable: %s", pm->node.nam);
+	zerr("read-only %s: %s",
+	     (pm->node.flags & PM_NAMEREF) ? "reference" : "variable",
+	     pm->node.nam);
 	return 1;
     }
     if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
@@ -3660,12 +3819,15 @@ unsetparam_pm(Param pm, int altflag, int exp)
 		/* fudge things so removenode isn't called */
 		altpm->level = 1;
 	    }
-	    unsetparam_pm(altpm, 1, exp);
+	    unsetparam_pm(altpm, 1, exp); /* This resets pm to empty */
+	    pm->node.flags |= PM_UNSET;   /* so we must repeat this */
 	}
 
 	zsfree(altremove);
-	if (!(pm->node.flags & PM_SPECIAL))
+	if (!(pm->node.flags & PM_SPECIAL)) {
 	    pm->gsu.s = &stdscalar_gsu;
+	    pm->node.flags &= ~PM_ARRAY;
+	}
     }
 
     /*
@@ -4119,7 +4281,8 @@ char *
 tiedarrgetfn(Param pm)
 {
     struct tieddata *dptr = (struct tieddata *)pm->u.data;
-    return *dptr->arrptr ? zjoin(*dptr->arrptr, STOUC(dptr->joinchar), 1) : "";
+    return *dptr->arrptr ?
+	    zjoin(*dptr->arrptr, (unsigned char) dptr->joinchar, 1) : "";
 }
 
 /**/
@@ -4446,7 +4609,7 @@ usernamesetfn(UNUSED(Param pm), char *x)
 	    zwarn("failed to change user ID: %e", errno);
 	else {
 	    zsfree(cached_username);
-	    cached_username = ztrdup(pswd->pw_name);
+	    cached_username = ztrdup_metafy(pswd->pw_name);
 	    cached_uid = pswd->pw_uid;
 	}
     }
@@ -4635,6 +4798,7 @@ setlang(char *x)
 	if ((x = getsparam_u(ln->name)) && *x)
 	    setlocale(ln->category, x);
     unqueue_signals();
+    inittyptab();
 }
 
 /**/
@@ -4658,6 +4822,7 @@ lc_allsetfn(Param pm, char *x)
     else {
 	setlocale(LC_ALL, unmeta(x));
 	clear_mbstate();
+	inittyptab();
     }
 }
 
@@ -4696,6 +4861,7 @@ lcsetfn(Param pm, char *x)
     }
     unqueue_signals();
     clear_mbstate();	/* LC_CTYPE may have changed */
+    inittyptab();
 }
 #endif /* USE_LOCALE */
 
@@ -4815,12 +4981,12 @@ keyboardhacksetfn(UNUSED(Param pm), char *x)
 	    zwarn("Only one KEYBOARD_HACK character can be defined");  /* could be changed if needed */
 	}
 	for (i = 0; i < len; i++) {
-	    if (!isascii(STOUC(x[i]))) {
+	    if (!isascii((unsigned char) x[i])) {
 		zwarn("KEYBOARD_HACK can only contain ASCII characters");
 		return;
 	    }
 	}
-	keyboardhackchar = len ? STOUC(x[0]) : '\0';
+	keyboardhackchar = len ? (unsigned char) x[0] : '\0';
 	free(x);
     } else
 	keyboardhackchar = '\0';
@@ -4854,14 +5020,14 @@ histcharssetfn(UNUSED(Param pm), char *x)
 	if (len > 3)
 	    len = 3;
 	for (i = 0; i < len; i++) {
-	    if (!isascii(STOUC(x[i]))) {
+	    if (!isascii((unsigned char) x[i])) {
 		zwarn("HISTCHARS can only contain ASCII characters");
 		return;
 	    }
 	}
-	bangchar = len ? STOUC(x[0]) : '\0';
-	hatchar =  len > 1 ? STOUC(x[1]) : '\0';
-	hashchar = len > 2 ? STOUC(x[2]) : '\0';
+	bangchar = len ? (unsigned char) x[0] : '\0';
+	hatchar =  len > 1 ? (unsigned char) x[1] : '\0';
+	hashchar = len > 2 ? (unsigned char) x[2] : '\0';
 	free(x);
     } else {
 	bangchar = '!';
@@ -5083,7 +5249,7 @@ arrfixenv(char *s, char **t)
     if (pm->node.flags & PM_SPECIAL)
 	joinchar = ':';
     else
-	joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar);
+	joinchar = (unsigned char) ((struct tieddata *)pm->u.data)->joinchar;
 
     addenv(pm, t ? zjoin(t, joinchar, 1) : "");
 }
@@ -5105,9 +5271,9 @@ zputenv(char *str)
     char *ptr;
     int ret;
 
-    for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++)
+    for (ptr = str; *ptr && (unsigned char) *ptr < 128 && *ptr != '='; ptr++)
 	;
-    if (STOUC(*ptr) >= 128) {
+    if ((unsigned char) *ptr >= 128) {
 	/*
 	 * Environment variables not in the portable character
 	 * set are non-standard and we don't really know of
@@ -5718,7 +5884,8 @@ scanendscope(HashNode hn, UNUSED(int flags))
 		export_param(pm);
 	} else
 	    unsetparam_pm(pm, 0, 0);
-    }
+    } else if ((pm->node.flags & PM_NAMEREF) && pm->base > pm->level)
+	pm->base = locallevel;
 }
 
 
@@ -5769,6 +5936,7 @@ static const struct paramtypes pmtypes[] = {
     { PM_ARRAY, "array", 'a', 0},
     { PM_HASHED, "association", 'A', 0},
     { 0, "local", 0, PMTF_TEST_LEVEL},
+    { PM_HIDE, "hide", 'h', 0 },
     { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH},
     { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH},
     { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH},
@@ -5778,7 +5946,8 @@ static const struct paramtypes pmtypes[] = {
     { PM_TAGGED, "tagged", 't', 0},
     { PM_EXPORTED, "exported", 'x', 0},
     { PM_UNIQUE, "unique", 'U', 0},
-    { PM_TIED, "tied", 'T', 0}
+    { PM_TIED, "tied", 'T', 0},
+    { PM_NAMEREF, "nameref", 'n', 0}
 };
 
 #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes)))
@@ -5875,7 +6044,12 @@ printparamnode(HashNode hn, int printflags)
 {
     Param p = (Param) hn;
     Param peer = NULL;
+    int altname = 0;
 
+    if (!(p->node.flags & PM_HASHELEM) &&
+	!(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.')
+	return;
+    
     if (p->node.flags & PM_UNSET) {
 	if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
 	     p->node.flags & (PM_READONLY|PM_EXPORTED)) ||
@@ -5893,13 +6067,21 @@ printparamnode(HashNode hn, int printflags)
 	printflags |= PRINT_NAMEONLY;
 
     if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) {
-	if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) {
+	if (p->node.flags & PM_AUTOLOAD) {
 	    /*
 	     * It's not possible to restore the state of
 	     * these, so don't output.
 	     */
 	    return;
 	}
+	if (p->node.flags & PM_RO_BY_DESIGN) {
+	    /*
+	     * Compromise: cannot be restored out of context,
+	     * but show anyway if printed in scope of declaration
+	     */
+	    if (p->level != locallevel || p->level == 0)
+		return;
+	}
 	/*
 	 * The zsh variants of export -p/readonly -p also report other
 	 * flags to indicate other attributes or scope. The POSIX variants
@@ -5908,16 +6090,26 @@ printparamnode(HashNode hn, int printflags)
 	if (printflags & PRINT_POSIX_EXPORT) {
 	    if (!(p->node.flags & PM_EXPORTED))
 		return;
+	    altname = 'x';
 	    printf("export ");
 	} else if (printflags & PRINT_POSIX_READONLY) {
 	    if (!(p->node.flags & PM_READONLY))
 		return;
+	    altname = 'r';
 	    printf("readonly ");
-	} else if (locallevel && p->level >= locallevel) {
-	    printf("typeset ");	    /* printf("local "); */
 	} else if ((p->node.flags & PM_EXPORTED) &&
 		   !(p->node.flags & (PM_ARRAY|PM_HASHED))) {
-	    printf("export ");
+	  if (p->level && p->level >= locallevel)
+		printf("local ");
+	    else {
+		altname = 'x';
+		printf("export ");
+	    }
+	} else if (locallevel && p->level >= locallevel) {
+	    if (p->node.flags & PM_EXPORTED)
+		printf("local ");
+	    else
+		printf("typeset ");	    /* printf("local "); */
 	} else if (locallevel) {
 	    printf("typeset -g ");
 	} else
@@ -5931,9 +6123,24 @@ printparamnode(HashNode hn, int printflags)
 
 	for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) {
 	    int doprint = 0;
+
+	    if (altname && altname == pmptr->typeflag)
+		continue;
+
 	    if (pmptr->flags & PMTF_TEST_LEVEL) {
-		if (p->level)
+		if (p->level) {
+		    /*
+		    if ((p->node.flags & PM_SPECIAL) &&
+			(p->node.flags & PM_LOCAL) &&
+			!(p->node.flags & PM_HIDE)) {
+			if (doneminus)
+			    putchar(' ');
+			printf("+h ");
+			doneminus = 0;
+		    }
+		    */
 		    doprint = 1;
+		}
 	    } else if ((pmptr->binflag != PM_EXPORTED || p->level ||
 			(p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) &&
 		       (p->node.flags & pmptr->binflag))
@@ -6018,7 +6225,7 @@ printparamnode(HashNode hn, int printflags)
 	 * append the join char for tied parameters if different from colon
 	 * for typeset -p output.
 	 */
-	unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar);
+	unsigned char joinchar = (unsigned char) ((struct tieddata *)peer->u.data)->joinchar;
 	if (joinchar != ':') {
 	    char buf[2];
 	    buf[0] = joinchar;
@@ -6032,3 +6239,220 @@ printparamnode(HashNode hn, int printflags)
     else if (!(printflags & PRINT_KV_PAIR))
 	putchar('\n');
 }
+
+/**/
+mod_export HashNode
+resolve_nameref(Param pm, const Asgment stop)
+{
+    HashNode hn = (HashNode)pm;
+    const char *seek = stop ? stop->value.scalar : NULL;
+
+    if (pm && (pm->node.flags & PM_NAMEREF)) {
+	char *refname = GETREFNAME(pm);
+	if (pm->node.flags & (PM_UNSET|PM_TAGGED)) {
+	    /* Semaphore with createparam() */
+	    pm->node.flags &= ~PM_UNSET;
+	    if (pm->node.flags & PM_NEWREF)	/* See setloopvar() */
+		return NULL;
+	    if (refname && *refname && (pm->node.flags & PM_TAGGED))
+		pm->node.flags |= PM_SELFREF;	/* See setscope() */
+	    return (HashNode) pm;
+	} else if (refname) {
+	    if ((pm->node.flags & PM_TAGGED) ||
+		(stop && strcmp(refname, stop->name) == 0)) {
+		/* zwarnnam(refname, "invalid self reference"); */
+		return stop ? (HashNode)pm : NULL;
+	    }
+	    if (*refname)
+		seek = refname;
+	}
+    }
+    else if (pm) {
+	if (!(stop && (stop->flags & PM_NAMEREF)))
+	    return (HashNode)pm;
+	if (!(pm->node.flags & PM_NAMEREF))
+	    return (pm->level < locallevel ? NULL : (HashNode)pm);
+    }
+    if (seek) {
+	queue_signals();
+	/* pm->width is the offset of any subscript */
+	if (pm && (pm->node.flags & PM_NAMEREF) && pm->width) {
+	    if (stop) {
+		if (stop->flags & PM_NAMEREF)
+		    hn = (HashNode)pm;
+		else
+		    hn = NULL;
+	    } else {
+		/* this has to be the end of any chain */
+		hn = (HashNode)pm;	/* see fetchvalue() */
+	    }
+	} else if ((hn = gethashnode2(realparamtab, seek))) {
+	    if (pm) {
+		if (!(stop && (stop->flags & (PM_LOCAL)))) {
+		    int scope = ((pm->node.flags & PM_NAMEREF) ?
+				 ((pm->node.flags & PM_UPPER) ? -1 :
+				  pm->base) : ((Param)hn)->level);
+		    hn = (HashNode)upscope((Param)hn, scope);
+		}
+		/* user can't tag a nameref, safe for loop detection */
+		pm->node.flags |= PM_TAGGED;
+	    }
+	    if (hn) {
+		if (hn->flags & PM_AUTOLOAD)
+		    hn = getparamnode(realparamtab, seek);
+		if (!(hn->flags & PM_UNSET))
+		    hn = resolve_nameref((Param)hn, stop);
+	    }
+	    if (pm)
+		pm->node.flags &= ~PM_TAGGED;
+	} else if (stop && (stop->flags & PM_NAMEREF))
+	    hn = (pm && (pm->node.flags & PM_NEWREF)) ? NULL : (HashNode)pm;
+	unqueue_signals();
+    }
+
+    return hn;
+}
+
+/**/
+mod_export void
+setloopvar(char *name, char *value)
+{
+  Param pm = (Param) gethashnode2(realparamtab, name);
+
+  if (pm && (pm->node.flags & PM_NAMEREF)) {
+      if (pm->node.flags & PM_READONLY) {
+	  /* Bash error is: "%s: readonly variable" */
+	  zerr("read-only reference: %s", pm->node.nam);
+	  return;
+      }
+      pm->base = pm->width = 0;
+      SETREFNAME(pm, ztrdup(value));
+      pm->node.flags &= ~PM_UNSET;
+      pm->node.flags |= PM_NEWREF;
+      setscope(pm);
+      pm->node.flags &= ~PM_NEWREF;
+  } else
+      setsparam(name, value);
+}
+
+/**/
+static void
+setscope(Param pm)
+{
+    queue_signals();
+    if (pm->node.flags & PM_NAMEREF) do {
+	Param basepm;
+	struct asgment stop;
+	char *refname = GETREFNAME(pm);
+	char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL;
+	int q = queue_signal_level();
+
+	/* Temporarily change nameref to array parameter itself */
+	if (t && *t == '[')
+	    *t = 0;
+	else
+	    t = 0;
+	stop.name = "";
+	stop.value.scalar = NULL;
+	stop.flags = PM_NAMEREF;
+	if (locallevel && !(pm->node.flags & PM_UPPER))
+	    stop.flags |= PM_LOCAL;
+	dont_queue_signals();	/* Prevent unkillable loops */
+	basepm = (Param)resolve_nameref(pm, &stop);
+	restore_queue_signals(q);
+	if (t) {
+	    pm->width = t - refname;
+	    *t = '[';
+	}
+	if (basepm) {
+	    if (basepm->node.flags & PM_NAMEREF) {
+		if (pm == basepm) {
+		    if (pm->node.flags & PM_SELFREF) {
+			/* Loop signalled by resolve_nameref() */
+			if (upscope(pm, pm->base) == pm) {
+			    zerr("%s: invalid self reference", refname);
+			    unsetparam_pm(pm, 0, 1);
+			    break;
+			}
+			pm->node.flags &= ~PM_SELFREF;
+		    } else if (pm->base == pm->level) {
+			if (refname && *refname &&
+			    strcmp(pm->node.nam, refname) == 0) {
+			    zerr("%s: invalid self reference", refname);
+			    unsetparam_pm(pm, 0, 1);
+			    break;
+			}
+		    }
+		} else if ((t = GETREFNAME(basepm))) {
+		    if (basepm->base <= basepm->level &&
+			strcmp(pm->node.nam, t) == 0) {
+			zerr("%s: invalid self reference", refname);
+			unsetparam_pm(pm, 0, 1);
+			break;
+		    }
+		}
+	    } else
+		pm->base = basepm->level;
+	}
+	if (pm->base > pm->level) {
+	    if (EMULATION(EMULATE_KSH)) {
+		zerr("%s: global reference cannot refer to local variable",
+		      pm->node.nam);
+		unsetparam_pm(pm, 0, 1);
+	    } else if (isset(WARNNESTEDVAR))
+		zwarn("reference %s in enclosing scope set to local variable %s",
+		      pm->node.nam, refname);
+	}
+	if (refname && upscope(pm, pm->base) == pm &&
+	    strcmp(pm->node.nam, refname) == 0) {
+	    zerr("%s: invalid self reference", refname);
+	    unsetparam_pm(pm, 0, 1);
+	}
+    } while (0);
+    unqueue_signals();
+}
+
+/**/
+mod_export Param
+upscope(Param pm, int reflevel)
+{
+    Param up = pm->old;
+    while (up && up->level >= reflevel) {
+	pm = up;
+	up = up->old;
+    }
+    if (reflevel < 0 && locallevel > 0)
+	return pm->level == locallevel ? up : pm;
+    return pm;
+}
+
+/**/
+mod_export int
+valid_refname(char *val)
+{
+    char *t = itype_end(val, INAMESPC, 0);
+
+    if (idigit(*val))
+	return 0;
+    if (*t != 0) {
+	if (*t == '[') {
+	    tokenize(t = dupstring(t+1));
+	    while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) {
+		if (*t == Inbrack)
+		    ++t;
+		else
+		    break;
+	    }
+	    if (t && *t) {
+		/* zwarn("%s: stuff after subscript: %s", val, t); */
+		t = NULL;
+	    }
+	} else if (t[1] || !(*t == '!' || *t == '?' ||
+			     *t == '$' || *t == '-' ||
+			     *t == '0' || *t == '_')) {
+	    /* Skipping * @ # because of doshfunc() implementation */
+	    t = NULL;
+	}
+    }
+    return !!t;
+}
diff --git a/Src/parse.c b/Src/parse.c
index 5054e59d5..334365649 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -120,7 +120,7 @@ struct heredocs *hdocs;
  *     - if not (type & Z_END), followed by next WC_LIST
  *
  *   WC_SUBLIST
- *     - data contains type (&&, ||, END) and flags (coprog, not)
+ *     - data contains type (&&, ||, END) and flags (coproc, not)
  *     - followed by code for sublist
  *     - if not (type == END), followed by next WC_SUBLIST
  *
@@ -433,9 +433,9 @@ ecstrcode(char *s)
 	t = has_token(s);
 	wordcode c = (t ? 3 : 2);
 	switch (l) {
-	case 4: c |= ((wordcode) STOUC(s[2])) << 19;
-	case 3: c |= ((wordcode) STOUC(s[1])) << 11;
-	case 2: c |= ((wordcode) STOUC(s[0])) <<  3; break;
+	case 4: c |= ((wordcode) (unsigned char) s[2]) << 19;
+	case 3: c |= ((wordcode) (unsigned char) s[1]) << 11;
+	case 2: c |= ((wordcode) (unsigned char) s[0]) <<  3; break;
 	case 1: c = (t ? 7 : 6); break;
 	}
 	return c;
@@ -601,7 +601,7 @@ clear_hdocs(void)
  *			| sublist [ SEPER | AMPER | AMPERBANG ]
  *
  * cmdsubst indicates our event is part of a command-style
- * substitution terminated by the token indicationg, usual closing
+ * substitution terminated by the token indicated, usually closing
  * parenthesis.  In other cases endtok is ENDINPUT.
  */
 
@@ -1935,6 +1935,8 @@ par_simple(int *cmplx, int nr)
 
 		if (*ptr == Outbrace && ptr > tokstr + 1)
 		{
+		    /* Should we allow namespace FDs, {.foo.bar}>&file ? *
+		     * If so, change IIDENT to INAMESPC here             */
 		    if (itype_end(tokstr+1, IIDENT, 0) >= ptr)
 		    {
 			char *toksave = tokstr;
@@ -2055,6 +2057,9 @@ par_simple(int *cmplx, int nr)
 	    if (isset(EXECOPT) && hasalias && !isset(ALIASFUNCDEF) && argc &&
 		hasalias != input_hasalias()) {
 		zwarn("defining function based on alias `%s'", hasalias);
+		herrflush();
+		if (noerrs != 2)
+		    errflag |= ERRFLAG_ERROR;
 		YYERROR(oecused);
 	    }
 
@@ -2387,7 +2392,7 @@ par_nl_wordlist(void)
  */
 
 /**/
-void (*condlex) _((void)) = zshlex;
+void (*condlex) (void) = zshlex;
 
 /*
  * cond	: cond_1 { SEPER } [ DBAR { SEPER } cond ]
@@ -2725,11 +2730,10 @@ yyerror(int noerr)
 	if (!t || !t[t0] || t[t0] == '\n')
 	    break;
     if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) {
-	if (t0 == 20)
-	    zwarn("parse error near `%l...'", t, 20);
-	else if (t0)
-	    zwarn("parse error near `%l'", t, t0);
-	else
+	if (t0) {
+	    t = metafy(t, t0, META_STATIC);
+	    zwarn("parse error near `%s%s'", t, t0 == 20 ? "..." : "");
+	} else
 	    zwarn("parse error");
     }
     if (!noerr && noerrs != 2)
@@ -3214,12 +3218,14 @@ bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func))
 
     if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) {
 	queue_signals();
-	ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'),
+	dump = unmetafy(dyncat(*args, FD_EXT), NULL);
+	ret = build_dump(nam, dump, args, OPT_ISSET(ops,'U'),
 			 map, flags);
 	unqueue_signals();
 	return ret;
     }
-    dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT));
+    dump = (strsfx(FD_EXT, *args) ? dupstring(*args) : dyncat(*args, FD_EXT));
+    unmetafy(dump, NULL);
 
     queue_signals();
     ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ?
@@ -3397,6 +3403,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
 
     for (hlen = FD_PRELEN, tlen = 0; *files; files++) {
 	struct stat st;
+	char *fnam;
 
 	if (check_cond(*files, "k")) {
 	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD;
@@ -3405,7 +3412,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
 	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD;
 	    continue;
 	}
-	if ((fd = open(*files, O_RDONLY)) < 0 ||
+	fnam = unmeta(*files);
+	if ((fd = open(fnam, O_RDONLY)) < 0 ||
 	    fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) ||
 	    (flen = lseek(fd, 0, 2)) == -1) {
 	    if (fd >= 0)
diff --git a/Src/pattern.c b/Src/pattern.c
index e947d1216..1e0ae88d9 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -239,7 +239,7 @@ typedef unsigned long zrange_t;
  * a bit tricky...
  */
 #define WCHAR_INVALID(ch)			\
-    ((wchar_t) (0xDC00 + STOUC(ch)))
+    ((wchar_t) (0xDC00 + (unsigned char) ch))
 #endif /* MULTIBYTE_SUPPORT */
 
 /*
@@ -346,7 +346,7 @@ metacharinc(char **x)
      * set doesn't have the property that all bytes with the 8th
      * bit clear are single characters then we are stuffed.
      */
-    if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*inptr) & 0x80))
+    if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *inptr & 0x80))
     {
 	if (itok(*inptr))
 	    inchar = ztokens[*inptr++ - Pound];
@@ -357,7 +357,7 @@ metacharinc(char **x)
 	    inchar = *inptr++;
 	}
 	*x = inptr;
-	return (wchar_t)STOUC(inchar);
+	return (wchar_t)(unsigned char) inchar;
     }
 
     while (*inptr) {
@@ -1181,8 +1181,8 @@ pattern_range_to_string(char *rangestr, char *outstr)
     int len = 0;
 
     while (*rangestr) {
-	if (imeta(STOUC(*rangestr))) {
-	    int swtype = STOUC(*rangestr) - STOUC(Meta);
+	if (imeta((unsigned char) *rangestr)) {
+	    int swtype = (unsigned char) *rangestr - (unsigned char) Meta;
 
 	    if (swtype == 0) {
 		/* Ordindary metafied character */
@@ -1278,17 +1278,17 @@ patcomppiece(int *flagp, int paren)
 	kshchar = '\0';
 	if (*patparse && patparse[1] == Inpar) {
 	    if (*patparse == zpc_special[ZPC_KSH_PLUS])
-		kshchar = STOUC('+');
+		kshchar = (unsigned char) '+';
 	    else if (*patparse == zpc_special[ZPC_KSH_BANG])
-		kshchar = STOUC('!');
+		kshchar = (unsigned char) '!';
 	    else if (*patparse == zpc_special[ZPC_KSH_BANG2])
-		kshchar = STOUC('!');
+		kshchar = (unsigned char) '!';
 	    else if (*patparse == zpc_special[ZPC_KSH_AT])
-		kshchar = STOUC('@');
+		kshchar = (unsigned char) '@';
 	    else if (*patparse == zpc_special[ZPC_KSH_STAR])
-		kshchar = STOUC('*');
+		kshchar = (unsigned char) '*';
 	    else if (*patparse == zpc_special[ZPC_KSH_QUEST])
-		kshchar = STOUC('?');
+		kshchar = (unsigned char) '?';
 	}
 
 	/*
@@ -1468,7 +1468,8 @@ patcomppiece(int *flagp, int paren)
 			ch = range_type(patparse, len);
 			patparse = nptr + 2;
 			if (ch != PP_UNKWN)
-			    patadd(NULL, STOUC(Meta) + ch, 1, PA_NOALIGN);
+			    patadd(NULL, (unsigned char) Meta + ch, 1,
+				PA_NOALIGN);
 			continue;
 		}
 		charstart = patparse;
@@ -1476,10 +1477,10 @@ patcomppiece(int *flagp, int paren)
 
 		if (*patparse == Dash && patparse[1] &&
 		    patparse[1] != Outbrack) {
-		    patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN);
+		    patadd(NULL, (unsigned char) Meta+PP_RANGE, 1, PA_NOALIGN);
 		    if (itok(*charstart)) {
-			patadd(0, STOUC(ztokens[*charstart - Pound]), 1,
-			       PA_NOALIGN);
+			patadd(0, (unsigned char) ztokens[*charstart - Pound],
+			       1, PA_NOALIGN);
 		    } else {
 			patadd(charstart, 0, patparse-charstart, PA_NOALIGN);
 		    }
@@ -1487,7 +1488,7 @@ patcomppiece(int *flagp, int paren)
 		    METACHARINC(patparse);
 		}
 		if (itok(*charstart)) {
-		    patadd(0, STOUC(ztokens[*charstart - Pound]), 1,
+		    patadd(0, (unsigned char) ztokens[*charstart - Pound], 1,
 			   PA_NOALIGN);
 		} else {
 		    patadd(charstart, 0, patparse-charstart, PA_NOALIGN);
@@ -1910,8 +1911,8 @@ charref(char *x, char *y, int *zmb_ind)
     wchar_t wc;
     size_t ret;
 
-    if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80))
-	return (wchar_t) STOUC(*x);
+    if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80))
+	return (wchar_t) (unsigned char) *x;
 
     ret = mbrtowc(&wc, x, y-x, &shiftstate);
 
@@ -1937,7 +1938,7 @@ charnext(char *x, char *y)
     wchar_t wc;
     size_t ret;
 
-    if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80))
+    if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80))
 	return x + 1;
 
     ret = mbrtowc(&wc, x, y-x, &shiftstate);
@@ -1965,8 +1966,8 @@ charrefinc(char **x, char *y, int *z)
     wchar_t wc;
     size_t ret;
 
-    if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(**x) & 0x80))
-	return (wchar_t) STOUC(*(*x)++);
+    if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) **x & 0x80))
+	return (wchar_t) (unsigned char) *(*x)++;
 
     ret = mbrtowc(&wc, *x, y-*x, &shiftstate);
 
@@ -2025,13 +2026,13 @@ charsub(char *x, char *y)
 #else /* no MULTIBYTE_SUPPORT */
 
 /* Get a character from the start point in a string */
-#define CHARREF(x, y)	(STOUC(*(x)))
+#define CHARREF(x, y)	((unsigned char) (*(x)))
 /* Get  a pointer to the next character */
 #define CHARNEXT(x, y)	((x)+1)
 /* Increment a pointer past the current character. */
 #define CHARINC(x, y)	((x)++)
 /* Get a character and increment */
-#define CHARREFINC(x, y, z)	(STOUC(*(x)++))
+#define CHARREFINC(x, y, z)	((unsigned char) (*(x)++))
 /* Counter the number of characters between two pointers, smaller first */
 #define CHARSUB(x,y)	((y) - (x))
 
@@ -2890,7 +2891,7 @@ patmatch(Upat prog)
 		}
 		if (!no && P_OP(next) == P_EXACTLY &&
 		    (!P_LS_LEN(next) ||
-		     !idigit(STOUC(*P_LS_STR(next)))) &&
+		     !idigit((unsigned char) (*P_LS_STR(next)))) &&
 		    !(patglobflags & 0xff))
 		    return 0;
 		patinput = --save;
@@ -2986,14 +2987,15 @@ patmatch(Upat prog)
 	case P_EXCSYNC:
 	    /* See the P_EXCLUDE code below for where syncptr comes from */
 	    {
-		unsigned char *syncptr;
+		unsigned char *syncstart, *syncptr, *ptr;
 		Upat after;
 		after = P_OPERAND(scan);
 		DPUTS(!P_ISEXCLUDE(after),
 		      "BUG: EXCSYNC not followed by EXCLUDE.");
 		DPUTS(!P_OPERAND(after)->p,
 		      "BUG: EXCSYNC not handled by EXCLUDE");
-		syncptr = P_OPERAND(after)->p + (patinput - patinstart);
+		syncstart = P_OPERAND(after)->p;
+		syncptr = syncstart + (patinput - patinstart);
 		/*
 		 * If we already matched from here, this time we fail.
 		 * See WBRANCH code for story about error count.
@@ -3008,6 +3010,23 @@ patmatch(Upat prog)
 		 * failed anyway.
 		 */
 		*syncptr = errsfound + 1;
+		/*
+		 * Because of backtracking, any match before this point
+		 * can't apply to the current branch we're on so is now
+		 * a failure --- this can happen if, on a previous
+		 * branch, we initially marked a success before failing
+		 * on a later part of the pattern after marking up the
+		 * P_EXCSYNC (even an end anchor will have this effect).
+		 * To make sure we record the current match point
+		 * correctly, mark those down now.
+		 *
+		 * This might have side effects on the efficiency of
+		 * pathological cases involving nested branches.  To
+		 * fix that we'd probably need to record matches on
+		 * different branches separately.
+		 */
+		for (ptr = syncstart; ptr < syncptr; ++ptr)
+		    *ptr = 0;
 	    }
 	    break;
 	case P_EXCEND:
@@ -3600,8 +3619,8 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
      * ranges specially.
      */
     while (*range) {
-	if (imeta(STOUC(*range))) {
-	    int swtype = STOUC(*range++) - STOUC(Meta);
+	if (imeta((unsigned char) *range)) {
+	    int swtype = (unsigned char) *range++ - (unsigned char) Meta;
 	    if (mtp)
 		*mtp = swtype;
 	    switch (swtype) {
@@ -3672,6 +3691,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
 		    return 1;
 		break;
 	    case PP_IDENT:
+		/* Could use INAMESPC here? */
 		if (wcsitype(ch, IIDENT))
 		    return 1;
 		break;
@@ -3753,8 +3773,8 @@ mb_patmatchindex(char *range, wint_t ind, wint_t *chr, int *mtp)
     *mtp = 0;
 
     while (*range) {
-	if (imeta(STOUC(*range))) {
-	    int swtype = STOUC(*range++) - STOUC(Meta);
+	if (imeta((unsigned char) *range)) {
+	    int swtype = (unsigned char) *range++ - (unsigned char) Meta;
 	    switch (swtype) {
 	    case 0:
 		range--;
@@ -3845,13 +3865,13 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
      * ranges specially.
      */
     for (; *range; range++) {
-	if (imeta(STOUC(*range))) {
-	    int swtype = STOUC(*range) - STOUC(Meta);
+	if (imeta((unsigned char) *range)) {
+	    int swtype = (unsigned char) *range - (unsigned char) Meta;
 	    if (mtp)
 		*mtp = swtype;
 	    switch (swtype) {
 	    case 0:
-		if (STOUC(*++range ^ 32) == ch)
+		if ((unsigned char) (*++range ^ 32) == ch)
 		    return 1;
 		break;
 	    case PP_ALPHA:
@@ -3931,9 +3951,9 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
 		break;
 	    case PP_RANGE:
 		range++;
-		r1 = STOUC(UNMETA(range));
+		r1 = (unsigned char) UNMETA(range);
 		METACHARINC(range);
-		r2 = STOUC(UNMETA(range));
+		r2 = (unsigned char) UNMETA(range);
 		if (*range == Meta)
 		    range++;
 		if (r1 <= ch && ch <= r2) {
@@ -3955,7 +3975,7 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
 		DPUTS(1, "BUG: unknown metacharacter in range.");
 		break;
 	    }
-	} else if (STOUC(*range) == ch) {
+	} else if ((unsigned char) *range == ch) {
 	    if (mtp)
 		*mtp = 0;
 	    return 1;
@@ -3989,12 +4009,12 @@ patmatchindex(char *range, int ind, int *chr, int *mtp)
     *mtp = 0;
 
     for (; *range; range++) {
-	if (imeta(STOUC(*range))) {
-	    int swtype = STOUC(*range) - STOUC(Meta);
+	if (imeta((unsigned char) *range)) {
+	    int swtype = (unsigned char) *range - (unsigned char) Meta;
 	    switch (swtype) {
 	    case 0:
 		/* ordinary metafied character */
-		rchr = STOUC(*++range) ^ 32;
+		rchr = (unsigned char) *++range ^ 32;
 		if (!ind) {
 		    *chr = rchr;
 		    return 1;
@@ -4028,9 +4048,9 @@ patmatchindex(char *range, int ind, int *chr, int *mtp)
 
 	    case PP_RANGE:
 		range++;
-		r1 = STOUC(UNMETA(range));
+		r1 = (unsigned char) UNMETA(range);
 		METACHARINC(range);
-		r2 = STOUC(UNMETA(range));
+		r2 = (unsigned char) UNMETA(range);
 		if (*range == Meta)
 		    range++;
 		rdiff = r2 - r1; 
@@ -4050,7 +4070,7 @@ patmatchindex(char *range, int ind, int *chr, int *mtp)
 	    }
 	} else {
 	    if (!ind) {
-		*chr = STOUC(*range);
+		*chr = (unsigned char) *range;
 		return 1;
 	    }
 	}
diff --git a/Src/prompt.c b/Src/prompt.c
index 092de63a4..e10b05215 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -30,10 +30,20 @@
 #include "zsh.mdh"
 #include "prompt.pro"
 
-/* text attribute mask */
+/* current text attributes */
 
 /**/
-mod_export zattr txtattrmask;
+mod_export zattr txtcurrentattrs;
+
+/* pending changes for attributes */
+
+/**/
+mod_export zattr txtpendingattrs;
+
+/* mask of attributes with an unknown state */
+
+/**/
+mod_export zattr txtunknownattrs;
 
 /* the command stack for use with %_ in prompts */
 
@@ -160,15 +170,11 @@ promptpath(char *p, int npath, int tilde)
  * between spacing and non-spacing parts of the prompt, and
  * Nularg, which (in a non-spacing sequence) indicates a
  * `glitch' space.
- *
- * txtchangep gives an integer controlling the attributes of
- * the prompt.  This is for use in zle to maintain the attributes
- * consistently.  Other parts of the shell should not need to use it.
  */
 
 /**/
 mod_export char *
-promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep)
+promptexpand(char *s, int ns, char *rs, char *Rs)
 {
     struct buf_vars new_vars;
 
@@ -212,7 +218,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep)
     new_vars.bp1 = NULL;
     new_vars.truncwidth = 0;
 
-    putpromptchar(1, '\0', txtchangep);
+    putpromptchar(1, '\0');
     addbufspc(2);
     if (new_vars.dontcount)
 	*new_vars.bp++ = Outpar;
@@ -235,6 +241,68 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep)
     return new_vars.buf;
 }
 
+/* Get the escape sequence for a given attribute. */
+/**/
+mod_export char *
+zattrescape(zattr atr, int *len)
+{
+    struct buf_vars new_vars;
+    zattr savecurrent = txtcurrentattrs;
+    zattr saveunknown = txtunknownattrs;
+
+    memset(&new_vars, 0, sizeof(new_vars));
+    new_vars.last = bv;
+    bv = &new_vars;
+    new_vars.bufspc = 256;
+    new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc);
+    new_vars.dontcount = 1;
+
+    txtunknownattrs = 0;
+    treplaceattrs(atr);
+    applytextattributes(TSC_PROMPT);
+
+    bv = new_vars.last;
+
+    txtpendingattrs = txtcurrentattrs = savecurrent;
+    txtunknownattrs = saveunknown;
+
+    return unmetafy(new_vars.buf, len);
+}
+
+/* Parse the argument for %H */
+/**/
+mod_export char *
+parsehighlight(char *arg, char endchar, zattr *atr)
+{
+    static int entered = 0;
+    char *var = ".zle.hlgroups";
+    struct value vbuf;
+    Value v;
+    char *ep, *attrs;
+    if ((ep = strchr(arg, endchar)))
+	*ep = '\0';
+    if (!entered && (v = getvalue(&vbuf, &var, 0)) &&
+	    PM_TYPE(v->pm->node.flags) == PM_HASHED)
+    {
+	Param node;
+	HashTable ht = v->pm->gsu.h->getfn(v->pm);
+	if (ht && (node = (Param) ht->getnode(ht, arg))) {
+	    attrs = node->gsu.s->getfn(node);
+	    entered = 1;
+	    if (match_highlight(attrs, atr, 0) == attrs)
+		*atr = TXT_ERROR;
+	} else
+	    *atr = TXT_ERROR;
+    } else
+	*atr = TXT_ERROR;
+    if (ep)
+	*ep++ = endchar;
+    else
+	ep = strchr(arg, '\0');
+    entered = 0;
+    return ep;
+}
+
 /* Parse the argument for %F and %K */
 static zattr
 parsecolorchar(zattr arg, int is_fg)
@@ -253,7 +321,7 @@ parsecolorchar(zattr arg, int is_fg)
 	    *ep = '\0';
 	    /* expand the contents of the argument so you can use
 	     * %v for example */
-	    coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL);
+	    coll = col = promptexpand(bv->fm, 0, NULL, NULL);
 	    *ep = oc;
 	    arg = match_colour((const char **)&coll, is_fg, 0);
 	    free(col);
@@ -278,7 +346,7 @@ parsecolorchar(zattr arg, int is_fg)
 
 /**/
 static int
-putpromptchar(int doprint, int endchar, zattr *txtchangep)
+putpromptchar(int doprint, int endchar)
 {
     char *ss, *hostnam;
     int t0, arg, test, sep, j, numjobs, len;
@@ -430,10 +498,9 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
 		/* Don't do the current truncation until we get back */
 		otruncwidth = bv->truncwidth;
 		bv->truncwidth = 0;
-		if (!putpromptchar(test == 1 && doprint, sep,
-				   txtchangep) || !*++bv->fm ||
-		    !putpromptchar(test == 0 && doprint, ')',
-				   txtchangep)) {
+		if (!putpromptchar(test == 1 && doprint, sep) ||
+				   !*++bv->fm ||
+		    !putpromptchar(test == 0 && doprint, ')')) {
 		    bv->truncwidth = otruncwidth;
 		    return 0;
 		}
@@ -519,71 +586,67 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
 		unqueue_signals();
 		break;
 	    case 'S':
-		txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT);
-		txtset(TXTSTANDOUT);
-		tsetcap(TCSTANDOUTBEG, TSC_PROMPT);
+		tsetattrs(TXTSTANDOUT);
+	        applytextattributes(TSC_PROMPT);
 		break;
 	    case 's':
-		txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
-		txtunset(TXTSTANDOUT);
-		tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY);
+		tunsetattrs(TXTSTANDOUT);
+	        applytextattributes(TSC_PROMPT);
 		break;
 	    case 'B':
-		txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE);
-		txtset(TXTBOLDFACE);
-		tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY);
+		tsetattrs(TXTBOLDFACE);
+	        applytextattributes(TSC_PROMPT);
 		break;
 	    case 'b':
-		txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE);
-		txtunset(TXTBOLDFACE);
-		tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
+		tunsetattrs(TXTBOLDFACE);
+	        applytextattributes(TSC_PROMPT);
 		break;
 	    case 'U':
-		txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE);
-		txtset(TXTUNDERLINE);
-		tsetcap(TCUNDERLINEBEG, TSC_PROMPT);
+		tsetattrs(TXTUNDERLINE);
+	        applytextattributes(TSC_PROMPT);
 		break;
 	    case 'u':
-		txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
-		txtunset(TXTUNDERLINE);
-		tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY);
+		tunsetattrs(TXTUNDERLINE);
+	        applytextattributes(TSC_PROMPT);
 		break;
 	    case 'F':
 		atr = parsecolorchar(arg, 1);
-		if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) {
-		    txtchangeset(txtchangep, atr & TXT_ATTR_FG_ON_MASK,
-				 TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK);
-		    txtunset(TXT_ATTR_FG_COL_MASK);
-		    txtset(atr & TXT_ATTR_FG_ON_MASK);
-		    set_colour_attribute(atr, COL_SEQ_FG, TSC_PROMPT);
+		if (atr && atr != TXT_ERROR) {
+		    tsetattrs(atr);
+		    applytextattributes(TSC_PROMPT);
 		    break;
 		}
 		/* else FALLTHROUGH */
 	    case 'f':
-		txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK);
-		txtunset(TXT_ATTR_FG_ON_MASK);
-		set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT);
+		tunsetattrs(TXTFGCOLOUR);
+		applytextattributes(TSC_PROMPT);
 		break;
 	    case 'K':
 		atr = parsecolorchar(arg, 0);
-		if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) {
-		    txtchangeset(txtchangep, atr & TXT_ATTR_BG_ON_MASK,
-				 TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK);
-		    txtunset(TXT_ATTR_BG_COL_MASK);
-		    txtset(atr & TXT_ATTR_BG_ON_MASK);
-		    set_colour_attribute(atr, COL_SEQ_BG, TSC_PROMPT);
+		if (atr && atr != TXT_ERROR) {
+		    tsetattrs(atr);
+		    applytextattributes(TSC_PROMPT);
 		    break;
 		}
 		/* else FALLTHROUGH */
 	    case 'k':
-		txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK);
-		txtunset(TXT_ATTR_BG_ON_MASK);
-		set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT);
+		tunsetattrs(TXTBGCOLOUR);
+	        applytextattributes(TSC_PROMPT);
+		break;
+	    case 'H':
+		if (bv->fm[1] == '{') {
+		    bv->fm = parsehighlight(bv->fm + 2, '}', &atr);
+		    --bv->fm;
+		    if (atr != TXT_ERROR) {
+			treplaceattrs(atr);
+			applytextattributes(TSC_PROMPT);
+		    }
+		}
 		break;
 	    case '[':
 		if (idigit(*++bv->fm))
 		    arg = zstrtol(bv->fm, &bv->fm, 10);
-		if (!prompttrunc(arg, ']', doprint, endchar, txtchangep))
+		if (!prompttrunc(arg, ']', doprint, endchar))
 		    return *bv->fm;
 		break;
 	    case '<':
@@ -596,7 +659,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep)
 		    if (arg <= 0)
 			arg = 1;
 		}
-		if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep))
+		if (!prompttrunc(arg, *bv->fm, doprint, endchar))
 		    return *bv->fm;
 		break;
 	    case '{': /*}*/
@@ -1013,9 +1076,8 @@ stradd(char *d)
 mod_export void
 tsetcap(int cap, int flags)
 {
-    if (tccan(cap) && !isset(SINGLELINEZLE) &&
-        !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
-	switch (flags & TSC_OUTPUT_MASK) {
+    if (tccan(cap) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) {
+	switch (flags) {
 	case TSC_RAW:
 	    tputs(tcstr[cap], 1, putraw);
 	    break;
@@ -1045,20 +1107,6 @@ tsetcap(int cap, int flags)
 	    }
 	    break;
 	}
-
-	if (flags & TSC_DIRTY) {
-	    flags &= ~TSC_DIRTY;
-	    if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG)
-		tsetcap(TCBOLDFACEBEG, flags);
-	    if (txtisset(TXTSTANDOUT))
-		tsetcap(TCSTANDOUTBEG, flags);
-	    if (txtisset(TXTUNDERLINE))
-		tsetcap(TCUNDERLINEBEG, flags);
-	    if (txtisset(TXTFGCOLOUR))
-		set_colour_attribute(txtattrmask, COL_SEQ_FG, flags);
-	    if (txtisset(TXTBGCOLOUR))
-		set_colour_attribute(txtattrmask, COL_SEQ_BG, flags);
-	}
     }
 }
 
@@ -1219,8 +1267,7 @@ countprompt(char *str, int *wp, int *hp, int overf)
 
 /**/
 static int
-prompttrunc(int arg, int truncchar, int doprint, int endchar,
-	    zattr *txtchangep)
+prompttrunc(int arg, int truncchar, int doprint, int endchar)
 {
     if (arg > 0) {
 	char ch = *bv->fm, *ptr, *truncstr;
@@ -1267,7 +1314,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
 	w = bv->bp - bv->buf;
 	bv->fm++;
 	bv->trunccount = bv->dontcount;
-	putpromptchar(doprint, endchar, txtchangep);
+	putpromptchar(doprint, endchar);
 	bv->trunccount = 0;
 	ptr = bv->buf + w;	/* putpromptchar() may have realloc()'d */
 	*bv->bp = '\0';
@@ -1547,7 +1594,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar,
 	     * With bv->truncwidth set to zero, we always reach endchar *
 	     * (or the terminating NULL) this time round.         *
 	     */
-	    if (!putpromptchar(doprint, endchar, txtchangep))
+	    if (!putpromptchar(doprint, endchar))
 		return 0;
 	}
 	/* Now we have to trick it into matching endchar again */
@@ -1585,6 +1632,158 @@ cmdpop(void)
 	cmdsp--;
 }
 
+/* functions for handling attributes */
+
+/**/
+mod_export void
+applytextattributes(int flags)
+{
+    zattr change = txtcurrentattrs ^ txtpendingattrs;
+    zattr keepon = ~change & txtpendingattrs & TXT_ATTR_ALL;
+    zattr turnoff = change & ~txtpendingattrs & TXT_ATTR_ALL;
+    int keepcount, turncount = 0;
+
+    /* bail out early if we wouldn't do anything */
+    if (!change)
+	return;
+
+    if (txtunknownattrs) {
+	txtunknownattrs &= ~change; /* changes cease to be unknown */
+	/* can't turn unknown attrs back on so avoid wiping them */
+	keepcount = 1;
+    } else {
+	/* If we want to turn off more attributes than we want to keep on
+	 * then it takes fewer termcap sequences to just turn off all the
+	 * attributes. */
+	for (keepcount = 0; keepon; keepcount++) /* count bits */
+	    keepon &= keepon - 1;
+	for (; turnoff; turncount++)
+	    turnoff &= turnoff - 1;
+    }
+
+    /* enabling bold can be relied upon to disable faint
+     * (the converse not so as that commonly does nothing at all) */
+    if (txtcurrentattrs & TXTFAINT && txtpendingattrs & TXTBOLDFACE) {
+	--turncount;
+	change &= ~TXTFAINT;
+    }
+
+    if (keepcount < turncount ||
+	    (change & ~txtpendingattrs & TXT_ATTR_FONT_WEIGHT)) {
+	tsetcap(TCALLATTRSOFF, flags);
+	/* this cleared all attributes, may need to restore some */
+	change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs;
+	txtunknownattrs = 0;
+    } else {
+	if (change & ~txtpendingattrs & TXTSTANDOUT) {
+	    tsetcap(TCSTANDOUTEND, flags);
+	    /* in some cases, that clears all attributes */
+	    change = (txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs) |
+		    (TXTUNDERLINE & change);
+	}
+	if (change & ~txtpendingattrs & TXTUNDERLINE) {
+	    tsetcap(TCUNDERLINEEND, flags);
+	    /* in some cases, that clears all attributes */
+	    change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs;
+	}
+	if (change & ~txtpendingattrs & TXTITALIC)
+	    tsetcap(TCITALICSEND, flags);
+    }
+    if (change & txtpendingattrs & TXTBOLDFACE)
+	tsetcap(TCBOLDFACEBEG, flags);
+    if (change & txtpendingattrs & TXTFAINT)
+	tsetcap(TCFAINTBEG, flags);
+    if (change & txtpendingattrs & TXTSTANDOUT)
+	tsetcap(TCSTANDOUTBEG, flags);
+    if (change & txtpendingattrs & TXTUNDERLINE)
+	tsetcap(TCUNDERLINEBEG, flags);
+    if (change & txtpendingattrs & TXTITALIC)
+	tsetcap(TCITALICSBEG, flags);
+
+    if (change & TXT_ATTR_FG_MASK)
+	set_colour_attribute(txtpendingattrs, COL_SEQ_FG, flags);
+    if (change & TXT_ATTR_BG_MASK)
+	set_colour_attribute(txtpendingattrs, COL_SEQ_BG, flags);
+
+    txtcurrentattrs = txtpendingattrs;
+}
+
+/**/
+mod_export void
+cleartextattributes(int flags)
+{
+    treplaceattrs(0);
+    applytextattributes(flags);
+}
+
+/**/
+mod_export void
+treplaceattrs(zattr newattrs)
+{
+    if (newattrs == TXT_ERROR)
+	return;
+
+    if (txtunknownattrs) {
+	/* Set current attributes to the opposite of the new ones
+	 * for any that are unknown so that applytextattributes()
+	 * detects them as changed. */
+	txtcurrentattrs &= ~txtunknownattrs;
+	txtcurrentattrs |= txtunknownattrs & ~newattrs;
+    }
+
+    txtpendingattrs = newattrs;
+}
+
+/**/
+mod_export void
+tsetattrs(zattr newattrs)
+{
+    /* assume any unknown attributes that we're now setting were unset */
+    txtcurrentattrs &= ~(newattrs & txtunknownattrs);
+
+    txtpendingattrs |= newattrs & TXT_ATTR_ALL;
+    if (newattrs & TXTFGCOLOUR) {
+	txtpendingattrs &= ~TXT_ATTR_FG_MASK;
+	txtpendingattrs |= newattrs & TXT_ATTR_FG_MASK;
+    }
+    if (newattrs & TXTBGCOLOUR) {
+	txtpendingattrs &= ~TXT_ATTR_BG_MASK;
+	txtpendingattrs |= newattrs & TXT_ATTR_BG_MASK;
+    }
+}
+
+/**/
+mod_export void
+tunsetattrs(zattr newattrs)
+{
+    /* assume any unknown attributes that we're now unsetting were set */
+    txtcurrentattrs |= newattrs & txtunknownattrs;
+
+    txtpendingattrs &= ~(newattrs & TXT_ATTR_ALL);
+    if (newattrs & TXTFGCOLOUR)
+	txtpendingattrs &= ~TXT_ATTR_FG_MASK;
+    if (newattrs & TXTBGCOLOUR)
+	txtpendingattrs &= ~TXT_ATTR_BG_MASK;
+}
+
+/* Merge two attribute sets. In an case where attributes might conflict
+ * choose those from the first parameter.  Foreground and background
+ * colours are taken together - less likely to end up with unreadable
+ * combinations. */
+
+/**/
+mod_export zattr
+mixattrs(zattr primary, zattr secondary)
+{
+    zattr result = secondary;
+    /* take colours from primary */
+    if (primary & (TXTFGCOLOUR|TXTBGCOLOUR))
+        result &= ~TXT_ATTR_COLOUR_MASK;
+    /* take font weight from primary */
+    if (primary & TXT_ATTR_FONT_WEIGHT)
+        result &= ~TXT_ATTR_FONT_WEIGHT;
+    return result | primary;
+}
 
 /*****************************************************************************
  * Utilities dealing with colour and other forms of highlighting.
@@ -1607,10 +1806,12 @@ struct highlight {
 };
 
 static const struct highlight highlights[] = {
-    { "none", 0, TXT_ATTR_ON_MASK },
-    { "bold", TXTBOLDFACE, 0 },
+    { "none", 0, TXT_ATTR_ALL },
+    { "bold", TXTBOLDFACE, TXTFAINT },
+    { "faint", TXTFAINT, TXTBOLDFACE },
     { "standout", TXTSTANDOUT, 0 },
     { "underline", TXTUNDERLINE, 0 },
+    { "italic", TXTITALIC, 0 },
     { NULL, 0, 0 }
 };
 
@@ -1645,8 +1846,8 @@ match_named_colour(const char **teststrp)
  * Match just the colour part of a highlight specification.
  * If teststrp is NULL, use the already parsed numeric colour.
  * Return the attributes to set in the attribute variable.
- * Return -1 for out of range.  Does not check the character
- * following the colour specification.
+ * Return TXT_ERROR for out of range.  Does not check the
+ * character following the colour specification.
  */
 
 /**/
@@ -1666,7 +1867,7 @@ match_colour(const char **teststrp, int is_fg, int colour)
 	tc = TCBGCOLOUR;
     }
     if (teststrp) {
-	if (**teststrp == '#' && isxdigit(STOUC((*teststrp)[1]))) {
+	if (**teststrp == '#' && isxdigit((unsigned char) (*teststrp)[1])) {
 	    struct color_rgb color;
 	    char *end;
 	    zlong col = zstrtol(*teststrp+1, &end, 16);
@@ -1693,10 +1894,8 @@ match_colour(const char **teststrp, int is_fg, int colour)
 	    }
 	} else if ((named = ialpha(**teststrp))) {
 	    colour = match_named_colour(teststrp);
-	    if (colour == 8) {
-		/* default */
-		return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR;
-	    }
+	    if (colour == 8) /* default */
+		return 0;
 	    if (colour < 0)
 		return TXT_ERROR;
 	}
@@ -1717,23 +1916,30 @@ match_colour(const char **teststrp, int is_fg, int colour)
 /*
  * Match a set of highlights in the given teststr.
  * Set *on_var to reflect the values found.
+ * Set *layer to the layer
  * Return a pointer to the first character not consumed.
  */
 
 /**/
 mod_export const char *
-match_highlight(const char *teststr, zattr *on_var)
+match_highlight(const char *teststr, zattr *on_var, int *layer)
 {
     int found = 1;
 
     *on_var = 0;
     while (found && *teststr) {
 	const struct highlight *hl;
+	zattr atr = 0;
 
 	found = 0;
-	if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
+	if (strpfx("hl=", teststr)) {
+	    teststr += 3;
+	    teststr = parsehighlight((char *)teststr, ',', &atr);
+	    if (atr != TXT_ERROR)
+		*on_var = atr;
+	    found = 1;
+	} else if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
 	    int is_fg = (teststr[0] == 'f');
-	    zattr atr;
 
 	    teststr += 3;
 	    atr = match_colour(&teststr, is_fg, 0);
@@ -1745,6 +1951,14 @@ match_highlight(const char *teststr, zattr *on_var)
 	    /* skip out of range colours but keep scanning attributes */
 	    if (atr != TXT_ERROR)
 		*on_var |= atr;
+	} else if (layer && strpfx("layer=", teststr)) {
+	    teststr += 6;
+	    *layer = (int) zstrtol(teststr, (char **) &teststr, 10);
+	    if (*teststr == ',')
+		teststr++;
+	    else if (*teststr && *teststr != ' ')
+		break;
+	    found = 1;
 	} else {
 	    for (hl = highlights; hl->name; hl++) {
 		if (strpfx(hl->name, teststr)) {
@@ -2024,13 +2238,13 @@ set_colour_attribute(zattr atr, int fg_bg, int flags)
     if (fg_bg == COL_SEQ_FG) {
 	colour = txtchangeget(atr, TXT_ATTR_FG_COL);
 	tc = TCFGCOLOUR;
-	def = txtchangeisset(atr, TXTNOFGCOLOUR);
-	use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT);
+	def = !(atr & TXTFGCOLOUR);
+	use_truecolor = atr & TXT_ATTR_FG_24BIT;
     } else {
 	colour = txtchangeget(atr, TXT_ATTR_BG_COL);
 	tc = TCBGCOLOUR;
-	def = txtchangeisset(atr, TXTNOBGCOLOUR);
-	use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT);
+	def = !(atr & TXTBGCOLOUR);
+	use_truecolor = atr & TXT_ATTR_BG_24BIT;
     }
 
     /* Test if current zle_highlight settings are customized, or
diff --git a/Src/prototypes.h b/Src/prototypes.h
index e3db4f5ee..3578482d0 100644
--- a/Src/prototypes.h
+++ b/Src/prototypes.h
@@ -28,9 +28,9 @@
  */
 
 #ifndef HAVE_STDLIB_H
-char *malloc _((size_t));
-char *realloc _((void *, size_t));
-char *calloc _((size_t, size_t));
+char *malloc (size_t);
+char *realloc (void *, size_t);
+char *calloc (size_t, size_t);
 #endif
 
 #if !(defined(USES_TERMCAP_H) || defined(USES_TERM_H))
@@ -45,11 +45,11 @@ char *calloc _((size_t, size_t));
 #else
 #define TC_CONST
 #endif
-extern int tgetent _((char *bp, TC_CONST char *name));
-extern int tgetnum _((char *id));
-extern int tgetflag _((char *id));
-extern char *tgetstr _((char *id, char **area));
-extern int tputs _((TC_CONST char *cp, int affcnt, int (*outc) (int)));
+extern int tgetent (char *bp, TC_CONST char *name);
+extern int tgetnum (char *id);
+extern int tgetflag (char *id);
+extern char *tgetstr (char *id, char **area);
+extern int tputs (TC_CONST char *cp, int affcnt, int (*outc) (int));
 #undef TC_CONST
 #endif
 
@@ -70,30 +70,30 @@ char *tgoto(const char *cap, int col, int row);
 #endif
 
 #ifdef __osf__
-char *mktemp _((char *));
+char *mktemp (char *);
 #endif
 
 #if defined(__osf__) && defined(__alpha) && defined(__GNUC__)
 /* Digital cc does not need these prototypes, gcc does need them */
 # ifndef HAVE_IOCTL_PROTO
-int ioctl _((int d, unsigned long request, void *argp));
+int ioctl (int d, unsigned long request, void *argp);
 # endif
 # ifndef HAVE_MKNOD_PROTO
-int mknod _((const char *pathname, int mode, dev_t device));
+int mknod (const char *pathname, int mode, dev_t device);
 # endif
-int nice _((int increment));
-int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout));
+int nice (int increment);
+int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout);
 #endif
 
 #if defined(DGUX) && defined(__STDC__)
 /* Just plain missing. */
-extern int getrlimit _((int resource, struct rlimit *rlp));
-extern int setrlimit _((int resource, const struct rlimit *rlp));
-extern int getrusage _((int who, struct rusage *rusage));
-extern int gettimeofday _((struct timeval *tv, struct timezone *tz));
-extern int wait3 _((union wait *wait_status, int options, struct rusage *rusage));
-extern int getdomainname _((char *name, int maxlength));
-extern int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout));
+extern int getrlimit (int resource, struct rlimit *rlp);
+extern int setrlimit (int resource, const struct rlimit *rlp);
+extern int getrusage (int who, struct rusage *rusage);
+extern int gettimeofday (struct timeval *tv, struct timezone *tz);
+extern int wait3 (union wait *wait_status, int options, struct rusage *rusage);
+extern int getdomainname (char *name, int maxlength);
+extern int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout);
 #endif /* DGUX and __STDC__ */
 
 #ifdef __NeXT__
@@ -101,34 +101,34 @@ extern pid_t getppid(void);
 #endif
 
 #if defined(__sun__) && !defined(__SVR4)  /* SunOS */
-extern char *strerror _((int errnum));
+extern char *strerror (int errnum);
 #endif
 
 /**************************************************/
 /*** prototypes for functions built in compat.c ***/
 #ifndef HAVE_STRSTR
-extern char *strstr _((const char *s, const char *t));
+extern char *strstr (const char *s, const char *t);
 #endif
 
 #ifndef HAVE_GETHOSTNAME
-extern int gethostname _((char *name, size_t namelen));
+extern int gethostname (char *name, size_t namelen);
 #endif
 
 #ifndef HAVE_GETTIMEOFDAY
-extern int gettimeofday _((struct timeval *tv, struct timezone *tz));
+extern int gettimeofday (struct timeval *tv, struct timezone *tz);
 #endif
 
 #ifndef HAVE_DIFFTIME
-extern double difftime _((time_t t2, time_t t1));
+extern double difftime (time_t t2, time_t t1);
 #endif
 
 #ifndef HAVE_STRERROR
-extern char *strerror _((int errnum));
+extern char *strerror (int errnum);
 #endif
 
 /*** end of prototypes for functions in compat.c ***/
 /***************************************************/
 
 #ifndef HAVE_MEMMOVE
-extern void bcopy _((const void *, void *, size_t));
+extern void bcopy (const void *, void *, size_t);
 #endif
diff --git a/Src/signals.c b/Src/signals.c
index a61368554..d28853ea6 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -31,10 +31,12 @@
 #include "signals.pro"
  
 /* Array describing the state of each signal: an element contains *
- * 0 for the default action or some ZSIG_* flags ored together.   */
+ * 0 for the default action or some ZSIG_* flags ored together.   *
+ * Contains TRAPCOUNT elements but can't be allocated statically  *
+ * because that's a dynamic value on Linux                        */
 
 /**/
-mod_export int sigtrapped[VSIGCOUNT];
+mod_export int *sigtrapped;
 
 /*
  * Trap programme lists for each signal.
@@ -48,7 +50,7 @@ mod_export int sigtrapped[VSIGCOUNT];
  */
 
 /**/
-mod_export Eprog siglists[VSIGCOUNT];
+mod_export Eprog *siglists;
 
 /* Total count of trapped signals */
 
@@ -556,9 +558,11 @@ wait_for_processes(void)
 		    jn->gleader = 0;
 		}
 	    }
+	    update_bg_job(jn, pid, status);
 	    update_job(jn);
 	} else if (findproc(pid, &jn, &pn, 1)) {
 	    pn->status = status;
+	    update_bg_job(jn, pid, status);
 	    update_job(jn);
 	} else {
 	    /* If not found, update the shell record of time spent by
@@ -567,19 +571,7 @@ wait_for_processes(void)
 	     * terminates.
 	     */
 	    get_usage();
-	}
-	/*
-	 * Accumulate a list of older jobs.  We only do this for
-	 * background jobs, which is something in the job table
-	 * that's not marked as in the current shell or as shell builtin
-	 * and is not equal to the current foreground job.
-	 */
-	if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) &&
-	    jn - jobtab != thisjob) {
-	    if (WIFEXITED(status))
-		addbgstatus(pid, WEXITSTATUS(status));
-	    else if (WIFSIGNALED(status))
-		addbgstatus(pid, 0200 | WTERMSIG(status));
+	    update_bg_job(jn, pid, status);
 	}
 
 	unqueue_signals();
@@ -902,7 +894,7 @@ dosavetrap(int sig, int level)
  * Set a trap:  note this does not handle manipulation of
  * the function table for TRAPNAL functions.
  *
- * sig is the signal number.
+ * sig is index into the table of trapped signals.
  *
  * l is the list to be eval'd for a trap defined with the "trap"
  * builtin and should be NULL for a function trap.
@@ -941,6 +933,10 @@ settrap(int sig, Eprog l, int flags)
 #endif
             sig != SIGCHLD)
             signal_ignore(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+	else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+	    signal_ignore(SIGNUM(sig));
+#endif
     } else {
 	nsigtrapped++;
         sigtrapped[sig] = ZSIG_TRAPPED;
@@ -950,6 +946,10 @@ settrap(int sig, Eprog l, int flags)
 #endif
             sig != SIGCHLD)
             install_handler(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+	if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+	    install_handler(SIGNUM(sig));
+#endif
     }
     sigtrapped[sig] |= flags;
     /*
@@ -1029,6 +1029,11 @@ removetrap(int sig)
 #endif
              sig != SIGCHLD)
         signal_default(sig);
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+    else if (sig >= VSIGCOUNT && sig < TRAPCOUNT)
+	    signal_default(SIGNUM(sig));
+#endif
+
     if (sig == SIGEXIT)
 	exit_trap_posix = 0;
 
@@ -1182,7 +1187,7 @@ endtrapscope(void)
 static int
 handletrap(int sig)
 {
-    if (!sigtrapped[sig])
+    if (!sigtrapped[SIGIDX(sig)])
 	return 0;
 
     if (trap_queueing_enabled)
@@ -1199,7 +1204,7 @@ handletrap(int sig)
 	return 1;
     }
 
-    dotrap(sig);
+    dotrap(SIGIDX(sig));
 
     if (sig == SIGALRM)
     {
@@ -1491,3 +1496,60 @@ dotrap(int sig)
 
     restore_queue_signals(q);
 }
+
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+
+/* Realtime signals, these are a contiguous block that can
+ * be separated from the other signals with an unused gap. */
+
+/**/
+int
+rtsigno(const char* signame)
+{
+    const int maxofs = SIGRTMAX - SIGRTMIN;
+    const char *end = signame + 5;
+    int offset;
+    struct rtdir { int sig; int dir; char op; } x = { 0, 0, 0 };
+    if (!strncmp(signame, "RTMIN", 5)) {
+	x = (struct rtdir) { SIGRTMIN, 1, '+' };
+    } else if (!strncmp(signame, "RTMAX", 5)) {
+	x = (struct rtdir) { SIGRTMAX, -1, '-' };
+    } else
+	return 0;
+
+    if (signame[5] == x.op) {
+	if ((offset = strtol(signame + 6, (char **) &end, 10)) > maxofs)
+	    return 0;
+	x.sig += offset * x.dir;
+    }
+    if (*end)
+	return 0;
+
+    return x.sig;
+}
+
+/**/
+char *
+rtsigname(int signo, int alt)
+{
+    char* buf = (char *) zhalloc(10);
+    int minofs = signo - SIGRTMIN;
+    int maxofs = SIGRTMAX - signo;
+    int offset;
+    int form = alt ^ (maxofs < minofs);
+
+    if (signo < SIGRTMIN || signo > SIGRTMAX)
+	return NULL;
+
+    strcpy(buf, "RT");
+    strcpy(buf+2, form ? "MAX-" : "MIN+");
+    offset = form ? maxofs : minofs;
+    if (offset) {
+	snprintf(buf + 6, 4, "%d", offset);
+    } else {
+	buf[5] = '\0';
+    }
+    return buf;
+}
+
+#endif
diff --git a/Src/signals.h b/Src/signals.h
index 41ac88cce..7910f6b79 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -27,7 +27,7 @@
  *
  */
 
-#define SIGNAL_HANDTYPE void (*)_((int))
+#define SIGNAL_HANDTYPE void (*)(int)
 
 #ifndef HAVE_KILLPG
 # define killpg(pgrp,sig) kill(-(pgrp),sig)
@@ -36,6 +36,15 @@
 #define SIGZERR   (SIGCOUNT+1)
 #define SIGDEBUG  (SIGCOUNT+2)
 #define VSIGCOUNT (SIGCOUNT+3)
+#if defined(SIGRTMIN) && defined(SIGRTMAX)
+# define TRAPCOUNT (VSIGCOUNT + SIGRTMAX - SIGRTMIN + 1)
+# define SIGNUM(x) ((x) >= VSIGCOUNT ? (x) - VSIGCOUNT + SIGRTMIN : (x))
+# define SIGIDX(x) ((x) >= SIGRTMIN && (x) <= SIGRTMAX ? (x) - SIGRTMIN + VSIGCOUNT : (x))
+#else
+# define TRAPCOUNT VSIGCOUNT
+# define SIGNUM(x) (x)
+# define SIGIDX(x) (x)
+#endif
 #define SIGEXIT    0
 
 #ifdef SV_BSDSIG
@@ -136,7 +145,7 @@
 #ifdef BSD_SIGNALS
 #define signal_block(S) sigblock(S)
 #else
-extern sigset_t signal_block _((sigset_t));
+extern sigset_t signal_block (sigset_t);
 #endif  /* BSD_SIGNALS   */
 
-extern sigset_t signal_unblock _((sigset_t));
+extern sigset_t signal_unblock (sigset_t);
diff --git a/Src/signames2.awk b/Src/signames2.awk
index 4d15681d5..0b254f751 100644
--- a/Src/signames2.awk
+++ b/Src/signames2.awk
@@ -13,8 +13,9 @@
     signam = substr(tmp[1], 4, 20)
     signum = tmp[2]
     if (signam == "CHLD" && sig[signum] == "CLD")  sig[signum] = ""
-    if (signam == "POLL" && sig[signum] == "IO")  sig[signum] = ""
-    if (sig[signum] == "") {
+    if (signam == "POLL" && sig[signum] == "IO")   sig[signum] = ""
+    if (signam == "ABRT" && sig[signum] == "IOT")  sig[signum] = ""
+    if (signam !~ /RTM(IN|AX)/ && sig[signum] == "") {
 	sig[signum] = signam
 	if (0 + max < 0 + signum && signum < 60)
 	    max = signum
@@ -26,6 +27,7 @@
 	if (signam == "CONT")   { msg[signum] = "continued" }
 	if (signam == "EMT")    { msg[signum] = "EMT instruction" }
 	if (signam == "FPE")    { msg[signum] = "floating point exception" }
+        if (signam == "FREEZE") { msg[signum] = "checkpoint freeze" }
 	if (signam == "HUP")    { msg[signum] = "hangup" }
 	if (signam == "ILL")    { msg[signum] = "illegal hardware instruction" }
 	if (signam == "INFO")   { msg[signum] = "status request from keyboard" }
@@ -33,23 +35,25 @@
 	if (signam == "IO")     { msg[signum] = "i/o ready" }
 	if (signam == "IOT")    { msg[signum] = "IOT instruction" }
 	if (signam == "KILL")   { msg[signum] = "killed" }
-	if (signam == "LOST")	{ msg[signum] = "resource lost" }
+	if (signam == "LOST")   { msg[signum] = "resource lost" }
 	if (signam == "PIPE")   { msg[signum] = "broken pipe" }
-	if (signam == "POLL")	{ msg[signum] = "pollable event occurred" }
+	if (signam == "POLL")   { msg[signum] = "pollable event occurred" }
 	if (signam == "PROF")   { msg[signum] = "profile signal" }
 	if (signam == "PWR")    { msg[signum] = "power fail" }
 	if (signam == "QUIT")   { msg[signum] = "quit" }
 	if (signam == "SEGV")   { msg[signum] = "segmentation fault" }
 	if (signam == "SYS")    { msg[signum] = "invalid system call" }
 	if (signam == "TERM")   { msg[signum] = "terminated" }
+        if (signam == "THAW")   { msg[signum] = "checkpoint thaw" }
 	if (signam == "TRAP")   { msg[signum] = "trace trap" }
-	if (signam == "URG")	{ msg[signum] = "urgent condition" }
+	if (signam == "URG")    { msg[signum] = "urgent condition" }
 	if (signam == "USR1")   { msg[signum] = "user-defined signal 1" }
 	if (signam == "USR2")   { msg[signum] = "user-defined signal 2" }
 	if (signam == "VTALRM") { msg[signum] = "virtual time alarm" }
 	if (signam == "WINCH")  { msg[signum] = "window size changed" }
 	if (signam == "XCPU")   { msg[signum] = "cpu limit exceeded" }
 	if (signam == "XFSZ")   { msg[signum] = "file size limit exceeded" }
+        if (signam == "XRES")   { msg[signum] = "resource control exceeded" }
     }
 }
 
@@ -65,10 +69,6 @@ END {
     printf "#include %czsh.mdh%c\n", 34, 34
     printf "\n"
     printf "/**/\n"
-    printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]"
-    printf " : %c%s%c)", 34, "unknown signal", 34
-    printf "\n"
-    printf "/**/\n"
     printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n"
     printf "\t%c%s%c,\n", 34, "done", 34
 
diff --git a/Src/sort.c b/Src/sort.c
index 26949ad9c..ce2b4bbc3 100644
--- a/Src/sort.c
+++ b/Src/sort.c
@@ -138,7 +138,7 @@ eltpcmp(const void *a, const void *b)
 	int mul = 0;
 	for (; *as == *bs && *as; as++, bs++);
 #ifndef HAVE_STRCOLL
-	cmp = (int)STOUC(*as) - (int)STOUC(*bs);
+	cmp = (int) (unsigned char) *as - (int) (unsigned char) *bs;
 #endif
 	if (sortnumeric < 0) {
 	    if (*as == '-' && idigit(as[1]) && idigit(*bs)) {
@@ -159,7 +159,7 @@ eltpcmp(const void *a, const void *b)
 		    bs++;
 		for (; idigit(*as) && *as == *bs; as++, bs++);
 		if (idigit(*as) || idigit(*bs)) {
-		    cmp = mul * ((int)STOUC(*as) - (int)STOUC(*bs));
+		    cmp = mul * ((int) (unsigned char) *as - (int) (unsigned char) *bs);
 		    while (idigit(*as) && idigit(*bs))
 			as++, bs++;
 		    if (idigit(*as) && !idigit(*bs))
diff --git a/Src/subst.c b/Src/subst.c
index 0f98e6ea3..a079672df 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -556,7 +556,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
 	for ( ; *x; x += l) {
 	    char c = (l = *x == Meta) ? x[1] ^ 32 : *x;
 	    l++;
-	    if (!iwsep(STOUC(c)))
+	    if (!iwsep((unsigned char) c))
 		break;
 	    *ms_flags |= MULTSUB_WS_AT_START;
 	}
@@ -573,7 +573,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
 	    convchar_t c;
 	    if (*x == Dash)
 		*x = '-';
-	    if (itok(STOUC(*x))) {
+	    if (itok((unsigned char) *x)) {
 		/* token, can't be separator, must be single byte */
 		rawc = *x;
 		l = 1;
@@ -582,7 +582,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
 		if (!inq && !inp && WC_ZISTYPE(c, ISEP)) {
 		    *x = '\0';
 		    for (x += l; *x; x += l) {
-			if (itok(STOUC(*x))) {
+			if (itok((unsigned char) *x)) {
 			    /* as above */
 			    rawc = *x;
 			    l = 1;
@@ -1489,21 +1489,27 @@ subst_parse_str(char **sp, int single, int err)
 static char *
 substevalchar(char *ptr)
 {
-    zlong ires = mathevali(ptr);
+    zlong ires;
     int len = 0;
+    int saved_errflag = errflag;
 
-    if (errflag)
-	return NULL;
-#ifdef MULTIBYTE_SUPPORT
-    if (isset(MULTIBYTE) && ires > 127) {
-	/* '\\' + 'U' + 8 bytes of character + '\0' */
-	char buf[11];
+    errflag = 0;
+    ires = mathevali(ptr);
 
-	/* inefficient: should separate out \U handling from getkeystring */
-	sprintf(buf, "\\U%.8x", (unsigned int)ires & 0xFFFFFFFFu);
-	ptr = getkeystring(buf, &len, GETKEYS_BINDKEY, NULL);
+    if (errflag) {  /* not a valid numerical expression */
+	errflag |= saved_errflag;
+	return noerrs ? dupstring(""): NULL;
+    }
+    errflag |= saved_errflag;
+    if (ires < 0) {
+	zerr("character not in range");
+    }
+#ifdef MULTIBYTE_SUPPORT
+    else if (isset(MULTIBYTE) && ires > 127) {
+	ptr = zhalloc(MB_CUR_MAX+1);
+	len = ucs4tomb((unsigned int)ires & 0xffffffff, ptr);
     }
-    if (len == 0)
+    if (len <= 0)
 #endif
     {
 	ptr = zhalloc(2);
@@ -1818,14 +1824,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * Use for the (k) flag.  Goes down into the parameter code,
      * sometimes.
      */
-    char hkeys = 0;
+    int hkeys = 0;
     /*
      * Used for the (v) flag, ditto.  Not quite sure why they're
      * separate, but the tradition seems to be that things only
      * get combined when that makes the result more obscure rather
      * than less.
      */
-    char hvals = 0;
+    int hvals = 0;
     /*
      * Whether we had to evaluate a subexpression, i.e. an
      * internal ${...} or $(...) or plain $pm.  We almost don't
@@ -1860,6 +1866,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * joining the array into a string (for compatibility with ksh/bash).
      */
     int quoted_array_with_offset = 0;
+    /*
+     * Nofork substitution controls
+     */
+    char *rplyvar = NULL;    /* Indicates ${|...;} or ${{var} ...;} */
+    char *rplytmp = NULL;    /* Indicates ${ ... ;} */
 
     *s++ = '\0';
     /*
@@ -1870,8 +1881,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * these later on, too.
      */
     c = *s;
-    if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound &&
-	!IS_DASH(c) &&
+    if (itype_end(s, (c == Inbrace ? INAMESPC : IIDENT), 1) == s &&
+	*s != '#' && c != Pound && !IS_DASH(c) &&
 	c != '!' && c != '$' && c != String && c != Qstring &&
 	c != '?' && c != Quest &&
 	c != '*' && c != Star && c != '@' && c != '{' &&
@@ -1887,19 +1898,235 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * flags in parentheses, but also one ksh hack.
      */
     if (c == Inbrace) {
-	inbrace = 1;
-	s++;
+	/* For processing nofork command substitution string */
+	char *cmdarg = NULL, *endvar = NULL, inchar = *++s;
+	char *outbracep = s, sav = *s;
+	Param rplypm = NULL;
+	size_t slen = 0;
+	int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt;
+
+	inbrace = 1;	/* Outer scope boolean, see above */
+
+        /* Handling for nofork command substitution e.g. ${|cmd;}
+	 * See other comments about kludges for why this is here.
+	 *
+         * The command string is extracted and executed, and the
+         * substitution assigned. There's no (...)-flags processing,
+         * i.e. no ${|(U)cmd;}, because it looks quite awful and
+         * should not be part of command substitution in any case.
+         * Use ${(U)${|cmd;}} as you would for ${(U)$(cmd;)}.
+	 */
+	if (inchar == '|' || inchar == Bar || inblank(inchar)) {
+	    *s = Inbrace;
+	    if (skipparens(Inbrace, Outbrace, &outbracep) == 0)
+		slen = outbracep - s - 1;
+	    *s = sav;
+	    if (inchar == '|')
+		inchar = Bar;	/* Simplify later compares */
+	} else if (inchar == '{' || inchar == Inbrace) {
+	    *s = Inbrace;
+	    if ((outbracep = itype_end(s+1, INAMESPC, 0))) {
+		if (*outbracep == Inbrack &&
+		    (outbracep = parse_subscript(++outbracep, 1, ']')))
+		    ++outbracep;
+	    }
+
+	    /* If we reached the first close brace, find the last */
+	    if (outbracep && *outbracep == Outbrace) {
+		char outchar = inchar == Inbrace ? Outbrace : '}';
+		endvar = outbracep++;
+
+		/* Require space to avoid ${{var}} typo for ${${var}} */
+		if (!inblank(*outbracep)) {
+		    zerr("bad substitution");
+		    return NULL;
+		}
+
+		*endvar = '|';	/* Almost anything but braces/brackets */
+		outbracep = s;
+		if (skipparens(Inbrace, outchar, &outbracep) == 0)
+		    *endvar = Outbrace;
+		else {	/* Never happens? */
+		    *endvar = outchar;
+		    outbracep = endvar + 1;
+		}
+		slen = outbracep - s - 1;
+		if (inchar != Inbrace)
+		    outbracep[-1] = Outbrace;
+		*s = sav;
+		inchar = Inbrace;	/* Simplify later compares */
+	    } else {
+		zerr("bad substitution");
+		return NULL;
+	    }
+	}
+	if (slen > 1) {
+	    char *outbracep = s + slen;
+	    if (!itok(*s) || inblank(inchar)) {
+		/* This tokenize() is important */
+		char sav = *outbracep;
+		*outbracep = '\0';
+		tokenize(s);
+		*outbracep = sav;
+	    }
+	    if (*outbracep == Outbrace) {
+		if (endvar == s+1) {
+		    /* For consistency with ${} we allow ${{}...} */
+		    rplyvar = NULL;
+		}
+		if (endvar && *endvar == Outbrace) {
+		    cmdarg = dupstrpfx(endvar+1, outbracep-endvar-1);
+		    rplyvar = dupstrpfx(s+1,endvar-s-1);
+		} else {
+		    cmdarg = dupstrpfx(s+1, outbracep-s-1);
+		    rplyvar = "REPLY";
+		}
+		if (inblank(inchar)) {
+		    /*
+		     * Admittedly a hack.  Take advantage of the added
+		     * parameter scope and the semantics of $(<file) to
+		     * construct a command to write/read a temporary file.
+		     * Then fall through to the regular parameter handling
+		     * to manage word splitting, expansion flags, etc.
+		     */
+		    char *outfmt = ">| %s {\n%s\n;}";	/* 13 */
+		    if ((rplytmp = gettempname(NULL, 1))) {
+			/* Prevent shenanigans with $TMPPREFIX */
+			char *tmpfile = quotestring(rplytmp, QT_BACKSLASH);
+			char *dummy = zhalloc(strlen(cmdarg) +
+					      strlen(tmpfile) +
+					      13);
+			sprintf(dummy, outfmt, tmpfile, cmdarg);
+			cmdarg = dummy;
+		    } else {
+			/* TMPPREFIX not writable? */
+			cmdoutval = lastval;
+			cmdarg = NULL;
+		    }
+		}
+		s = outbracep;
+	    }
+	}
+
+	if (rplyvar) {
+	    /* char *rplyval = getsparam("REPLY");  cf. Future? below */
+	    startparamscope(); /* "local" behaves as if in a function */
+	    if (inchar == Bar) {
+		/* rplyvar should be REPLY at this point, but create
+		 * hardwired name anyway to expose any bugs elsewhere
+		 */
+		rplypm = createparam("REPLY", PM_LOCAL|PM_UNSET|PM_HIDE);
+		if (rplypm)	/* Shouldn't createparam() do this? */
+		    rplypm->level = locallevel;
+		/* Future?  Expose global value of $REPLY if any? */
+		/* if (rplyval) setsparam("REPLY", ztrdup(rplyval)); */
+	    } else if (inblank(inchar)) {
+		rplypm = createparam(".zsh.cmdsubst",
+				     PM_LOCAL|PM_UNSET|PM_HIDE|
+				     PM_READONLY_SPECIAL);
+		if (rplypm)
+		    rplypm->level = locallevel;
+	    }
+	    if (inchar != Inbrace && !rplypm) {
+		zerr("failed to create scope for command substitution");
+		return NULL;
+	    }
+	}
+
+	if (rplyvar && cmdarg && *cmdarg) {
+	    int obreaks = breaks;
+	    Eprog cmdprog;
+	    /* Execute the shell command */
+	    queue_signals();
+	    untokenize(cmdarg);
+	    cmdprog = parse_string(cmdarg, 0);
+	    if (cmdprog) {
+		/* exec.c handles dont_queue_signals() */
+		execode(cmdprog, 1, 0, "cmdsubst");
+		cmdoutval = lastval;
+		/* "return" behaves as if in a function */
+		if (retflag) {
+		    retflag = 0;
+		    breaks = obreaks;	/* Is this ever not zero? */
+		}
+	    } else	/* parse error */
+		errflag |= ERRFLAG_ERROR;
+	    if (rplypm)
+		rplypm->node.flags &= ~PM_READONLY_SPECIAL;
+	    if (rplytmp && !errflag) {
+		int onoerrs = noerrs, rplylen;
+		noerrs = 2;
+		rplylen = zstuff(&cmdarg, rplytmp);
+		if (trim) {
+		    /* bash and ksh strip trailing newlines here */
+		    while (rplylen > 0 && cmdarg[rplylen-1] == '\n') {
+			rplylen--;
+			if (trim == 1)
+			    break;
+		    }
+		    cmdarg[rplylen] = 0;
+		}
+		noerrs = onoerrs;
+		if (rplylen >= 0)
+		    setsparam(rplyvar, metafy(cmdarg, rplylen, META_REALLOC));
+	    }
+	    unqueue_signals();
+	}
+
+	if (rplytmp)
+	    unlink(rplytmp);
+	if (rplyvar) {
+	    if (inchar != Inbrace) {
+		if ((val = dupstring(getsparam(rplyvar))))
+		    vunset = 0;
+		else {
+		    vunset = 1;
+		    val = dupstring("");
+		}
+	    } else {
+		s = dyncat(rplyvar, s);
+		rplyvar = NULL;
+	    }
+	    endparamscope();
+	    if (exit_pending) {
+		if (mypid == getpid()) {
+		    /*
+		     * paranoia: don't check for jobs, but there
+		     * shouldn't be any if not interactive.
+		     */
+		    stopmsg = 1;
+		    zexit(exit_val, ZEXIT_NORMAL);
+		} else
+		    _exit(exit_val);
+	    }
+	}
+
 	/*
 	 * In ksh emulation a leading `!' is a special flag working
-	 * sort of like our (k).
+	 * sort of like our (k).  This is true only for arrays or
+	 * associative arrays and only with subscripts [*] or [@],
+	 * so zsh's implementation is approximate.  For namerefs
+	 * in ksh, ${!ref} substitues the parameter name at the
+	 * end of any chain of references, rather than the value.
+	 *
 	 * TODO: this is one of very few cases tied directly to
 	 * the emulation mode rather than an option.  Since ksh
 	 * doesn't have parameter flags it might be neater to
 	 * handle this with the ^, =, ~ stuff, below.
 	 */
 	if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) {
-	    hkeys = SCANPM_WANTKEYS;
+	    hkeys = SCANPM_WANTKEYS|SCANPM_NONAMEREF;
 	    s++;
+	    /* There's a slew of other special bash meanings of parameter
+	     * references that start with "!":
+	     *  ${!name} == ${(P)name} (when name is not a nameref)
+	     *  ${!name*} == ${(k)parameters[(I)name*]}
+	     *  ${!name@} == ${(@k)parameters[(I)name*]}
+	     *  ${!name[*]} == ${(k)name} (but indexes of ordinary arrays, too)
+	     *  ${!name[@]} == ${(@k)name} (ditto, as noted above for ksh)
+	     *
+	     * See also workers/34390, workers/34397, workers/34408.
+	     */
 	} else if (c == '(' || c == Inpar) {
 	    char *t, sav;
 	    int tt = 0;
@@ -2154,10 +2381,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		    escapes = 1;
 		    break;
 
+		case '!':
+		    if ((hkeys|hvals) & ~SCANPM_NONAMEREF)
+			goto flagerr;
+		    hkeys = SCANPM_NONAMEREF;
+		    break;
 		case 'k':
+		    if (hkeys & ~SCANPM_WANTKEYS)
+			goto flagerr;
 		    hkeys = SCANPM_WANTKEYS;
 		    break;
 		case 'v':
+		    if (hvals & ~SCANPM_WANTVALS)
+			goto flagerr;
 		    hvals = SCANPM_WANTVALS;
 		    break;
 
@@ -2308,7 +2544,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
     /*
      * Look for special unparenthesised flags.
      * TODO: could make these able to appear inside parentheses, too,
-     * i.e. ${(^)...} etc.
+     * i.e. ${(^)...} etc., but ${(~)...} already has another meaning.
      */
     for (;;) {
 	if ((c = *s) == '^' || c == Hat) {
@@ -2332,7 +2568,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    }
 	} else if ((c == '#' || c == Pound) &&
 		   (inbrace || !isset(POSIXIDENTIFIERS)) &&
-		   (itype_end(s+1, IIDENT, 0) != s + 1
+		   (itype_end(s+1, INAMESPC, 0) != s + 1
 		    || (cc = s[1]) == '*' || cc == Star || cc == '@'
 		    || cc == '?' || cc == Quest
 		    || cc == '$' || cc == String || cc == Qstring
@@ -2369,8 +2605,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	     * Try to handle this when parameter is named
 	     * by (P) (second part of test).
 	     */
-	    if (itype_end(s+1, IIDENT, 0) != s+1 || (aspar && isstring(s[1]) &&
-				 (s[2] == Inbrace || s[2] == Inpar)))
+	    if (itype_end(s+1, INAMESPC, 0) != s+1 ||
+		(aspar && isstring(s[1]) &&
+		 (s[2] == Inbrace || s[2] == Inpar)))
 		chkset = 1, s++;
 	    else if (!inbrace) {
 		/* Special case for `$+' on its own --- leave unmodified */
@@ -2531,6 +2768,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    scanflags |= SCANPM_DQUOTED;
 	if (chkset)
 	    scanflags |= SCANPM_CHECKING;
+	if (!inbrace)
+	    scanflags |= SCANPM_NONAMESPC;
 	/*
 	 * Second argument: decide whether to use the subexpression or
 	 *   the string next on the line as the parameter name.
@@ -2556,14 +2795,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	 * we let fetchvalue set the main string pointer s to
 	 * the end of the bit it's fetched.
 	 */
-	if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
-			     (wantt ? -1 :
-			      ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
-			     scanflags)) ||
-	    (v->pm && (v->pm->node.flags & PM_UNSET)) ||
-	    (v->flags & VALFLAG_EMPTY))
+	if (!rplyvar &&
+	    (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
+			      (wantt ? -1 :
+			       ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
+			      scanflags)) ||
+	     (v->pm && (v->pm->node.flags & PM_UNSET)) ||
+	     (v->flags & VALFLAG_EMPTY)))
 	    vunset = 1;
-
 	if (wantt) {
 	    /*
 	     * Handle the (t) flag: value now becomes the type
@@ -2573,13 +2812,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 			       !(v->pm->node.flags & PM_UNSET))) {
 		int f = v->pm->node.flags;
 
-		switch (PM_TYPE(f)) {
+		switch (PM_TYPE(f)|(f & PM_NAMEREF)) {
 		case PM_SCALAR:  val = "scalar"; break;
 		case PM_ARRAY:   val = "array"; break;
 		case PM_INTEGER: val = "integer"; break;
 		case PM_EFLOAT:
 		case PM_FFLOAT:  val = "float"; break;
 		case PM_HASHED:  val = "association"; break;
+		case PM_NAMEREF: val = "nameref"; break;
 		}
 		val = dupstring(val);
 		if (v->pm->level)
@@ -2910,6 +3150,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 			chuck(ptr);
 		    else
 			ptr++;
+		} else if (c == Dnull) {
+		    chuck(ptr);
+		    while (*ptr && *ptr != c)
+			ptr++;
+		    if (*ptr == Dnull)
+			chuck(ptr);
+		    ptr--;	/* Outer loop is about to increment */
 		}
 	    }
 	    replstr = (*ptr && ptr[1]) ? ptr+1 : "";
@@ -2926,6 +3173,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	 */
 	if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN)))
 	    flags |= SUB_REST;
+	/* If matching at start and end, don't stop early */
+	if ((flags & (SUB_START|SUB_END)) == (SUB_START|SUB_END))
+	    flags |= SUB_LONG;
 
 	/*
 	 * With ":" treat a value as unset if the variable is set but
@@ -3076,7 +3326,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    if (vunset) {
                 if (isset(EXECOPT)) {
                     *idend = '\0';
-                    zerr("%s: %s", idbeg, *s ? s : "parameter not set");
+		    if (*s){
+			int l;
+			singsub(&s);
+			s = unmetafy(s, &l);
+			zerr("%s: %l", idbeg, s, l);
+		    } else
+			zerr("%s: %s", idbeg, "parameter not set");
                     /*
                      * In interactive shell we need to return to
                      * top-level prompt --- don't clear this error
@@ -3203,7 +3459,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    shortest = 0;
 	    ++s;
 	}
-	if (*itype_end(s, IIDENT, 0)) {
+	if (*itype_end(s, INAMESPC, 0)) {
 	    untokenize(s);
 	    zerr("not an identifier: %s", s);
 	    return NULL;
@@ -3219,6 +3475,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    char *sval;
 	    zip = getaparam(s);
 	    if (!zip) {
+		zip = gethparam(s);
+	    }
+	    if (!zip) {
 		sval = getsparam(s);
 		if (sval)
 		    zip = hmkarray(sval);
@@ -3263,7 +3522,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	int intersect = (*s == '*' || *s == Star);
 	char **compare, **ap, **apsrc;
 	++s;
-	if (*itype_end(s, IIDENT, 0)) {
+	if (*itype_end(s, INAMESPC, 0)) {
 	    untokenize(s);
 	    zerr("not an identifier: %s", s);
 	    return NULL;
@@ -3716,6 +3975,8 @@ colonsubscript:
     if (presc) {
 	int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG];
 	int opp = opts[PROMPTPERCENT];
+	zattr savecurrent = txtcurrentattrs;
+	zattr saveunknown = txtunknownattrs;
 
 	if (presc < 2) {
 	    opts[PROMPTPERCENT] = 1;
@@ -3738,7 +3999,8 @@ colonsubscript:
 	    for (; *ap; ap++) {
 		char *tmps;
 		untokenize(*ap);
-		tmps = promptexpand(*ap, 0, NULL, NULL, NULL);
+		txtunknownattrs = TXT_ATTR_ALL;
+		tmps = promptexpand(*ap, 0, NULL, NULL);
 		*ap = dupstring(tmps);
 		free(tmps);
 	    }
@@ -3747,10 +4009,14 @@ colonsubscript:
 	    if (!copied)
 		val = dupstring(val), copied = 1;
 	    untokenize(val);
-	    tmps = promptexpand(val, 0, NULL, NULL, NULL);
+	    txtunknownattrs = TXT_ATTR_ALL;
+	    tmps = promptexpand(val, 0, NULL, NULL);
 	    val = dupstring(tmps);
 	    free(tmps);
 	}
+
+	txtpendingattrs = txtcurrentattrs = savecurrent;
+	txtunknownattrs = saveunknown;
 	opts[PROMPTSUBST] = ops;
 	opts[PROMPTBANG] = opb;
 	opts[PROMPTPERCENT] = opp;
@@ -4309,6 +4575,8 @@ modify(char **str, char **ptr, int inbrace)
 		break;
 
 	    case 's':
+	    case 'S':
+		hsubpatopt = (**ptr == 'S');
 		c = **ptr;
 		(*ptr)++;
 		ptr1 = *ptr;
@@ -4403,7 +4671,7 @@ modify(char **str, char **ptr, int inbrace)
 		break;
 
 	    case '&':
-		c = 's';
+		c = hsubpatopt ? 'S' : 's';
 		break;
 
 	    case 'g':
@@ -4492,8 +4760,11 @@ modify(char **str, char **ptr, int inbrace)
 			copy = casemodify(tt, CASMOD_UPPER);
 			break;
 		    case 's':
+		    case 'S':
+			hsubpatopt = (c == 'S');
 			if (hsubl && hsubr)
-			    subst(&copy, hsubl, hsubr, gbal);
+			    subst(&copy, dupstring(hsubl), dupstring(hsubr),
+				  gbal, hsubpatopt);
 			break;
 		    case 'q':
 			copy = quotestring(copy, QT_BACKSLASH_SHOWNULL);
@@ -4578,8 +4849,11 @@ modify(char **str, char **ptr, int inbrace)
 		    *str = casemodify(*str, CASMOD_UPPER);
 		    break;
 		case 's':
+		case 'S':
+		    hsubpatopt = (c == 'S');
 		    if (hsubl && hsubr)
-			subst(str, hsubl, hsubr, gbal);
+			subst(str, dupstring(hsubl), dupstring(hsubr),
+			      gbal, hsubpatopt);
 		    break;
 		case 'q':
 		    *str = quotestring(*str, QT_BACKSLASH);
diff --git a/Src/text.c b/Src/text.c
index 56127c457..8b1bd96b6 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -335,7 +335,7 @@ getjobtext(Eprog prog, Wordcode c)
     tlim = tptr + JOBTEXTSIZE - 1;
     tjob = 1;
     gettext2(&s);
-    if (tptr[-1] == Meta)
+    if (tptr > jbuf && tptr[-1] == Meta)
 	--tptr;
     *tptr = '\0';
     freeeprog(prog);		/* mark as unused */
diff --git a/Src/utils.c b/Src/utils.c
index 62bd3e602..ce4e875fd 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -74,9 +74,6 @@ set_widearray(char *mb_array, Widechar_array wca)
     }
     wca->len = 0;
 
-    if (!isset(MULTIBYTE))
-	return;
-
     if (mb_array) {
 	VARARR(wchar_t, tmpwcs, strlen(mb_array));
 	wchar_t *wcptr = tmpwcs;
@@ -86,9 +83,8 @@ set_widearray(char *mb_array, Widechar_array wca)
 	while (*mb_array) {
 	    int mblen;
 
-	    if (STOUC(*mb_array) <= 0x7f) {
-		mb_array++;
-		*wcptr++ = (wchar_t)*mb_array;
+	    if ((unsigned char) *mb_array <= 0x7f) {
+		*wcptr++ = (wchar_t)*mb_array++;
 		continue;
 	    }
 
@@ -126,14 +122,20 @@ set_widearray(char *mb_array, Widechar_array wca)
    (implemented by zerrmsg()):
 
    Code	Argument types		Prints
-   %s	const char *		C string (null terminated)
-   %l	const char *, int	C string of given length (null not required)
+   %s	const char *		C string (metafied, null terminated)
+     	                 	(output "nice")
+   %l	const char *, int	C string of given length (not metafied)
+     	                 	(output raw)
    %L	long			decimal value
    %d	int			decimal value
    %z	zlong			decimal value
    %%	(none)			literal '%'
    %c	int			character at that codepoint
-   %e	int			strerror() message (argument is typically 'errno')
+   %e	int			strerror() message (argument usually 'errno')
+     	                 	(output raw)
+
+   For %s and %l, the caller is responsible for assuring end-of-string
+   is not in the middle of a metafy pair (%s) or a multibyte character.
  */
 
 static void
@@ -314,14 +316,9 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
 		nicezputs(str, file);
 		break;
 	    case 'l': {
-		char *s;
 		str = va_arg(ap, const char *);
 		num = va_arg(ap, int);
-		num = metalen(str, num);
-		s = zhalloc(num + 1);
-		memcpy(s, str, num);
-		s[num] = '\0';
-		nicezputs(s, file);
+		fwrite(str, num, 1, file);
 		break;
 	    }
 	    case 'L':
@@ -380,11 +377,13 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
     fflush(file);
 }
 
+#ifdef HAVE_SETUPTERM
 /*
  * Wrapper for setupterm() and del_curterm().
  * These are called from terminfo.c and termcap.c.
  */
 static int term_count;	/* reference count of cur_term */
+#endif
 
 /**/
 mod_export void
@@ -717,7 +716,8 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
  */
 
 /**/
-mod_export int is_wcs_nicechar(wchar_t c)
+mod_export int
+is_wcs_nicechar(wchar_t c)
 {
     if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
 	if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
@@ -1071,7 +1071,7 @@ get_username(void)
 	cached_uid = current_uid;
 	zsfree(cached_username);
 	if ((pswd = getpwuid(current_uid)))
-	    cached_username = ztrdup(pswd->pw_name);
+	    cached_username = ztrdup_metafy(pswd->pw_name);
 	else
 	    cached_username = ztrdup("");
     }
@@ -1543,7 +1543,8 @@ preprompt(void)
 	if (!eolmark)
 	    eolmark = "%B%S%#%s%b";
 	opts[PROMPTPERCENT] = 1;
-	str = promptexpand(eolmark, 1, NULL, NULL, NULL);
+	txtunknownattrs = TXT_ATTR_ALL;
+	str = promptexpand(eolmark, 1, NULL, NULL);
 	countprompt(str, &w, 0, -1);
 	opts[PROMPTPERCENT] = percents;
 	zputs(str, shout);
@@ -1713,7 +1714,7 @@ printprompt4(void)
 	opts[XTRACE] = 0;
 	unmetafy(s, &l);
 	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC),
-				  0, NULL, NULL, NULL), &l);
+				  0, NULL, NULL), &l);
 	opts[XTRACE] = t;
 
 	fprintf(xtrerr, "%s", s);
@@ -1732,6 +1733,13 @@ freestr(void *a)
 mod_export void
 gettyinfo(struct ttyinfo *ti)
 {
+    fdgettyinfo(SHTTY, ti);
+}
+
+/**/
+mod_export void
+fdgettyinfo(int SHTTY, struct ttyinfo *ti)
+{
     if (SHTTY != -1) {
 #ifdef HAVE_TERMIOS_H
 # ifdef HAVE_TCGETATTR
@@ -1757,6 +1765,13 @@ gettyinfo(struct ttyinfo *ti)
 mod_export void
 settyinfo(struct ttyinfo *ti)
 {
+    fdsettyinfo(SHTTY, ti);
+}
+
+/**/
+mod_export void
+fdsettyinfo(int SHTTY, struct ttyinfo *ti)
+{
     if (SHTTY != -1) {
 #ifdef HAVE_TERMIOS_H
 # ifdef HAVE_TCGETATTR
@@ -1995,6 +2010,28 @@ redup(int x, int y)
 {
     int ret = y;
 
+#ifdef HAVE_FPURGE
+    /* Make sure buffers are cleared when changing descriptor for a
+     * FILE object.  No fflush() here because the only way anything
+     * can legitimately be left in the buffer is when an error has
+     * occurred, so attempting flush here would at best error again
+     * and at worst squirt out something unexpected.
+     */
+    if (stdout && y == fileno(stdout))
+	fpurge(stdout);
+    if (stderr && y == fileno(stderr))
+	fpurge(stderr);
+    if (shout && y == fileno(shout))
+	fpurge(shout);
+    if (xtrerr && y == fileno(xtrerr))
+	fpurge(xtrerr);
+#ifndef _IONBF
+    /* See init.c setupshin() -- stdin otherwise unbuffered */
+    if (stdin && y == fileno(stdin))
+	fpurge(stdin);
+#endif
+#endif
+
     if(x < 0)
 	zclose(y);
     else if (x != y) {
@@ -2920,7 +2957,7 @@ read1char(int echo)
     restore_queue_signals(q);
     if (echo)
 	write_loop(SHTTY, &c, 1);
-    return STOUC(c);
+    return (unsigned char) c;
 }
 
 /**/
@@ -3122,7 +3159,7 @@ spckword(char **s, int hist, int cmd, int ask)
 
     if (**s == String && !*t) {
 	guess = *s + 1;
-	if (itype_end(guess, IIDENT, 1) == guess)
+	if (itype_end(guess, INAMESPC, 1) == guess)
 	    return;
 	ic = String;
 	d = 100;
@@ -3211,7 +3248,7 @@ spckword(char **s, int hist, int cmd, int ask)
 		x = 'n';
 	    } else if (shout) {
 		char *pptbuf;
-		pptbuf = promptexpand(sprompt, 0, best, guess, NULL);
+		pptbuf = promptexpand(sprompt, 0, best, guess);
 		zputs(pptbuf, shout);
 		free(pptbuf);
 		fflush(shout);
@@ -4118,27 +4155,41 @@ inittyptab(void)
      * having IIDENT here is a good idea at all, but this code
      * should disappear into history...
      */
-    for (t0 = 0240; t0 != 0400; t0++)
-	typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
+    if isset(MULTIBYTE)
+	for (t0 = 0240; t0 != 0400; t0++)
+	    typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD;
 #endif
     /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */
     typtab['_'] = IIDENT | IUSER;
-    typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER;
+    typtab['-'] = typtab['.'] = typtab[(unsigned char) Dash] = IUSER;
     typtab[' '] |= IBLANK | INBLANK;
     typtab['\t'] |= IBLANK | INBLANK;
     typtab['\n'] |= INBLANK;
     typtab['\0'] |= IMETA;
-    typtab[STOUC(Meta)  ] |= IMETA;
-    typtab[STOUC(Marker)] |= IMETA;
-    for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++)
+    typtab[(unsigned char) Meta  ] |= IMETA;
+    typtab[(unsigned char) Marker] |= IMETA;
+    for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) LAST_NORMAL_TOK; t0++)
 	typtab[t0] |= ITOK | IMETA;
-    for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++)
+    for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++)
 	typtab[t0] |= ITOK | IMETA | INULL;
-    for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
-	DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) {
-	int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
+     /* ifs */
+#define CURRENT_DEFAULT_IFS (EMULATION(EMULATE_KSH|EMULATE_SH) ? \
+			    DEFAULT_IFS_SH : DEFAULT_IFS)
+#ifdef MULTIBYTE_SUPPORT
+    if (isset(MULTIBYTE)) {
+	set_widearray(ifs ? ifs : CURRENT_DEFAULT_IFS, &ifs_wide);
+	if (ifs && !ifs_wide.chars) {
+	    zwarn("IFS has an invalid character; resetting IFS to default");
+	    zsfree(ifs);
+	    ifs = ztrdup(CURRENT_DEFAULT_IFS);
+	    set_widearray(ifs, &ifs_wide);
+	}
+    }
+#endif
+     for (s = ifs ? ifs : CURRENT_DEFAULT_IFS; *s; s++) {
+	int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s);
 #ifdef MULTIBYTE_SUPPORT
-	if (!isascii(c)) {
+	if (isset(MULTIBYTE) && !isascii(c)) {
 	    /* see comment for wordchars below */
 	    continue;
 	}
@@ -4151,10 +4202,15 @@ inittyptab(void)
 	}
 	typtab[c] |= ISEP;
     }
+    /* wordchars */
+#ifdef MULTIBYTE_SUPPORT
+    if (isset(MULTIBYTE))
+	set_widearray(wordchars, &wordchars_wide);
+#endif
     for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) {
-	int c = STOUC(*s == Meta ? *++s ^ 32 : *s);
+	int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s);
 #ifdef MULTIBYTE_SUPPORT
-	if (!isascii(c)) {
+	if (isset(MULTIBYTE) && !isascii(c)) {
 	    /*
 	     * If we have support for multibyte characters, we don't
 	     * handle non-ASCII characters here; instead, we turn
@@ -4167,22 +4223,17 @@ inittyptab(void)
 #endif
 	typtab[c] |= IWORD;
     }
-#ifdef MULTIBYTE_SUPPORT
-    set_widearray(wordchars, &wordchars_wide);
-    set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ?
-	DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide);
-#endif
     for (s = SPECCHARS; *s; s++)
-	typtab[STOUC(*s)] |= ISPECIAL;
+	typtab[(unsigned char) *s] |= ISPECIAL;
     if (typtab_flags & ZTF_SP_COMMA)
-	typtab[STOUC(',')] |= ISPECIAL;
+	typtab[(unsigned char) ','] |= ISPECIAL;
     if (isset(BANGHIST) && bangchar && (typtab_flags & ZTF_INTERACT)) {
 	typtab_flags |= ZTF_BANGCHAR;
 	typtab[bangchar] |= ISPECIAL;
     } else
 	typtab_flags &= ~ZTF_BANGCHAR;
     for (s = PATCHARS; *s; s++)
-	typtab[STOUC(*s)] |= IPATTERN;
+	typtab[(unsigned char) *s] |= IPATTERN;
 
     unqueue_signals();
 }
@@ -4193,10 +4244,10 @@ makecommaspecial(int yesno)
 {
     if (yesno != 0) {
 	typtab_flags |= ZTF_SP_COMMA;
-	typtab[STOUC(',')] |= ISPECIAL;
+	typtab[(unsigned char) ','] |= ISPECIAL;
     } else {
 	typtab_flags &= ~ZTF_SP_COMMA;
-	typtab[STOUC(',')] &= ~ISPECIAL;
+	typtab[(unsigned char) ','] &= ~ISPECIAL;
     }
 }
 
@@ -4309,13 +4360,27 @@ wcsitype(wchar_t c, int itype)
  * If "once" is set, just test the first character, i.e. (outptr !=
  * inptr) tests whether the first character is valid in an identifier.
  *
- * Currently this is only called with itype IIDENT, IUSER or ISEP.
+ * Currently called only with itype INAMESPC, IIDENT, IUSER or ISEP.
  */
 
 /**/
 mod_export char *
 itype_end(const char *ptr, int itype, int once)
 {
+    if (itype == INAMESPC) {
+	itype = IIDENT;
+	if (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH)) {
+	    /* Special case for names containing ".", ksh93 namespaces */
+	    char *t = itype_end(ptr + (*ptr == '.'), itype, 0);
+	    if (t > ptr + (*ptr == '.')) {
+		if (*t == '.')
+		    ptr = t + 1;	/* Fall through */
+		else if (!once)
+		    return t;
+	    }
+	}
+    }
+
 #ifdef MULTIBYTE_SUPPORT
     if (isset(MULTIBYTE) &&
 	(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
@@ -4336,7 +4401,7 @@ itype_end(const char *ptr, int itype, int once)
 
 		if (wc == WEOF) {
 		    /* invalid, treat as single character */
-		    int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
+		    int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr);
 		    /* in this case non-ASCII characters can't match */
 		    if (chr > 127 || !zistype(chr,itype))
 			break;
@@ -4375,7 +4440,7 @@ itype_end(const char *ptr, int itype, int once)
     } else
 #endif
 	for (;;) {
-	    int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
+	    int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr);
 	    if (!zistype(chr,itype))
 		break;
 	    ptr += (*ptr == Meta) ? 2 : 1;
@@ -4983,11 +5048,11 @@ unmeta_one(const char *in, int *sz)
     *sz = mb_metacharlenconv_r(in, &wc, &wstate);
 #else
     if (in[0] == Meta) {
-      *sz = 2;
-      wc = STOUC(in[1] ^ 32);
+	*sz = 2;
+	wc = (unsigned char) (in[1] ^ 32);
     } else {
-      *sz = 1;
-      wc = STOUC(in[0]);
+	*sz = 1;
+	wc = (unsigned char) in[0];
     }
 #endif
     return wc;
@@ -5022,11 +5087,11 @@ ztrcmp(char const *s1, char const *s2)
 
     if(!(c1 = *s1))
 	c1 = -1;
-    else if(c1 == STOUC(Meta))
+    else if(c1 == (unsigned char) Meta)
 	c1 = *++s1 ^ 32;
     if(!(c2 = *s2))
 	c2 = -1;
-    else if(c2 == STOUC(Meta))
+    else if(c2 == (unsigned char) Meta)
 	c2 = *++s2 ^ 32;
 
     if(c1 == c2)
@@ -5212,6 +5277,7 @@ nicedupstring(char const *s)
 }
 
 
+/**/
 #ifndef MULTIBYTE_SUPPORT
 /* Unmetafy and output a string, displaying special characters readably. */
 
@@ -5246,8 +5312,9 @@ niceztrlen(char const *s)
     }
     return l;
 }
-#endif
 
+/**/
+#endif
 
 /**/
 #ifdef MULTIBYTE_SUPPORT
@@ -5458,7 +5525,7 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
     const char *ptr;
     wchar_t wc;
 
-    if (STOUC(*s) <= 0x7f) {
+    if ((unsigned char) *s <= 0x7f) {
 	if (wcp)
 	    *wcp = (wint_t)*s;
 	return 1;
@@ -5516,10 +5583,10 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
 mod_export int
 mb_metacharlenconv(const char *s, wint_t *wcp)
 {
-    if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
+    if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) {
 	/* treat as single byte, possibly metafied */
 	if (wcp)
-	    *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s);
+	    *wcp = (wint_t)(unsigned char) (*s == Meta ? s[1] ^ 32 : *s);
 	return 1 + (*s == Meta);
     }
     /*
@@ -5581,7 +5648,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
 	    inchar = *ptr;
 	ptr++;
 
-	if (complete && STOUC(inchar) <= STOUC(0x7f)) {
+	if (complete && (unsigned char) inchar <= (unsigned char) 0x7f) {
 	    /*
 	     * We rely on 7-bit US-ASCII as a subset, so skip
 	     * multibyte handling if we have such a character.
@@ -5657,7 +5724,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
     const char *ptr;
     wchar_t wc;
 
-    if (slen && STOUC(*s) <= 0x7f) {
+    if (slen && (unsigned char) *s <= 0x7f) {
 	if (wcp)
 	    *wcp = (wint_t)*s;
 	return 1;
@@ -5698,7 +5765,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
 mod_export int
 mb_charlenconv(const char *s, int slen, wint_t *wcp)
 {
-    if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
+    if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) {
 	if (wcp)
 	    *wcp = (wint_t)*s;
 	return 1;
@@ -5717,7 +5784,7 @@ mod_export int
 metacharlenconv(const char *x, int *c)
 {
     /*
-     * Here we don't use STOUC() on the chars since they
+     * Here we don't use an (unsigned char)  cast on the chars since they
      * may be compared against other chars and this will fail
      * if chars are signed and the high bit is set.
      */
@@ -5779,7 +5846,7 @@ sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
     eptr = ptr + umlen;
 
     while (ptr < eptr) {
-	int c = STOUC(*ptr);
+	int c = (unsigned char) *ptr;
 	if (c == '\'' && (flags & NICEFLAG_QUOTE)) {
 	    fmt = "\\'";
 	    newl = 2;
@@ -5996,9 +6063,9 @@ addunprintable(char *v, const char *u, const char *uend)
 	 */
 	int c;
 	if (*u == Meta)
-	    c = STOUC(*++u ^ 32);
+	    c = (unsigned char) (*++u ^ 32);
 	else
-	    c = STOUC(*u);
+	    c = (unsigned char) *u;
 	switch (c) {
 	case '\0':
 	    *v++ = '\\';
@@ -6645,11 +6712,14 @@ dquotedzputs(char const *s, FILE *stream)
 # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__)
 /* Convert a character from UCS4 encoding to UTF-8 */
 
-static size_t
+static int
 ucs4toutf8(char *dest, unsigned int wval)
 {
-    size_t len;
+    int len;
 
+    /* UCS4 is now equvalent to UTF-32 and limited to 0 - 0x10_FFFF.
+     * This function accepts 0 - 0x7FFF_FFFF (old range of UCS4) to be
+     * compatible with wctomb(3) (in UTF-8 locale) on Linux. */
     if (wval < 0x80)
       len = 1;
     else if (wval < 0x800)
@@ -6660,8 +6730,12 @@ ucs4toutf8(char *dest, unsigned int wval)
       len = 4;
     else if (wval < 0x4000000)
       len = 5;
-    else
+    else if (wval < 0x80000000)
       len = 6;
+    else {
+      zerr("character not in range");
+      return -1;
+    }
 
     switch (len) { /* falls through except to the last case */
     case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6;
@@ -6678,30 +6752,89 @@ ucs4toutf8(char *dest, unsigned int wval)
 }
 #endif
 
+/* Convert UCS4 to a multibyte character in current locale.
+ * Result is saved in buf (must be at least MB_CUR_MAX bytes long).
+ * Returns the number of bytes saved in buf, or -1 if conversion fails. */
 
-/*
- * The following only occurs once or twice in the code, but in different
- * places depending how character set conversion is implemented.
- */
-#define CHARSET_FAILED()		      \
-    if (how & GETKEY_DOLLAR_QUOTE) {	      \
-	while ((*tdest++ = *++s)) {	      \
-	    if (how & GETKEY_UPDATE_OFFSET) { \
-		if (s - sstart > *misc)	      \
-		    (*misc)++;		      \
-	    }				      \
-	    if (*s == Snull) {		      \
-		*len = (s - sstart) + 1;      \
-		*tdest = '\0';		      \
-		return buf;		      \
-	    }				      \
-	}				      \
-	*len = tdest - buf;		      \
-	return buf;			      \
-    }					      \
-    *t = '\0';				      \
-    *len = t - buf;			      \
-    return buf
+/**/
+int
+ucs4tomb(unsigned int wval, char *buf)
+{
+#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
+    int count = wctomb(buf, (wchar_t)wval);
+    if (count == -1)
+	zerr("character not in range");
+    return count;
+#else	/* !(HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__) */
+# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
+    if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
+	return ucs4toutf8(buf, wval);
+    } else {
+#   ifdef HAVE_ICONV
+	iconv_t cd;
+	char inbuf[4], *bsave = buf;
+	ICONV_CONST char *inptr = inbuf;
+	size_t inbytes = 4, outbytes = 6;
+	const char *codesetstr = nl_langinfo(CODESET);
+	size_t count;
+	int i;
+
+	/*
+	 * If the code set isn't handled, we'd better assume it's US-ASCII
+	 * rather than just failing hopelessly.  Solaris has a weird habit
+	 * of returning 646.  This is handled by the native iconv(), but
+	 * not by GNU iconv; what's more, some versions of the native iconv
+	 * don't handle standard names like ASCII.
+	 *
+	 * This should only be a problem if there's a mismatch between the
+	 * NLS and the iconv in use, which probably only means if libiconv
+	 * is in use.  We checked at configure time if our libraries pulled
+	 * in _libiconv_version, which should be a good test.
+	 *
+	 * It shouldn't ever be NULL, but while we're being paranoid...
+	 */
+#     ifdef ICONV_FROM_LIBICONV
+	if (!codesetstr || !*codesetstr)
+	    codesetstr = "US-ASCII";
+#     endif
+	cd = iconv_open(codesetstr, "UCS-4BE");
+#     ifdef ICONV_FROM_LIBICONV
+	if (cd == (iconv_t)-1 &&  !strcmp(codesetstr, "646")) {
+	    codesetstr = "US-ASCII";
+	    cd = iconv_open(codesetstr, "UCS-4BE");
+	}
+#     endif
+	if (cd == (iconv_t)-1) {
+	    zerr("cannot do charset conversion (iconv failed)");
+	    return -1;
+	}
+
+	/* store value in big endian form */
+	for (i=3; i>=0; i--) {
+	    inbuf[i] = wval & 0xff;
+	    wval >>= 8;
+	}
+	count = iconv(cd, &inptr, &inbytes, &buf, &outbytes);
+	iconv_close(cd);
+	if (count) {
+	    /* -1 indicates error. Positive value means number of "invalid"
+	     * (or "non-reversible") conversions, which we consider as
+	     * "out-of-range" characters. */
+	    zerr("character not in range");
+	    return -1;
+	}
+	return buf - bsave;
+#   else    /* !HAVE_ICONV */
+	zerr("cannot do charset conversion (iconv not available)");
+	return -1;
+#   endif   /* HAVE_ICONV */
+    }
+# else	/* !(HAVE_NL_LANGINFO && CODESET) */
+    zerr("cannot do charset conversion (NLS not supported)");
+    return -1;
+# endif	/* HAVE_NL_LANGINFO && CODESET */
+#endif	/* HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__ */
+}
 
 /*
  * Decode a key string, turning it into the literal characters.
@@ -6758,21 +6891,6 @@ getkeystring(char *s, int *len, int how, int *misc)
     char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL;
     char svchar = '\0';
     int meta = 0, control = 0, ignoring = 0;
-    int i;
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
-    wint_t wval;
-    int count;
-#else
-    unsigned int wval;
-# if defined(HAVE_NL_LANGINFO) && defined(CODESET)
-#  if defined(HAVE_ICONV)
-    iconv_t cd;
-    char inbuf[4];
-    size_t inbytes, outbytes;
-#  endif
-    size_t count;
-# endif
-#endif
 
     DPUTS((how & GETKEY_UPDATE_OFFSET) &&
 	  (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)),
@@ -6837,7 +6955,8 @@ getkeystring(char *s, int *len, int how, int *misc)
     }
     for (; *s; s++) {
 	if (*s == '\\' && s[1]) {
-	    int miscadded;
+	    int miscadded, count, i;
+	    unsigned int wval;
 	    if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) {
 		(*misc)--;
 		miscadded = 1;
@@ -6952,86 +7071,32 @@ getkeystring(char *s, int *len, int how, int *misc)
 		    *misc = wval;
 		    return s+1;
 		}
-#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__)
-		count = wctomb(t, (wchar_t)wval);
+		count = ucs4tomb(wval, t);
 		if (count == -1) {
-		    zerr("character not in range");
-		    CHARSET_FAILED();
+		    if (how & GETKEY_DOLLAR_QUOTE) {
+			while ((*tdest++ = *++s)) {
+			    if (how & GETKEY_UPDATE_OFFSET) {
+				if (s - sstart > *misc)
+				    (*misc)++;
+			    }
+			    if (*s == Snull) {
+				*len = (s - sstart) + 1;
+				*tdest = '\0';
+				return buf;
+			    }
+			}
+			*len = tdest - buf;
+		    }
+		    else {
+			*t = '\0';
+			*len = t - buf;
+		    }
+		    return buf;
 		}
 		if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
 		    (*misc) += count;
 		t += count;
-# else
-#  if defined(HAVE_NL_LANGINFO) && defined(CODESET)
-		if (!strcmp(nl_langinfo(CODESET), "UTF-8")) {
-		    count = ucs4toutf8(t, wval);
-		    t += count;
-		    if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
-			(*misc) += count;
-		} else {
-#   ifdef HAVE_ICONV
-		    ICONV_CONST char *inptr = inbuf;
-		    const char *codesetstr = nl_langinfo(CODESET);
-    	    	    inbytes = 4;
-		    outbytes = 6;
-		    /* store value in big endian form */
-		    for (i=3;i>=0;i--) {
-			inbuf[i] = wval & 0xff;
-			wval >>= 8;
-		    }
 
-		    /*
-		     * If the code set isn't handled, we'd better
-		     * assume it's US-ASCII rather than just failing
-		     * hopelessly.  Solaris has a weird habit of
-		     * returning 646.  This is handled by the
-		     * native iconv(), but not by GNU iconv; what's
-		     * more, some versions of the native iconv don't
-		     * handle standard names like ASCII.
-		     *
-		     * This should only be a problem if there's a
-		     * mismatch between the NLS and the iconv in use,
-		     * which probably only means if libiconv is in use.
-		     * We checked at configure time if our libraries
-		     * pulled in _libiconv_version, which should be
-		     * a good test.
-		     *
-		     * It shouldn't ever be NULL, but while we're
-		     * being paranoid...
-		     */
-#ifdef ICONV_FROM_LIBICONV
-		    if (!codesetstr || !*codesetstr)
-			codesetstr = "US-ASCII";
-#endif
-    	    	    cd = iconv_open(codesetstr, "UCS-4BE");
-#ifdef ICONV_FROM_LIBICONV
-		    if (cd == (iconv_t)-1 &&  !strcmp(codesetstr, "646")) {
-			codesetstr = "US-ASCII";
-			cd = iconv_open(codesetstr, "UCS-4BE");
-		    }
-#endif
-		    if (cd == (iconv_t)-1) {
-			zerr("cannot do charset conversion (iconv failed)");
-			CHARSET_FAILED();
-		    }
-                    count = iconv(cd, &inptr, &inbytes, &t, &outbytes);
-		    iconv_close(cd);
-		    if (count == (size_t)-1) {
-                        zerr("character not in range");
-			CHARSET_FAILED();
-		    }
-		    if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc)
-			(*misc) += count;
-#   else
-                    zerr("cannot do charset conversion (iconv not available)");
-		    CHARSET_FAILED();
-#   endif
-		}
-#  else
-                zerr("cannot do charset conversion (NLS not supported)");
-		CHARSET_FAILED();
-#  endif
-# endif
 		if (how & GETKEY_DOLLAR_QUOTE) {
 		    char *t2;
 		    for (t2 = tbuf; t2 < t; t2++) {
@@ -7104,7 +7169,7 @@ getkeystring(char *s, int *len, int how, int *misc)
 	    continue;
 #ifdef MULTIBYTE_SUPPORT
 	} else if ((how & GETKEY_SINGLE_CHAR) &&
-		   isset(MULTIBYTE) && STOUC(*s) > 127) {
+		   isset(MULTIBYTE) && (unsigned char) *s > 127) {
 	    wint_t wc;
 	    int len;
 	    len = mb_metacharlenconv(s, &wc);
@@ -7207,7 +7272,7 @@ getkeystring(char *s, int *len, int how, int *misc)
 	    t = tbuf;
 	}
 	if ((how & GETKEY_SINGLE_CHAR) && t != tmp) {
-	    *misc = STOUC(tmp[0]);
+	    *misc = (unsigned char) tmp[0];
 	    return s + 1;
 	}
     }
@@ -7498,8 +7563,8 @@ restoredir(struct dirsav *d)
     else if (d->level < 0)
 	err = -1;
     if (d->dev || d->ino) {
-	stat(".", &sbuf);
-	if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
+	if (stat(".", &sbuf) < 0 ||
+	    sbuf.st_ino != d->ino || sbuf.st_dev != d->dev)
 	    err = -2;
     }
     return err;
@@ -7524,9 +7589,9 @@ privasserted(void)
 	    /* POSIX doesn't define a way to test whether a capability set *
 	     * is empty or not.  Typical.  I hope this is conforming...    */
 	    cap_flag_value_t val;
-	    cap_value_t n;
-	    for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++)
-		if(val) {
+	    cap_value_t cap;
+	    for(cap = 0; !cap_get_flag(caps, cap, CAP_EFFECTIVE, &val); cap++)
+		if(val && cap != CAP_WAKE_ALARM) {
 		    cap_free(caps);
 		    return 1;
 		}
@@ -7570,6 +7635,7 @@ mode_to_octal(mode_t mode)
     return m;
 }
 
+/**/
 #ifdef MAILDIR_SUPPORT
 /*
  *     Stat a file. If it's a maildir, check all messages
@@ -7693,4 +7759,6 @@ mailstat(char *path, struct stat *st)
        *st = st_ret_last = st_ret;
        return 0;
 }
+
+/**/
 #endif
diff --git a/Src/zsh.h b/Src/zsh.h
index 40f9ea537..090abf8f5 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -135,19 +135,6 @@ struct mathfunc {
 #define STRMATHFUNC(name, func, id) \
     { NULL, name, MFF_STR, NULL, func, NULL, 0, 0, id }
 
-/* Character tokens are sometimes casted to (unsigned char)'s.         * 
- * Unfortunately, some compilers don't correctly cast signed to        * 
- * unsigned promotions; i.e. (int)(unsigned char)((char) -1) evaluates * 
- * to -1, instead of 255 like it should.  We circumvent the troubles   * 
- * of such shameful delinquency by casting to a larger unsigned type   * 
- * then back down to unsigned char.                                    */
-
-#ifdef BROKEN_SIGNED_TO_UNSIGNED_CASTING
-# define STOUC(X)	((unsigned char)(unsigned short)(X))
-#else
-# define STOUC(X)	((unsigned char)(X))
-#endif
-
 /* Meta together with the character following Meta denotes the character *
  * which is the exclusive or of 32 and the character following Meta.     *
  * This is used to represent characters which otherwise has special      *
@@ -309,6 +296,9 @@ enum {
 /*
  * Lexical tokens: unlike the character tokens above, these never
  * appear in strings and don't necessarily represent a single character.
+ *
+ * See Src/lex.c:tokstrings[] for hints on what these mean.  Note that
+ * SEPER or SEMI are both stringified as ";".
  */
 
 enum lextok {
@@ -628,7 +618,7 @@ union linkroot {
 /* Specific elements of linked lists */
 /*************************************/
 
-typedef void (*voidvoidfnptr_t) _((void));
+typedef void (*voidvoidfnptr_t) (void);
 
 /*
  * Element of the prepromptfns list.
@@ -688,7 +678,7 @@ struct timedfn {
 #define COND_MOD   18
 #define COND_MODI  19
 
-typedef int (*CondHandler) _((char **, int));
+typedef int (*CondHandler) (char **, int);
 
 struct conddef {
     Conddef next;		/* next in list                       */
@@ -1174,28 +1164,28 @@ struct dirsav {
 /* Definitions for Hash Tables */
 /*******************************/
 
-typedef void *(*VFunc) _((void *));
-typedef void (*FreeFunc) _((void *));
+typedef void *(*VFunc) (void *);
+typedef void (*FreeFunc) (void *);
 
-typedef unsigned (*HashFunc)       _((const char *));
-typedef void     (*TableFunc)      _((HashTable));
+typedef unsigned (*HashFunc)       (const char *);
+typedef void     (*TableFunc)      (HashTable);
 /*
  * Note that this is deliberately "char *", not "const char *",
  * since the AddNodeFunc is passed a pointer to a string that
  * will be stored and later freed.
  */
-typedef void     (*AddNodeFunc)    _((HashTable, char *, void *));
-typedef HashNode (*GetNodeFunc)    _((HashTable, const char *));
-typedef HashNode (*RemoveNodeFunc) _((HashTable, const char *));
-typedef void     (*FreeNodeFunc)   _((HashNode));
-typedef int      (*CompareFunc)    _((const char *, const char *));
+typedef void     (*AddNodeFunc)    (HashTable, char *, void *);
+typedef HashNode (*GetNodeFunc)    (HashTable, const char *);
+typedef HashNode (*RemoveNodeFunc) (HashTable, const char *);
+typedef void     (*FreeNodeFunc)   (HashNode);
+typedef int      (*CompareFunc)    (const char *, const char *);
 
 /* type of function that is passed to *
  * scanhashtable or scanmatchtable    */
-typedef void     (*ScanFunc)       _((HashNode, int));
-typedef void     (*ScanTabFunc)    _((HashTable, ScanFunc, int));
+typedef void     (*ScanFunc)       (HashNode, int);
+typedef void     (*ScanTabFunc)    (HashTable, ScanFunc, int);
 
-typedef void (*PrintTableStats) _((HashTable));
+typedef void (*PrintTableStats) (HashTable);
 
 /* Hash table for standard open hashing. Instances of struct hashtable can be *
  * created only by newhashtable(). In fact, this function creates an instance *
@@ -1362,7 +1352,7 @@ struct funcstack {
 
 /* node in list of function call wrappers */
 
-typedef int (*WrapFunc) _((Eprog, FuncWrap, char *));
+typedef int (*WrapFunc) (Eprog, FuncWrap, char *);
 
 struct funcwrap {
     FuncWrap next;
@@ -1438,8 +1428,8 @@ enum {
  * builtin structure.
  */
 
-typedef int (*HandlerFunc) _((char *, char **, Options, int));
-typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int));
+typedef int (*HandlerFunc) (char *, char **, Options, int);
+typedef int (*HandlerFuncAssign) (char *, char **, LinkList, Options, int);
 #define NULLBINCMD ((HandlerFunc) 0)
 
 struct builtin {
@@ -1536,10 +1526,10 @@ struct module {
 /* Module record is an alias */
 #define MOD_ALIAS   (1<<6)
 
-typedef int (*Module_generic_func) _((void));
-typedef int (*Module_void_func) _((Module));
-typedef int (*Module_features_func) _((Module, char ***));
-typedef int (*Module_enables_func) _((Module, int **));
+typedef int (*Module_generic_func) (void);
+typedef int (*Module_void_func) (Module);
+typedef int (*Module_features_func) (Module, char ***);
+typedef int (*Module_enables_func) (Module, int **);
 
 struct linkedmod {
     char *name;
@@ -1584,11 +1574,11 @@ struct feature_enables {
 
 /* C-function hooks */
 
-typedef int (*Hookfn) _((Hookdef, void *));
+typedef int (*Hookfn) (Hookdef, void *);
 
 struct hookdef {
     Hookdef next;
-    char *name;
+    const char *name;
     Hookfn def;
     int flags;
     LinkList funcs;
@@ -1799,33 +1789,33 @@ typedef const struct gsu_array *GsuArray;
 typedef const struct gsu_hash *GsuHash;
 
 struct gsu_scalar {
-    char *(*getfn) _((Param));
-    void (*setfn) _((Param, char  *));
-    void (*unsetfn) _((Param, int));
+    char *(*getfn) (Param);
+    void (*setfn) (Param, char  *);
+    void (*unsetfn) (Param, int);
 };
 
 struct gsu_integer {
-    zlong (*getfn) _((Param));
-    void (*setfn) _((Param, zlong));
-    void (*unsetfn) _((Param, int));
+    zlong (*getfn) (Param);
+    void (*setfn) (Param, zlong);
+    void (*unsetfn) (Param, int);
 };
 
 struct gsu_float {
-    double (*getfn) _((Param));
-    void (*setfn) _((Param, double));
-    void (*unsetfn) _((Param, int));
+    double (*getfn) (Param);
+    void (*setfn) (Param, double);
+    void (*unsetfn) (Param, int);
 };
 
 struct gsu_array {
-    char **(*getfn) _((Param));
-    void (*setfn) _((Param, char **));
-    void (*unsetfn) _((Param, int));
+    char **(*getfn) (Param);
+    void (*setfn) (Param, char **);
+    void (*unsetfn) (Param, int);
 };
 
 struct gsu_hash {
-    HashTable (*getfn) _((Param));
-    void (*setfn) _((Param, HashTable));
-    void (*unsetfn) _((Param, int));
+    HashTable (*getfn) (Param);
+    void (*setfn) (Param, HashTable);
+    void (*unsetfn) (Param, int);
 };
 
 
@@ -1862,8 +1852,9 @@ struct param {
 	GsuHash h;
     } gsu;
 
-    int base;			/* output base or floating point prec    */
-    int width;			/* field width                           */
+    int base;			/* output base or floating point prec or */
+ 				/* for namerefs, locallevel of reference */
+    int width;			/* field width or nameref subscript idx  */
     char *env;			/* location in environment, if exported  */
     char *ename;		/* name of corresponding environment var */
     Param old;			/* old struct for use with local         */
@@ -1942,9 +1933,13 @@ struct tieddata {
 				 */
 #define PM_HASHELEM     (1<<28) /* is a hash-element */
 #define PM_NAMEDDIR     (1<<29) /* has a corresponding nameddirtab entry    */
+#define PM_NAMEREF      (1<<30) /* pointer to a different parameter         */
+
+#define PM_SELFREF	PM_UNIQUE	/* Overload when namerefs resolved  */
+#define PM_NEWREF	PM_SINGLE	/* Overload in for-loop namerefs    */
 
 /* The option string corresponds to the first of the variables above */
-#define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz"
+#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT"
 
 /* These typeset options take an optional numeric argument */
 #define TYPESET_OPTNUM "LRZiEF"
@@ -1967,6 +1962,10 @@ struct tieddata {
 				  * elements
 				  */
 #define SCANPM_CHECKING   (1<<10) /* Check if set, no need to create */
+#define SCANPM_NOEXEC     (1<<11) /* No command substitutions, etc. */
+#define SCANPM_NONAMESPC  (1<<12) /* namespace syntax not allowed */
+#define SCANPM_NONAMEREF  (1<<13) /* named references are not followed */
+
 /* "$foo[@]"-style substitution
  * Only sign bit is significant
  */
@@ -2185,6 +2184,7 @@ typedef groupset *Groupset;
 #define PRINT_LINE	        (1<<6)
 #define PRINT_POSIX_EXPORT	(1<<7)
 #define PRINT_POSIX_READONLY	(1<<8)
+#define PRINT_WITH_NAMESPACE	(1<<9)
 
 /* flags for printing for the whence builtin */
 #define PRINT_WHENCE_CSH	(1<<7)
@@ -2220,8 +2220,6 @@ enum noerrexit_bits {
     NOERREXIT_EXIT = 1,
     /* Suppress ERR_RETURN: per function call */
     NOERREXIT_RETURN = 2,
-    /* NOERREXIT only needed on way down */
-    NOERREXIT_UNTIL_EXEC = 4,
     /* Force exit on SIGINT */
     NOERREXIT_SIGNAL = 8
 };
@@ -2658,22 +2656,25 @@ struct ttyinfo {
 #define TCDELLINE      16
 #define TCNEXTTAB      17
 #define TCBOLDFACEBEG  18
-#define TCSTANDOUTBEG  19
-#define TCUNDERLINEBEG 20
-#define TCALLATTRSOFF  21
-#define TCSTANDOUTEND  22
-#define TCUNDERLINEEND 23
-#define TCHORIZPOS     24
-#define TCUPCURSOR     25
-#define TCDOWNCURSOR   26
-#define TCLEFTCURSOR   27
-#define TCRIGHTCURSOR  28
-#define TCSAVECURSOR   29
-#define TCRESTRCURSOR  30
-#define TCBACKSPACE    31
-#define TCFGCOLOUR     32
-#define TCBGCOLOUR     33
-#define TC_COUNT       34
+#define TCFAINTBEG     19
+#define TCSTANDOUTBEG  20
+#define TCUNDERLINEBEG 21
+#define TCITALICSBEG   22
+#define TCALLATTRSOFF  23
+#define TCSTANDOUTEND  24
+#define TCUNDERLINEEND 25
+#define TCITALICSEND   26
+#define TCHORIZPOS     27
+#define TCUPCURSOR     28
+#define TCDOWNCURSOR   29
+#define TCLEFTCURSOR   30
+#define TCRIGHTCURSOR  31
+#define TCSAVECURSOR   32
+#define TCRESTRCURSOR  33
+#define TCBACKSPACE    34
+#define TCFGCOLOUR     35
+#define TCBGCOLOUR     36
+#define TC_COUNT       37
 
 #define tccan(X) (tclen[X])
 
@@ -2688,38 +2689,27 @@ struct ttyinfo {
 #endif
 
 #define TXTBOLDFACE   0x0001
-#define TXTSTANDOUT   0x0002
-#define TXTUNDERLINE  0x0004
-#define TXTFGCOLOUR   0x0008
-#define TXTBGCOLOUR   0x0010
-
-#define TXT_ATTR_ON_MASK   0x001F
-
-#define txtisset(X)  (txtattrmask & (X))
-#define txtset(X)    (txtattrmask |= (X))
-#define txtunset(X)  (txtattrmask &= ~(X))
-
-#define TXTNOBOLDFACE	0x0020
-#define TXTNOSTANDOUT	0x0040
-#define TXTNOUNDERLINE	0x0080
-#define TXTNOFGCOLOUR	0x0100
-#define TXTNOBGCOLOUR	0x0200
-
-#define TXT_ATTR_OFF_MASK  0x03E0
-/* Bits to shift off right to get on */
-#define TXT_ATTR_OFF_ON_SHIFT 5
-#define TXT_ATTR_OFF_FROM_ON(attr)	\
-    (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT)
-#define TXT_ATTR_ON_FROM_OFF(attr)	\
-    (((attr) & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT)
+#define TXTFAINT      0x0002
+#define TXTSTANDOUT   0x0004
+#define TXTUNDERLINE  0x0008
+#define TXTITALIC     0x0010
+#define TXTFGCOLOUR   0x0020
+#define TXTBGCOLOUR   0x0040
+
+#define TXT_ATTR_ALL  0x007F
+
 /*
  * Indicates to zle_refresh.c that the character entry is an
  * index into the list of multiword symbols.
  */
 #define TXT_MULTIWORD_MASK  0x0400
 
-/* used when, e.g an invalid colour is specified */
-#define TXT_ERROR 0x0800
+/* Used when, e.g an invalid colour is specified. Also used in REFRESH_ELEMENT
+ * to indicate that attributes should remain unchanged. */
+#define TXT_ERROR 0xF00000F000000003
+
+/* Mask for font weight */
+#define TXT_ATTR_FONT_WEIGHT     (TXTBOLDFACE|TXTFAINT)
 
 /* Mask for colour to use in foreground */
 #define TXT_ATTR_FG_COL_MASK     0x000000FFFFFF0000
@@ -2735,26 +2725,19 @@ struct ttyinfo {
 /* Flag to indicate that background is a 24-bit colour */
 #define TXT_ATTR_BG_24BIT        0x8000
 
-/* Things to turn on, including values for the colour elements */
-#define TXT_ATTR_ON_VALUES_MASK	\
-    (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\
-     TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT)
-
 /* Mask out everything to do with setting a foreground colour */
-#define TXT_ATTR_FG_ON_MASK \
+#define TXT_ATTR_FG_MASK \
     (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_24BIT)
 
 /* Mask out everything to do with setting a background colour */
-#define TXT_ATTR_BG_ON_MASK \
+#define TXT_ATTR_BG_MASK \
     (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT)
 
 /* Mask out everything to do with activating colours */
-#define TXT_ATTR_COLOUR_ON_MASK			\
-    (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK)
+#define TXT_ATTR_COLOUR_MASK \
+    (TXT_ATTR_FG_MASK|TXT_ATTR_BG_MASK)
 
-#define txtchangeisset(T,X)	((T) & (X))
 #define txtchangeget(T,A)	(((T) & A ## _MASK) >> A ## _SHIFT)
-#define txtchangeset(T, X, Y)	((void)(T && (*T &= ~(Y), *T |= (X))))
 
 /*
  * For outputting sequences to change colour: specify foreground
@@ -2762,7 +2745,6 @@ struct ttyinfo {
  */
 #define COL_SEQ_FG	(0)
 #define COL_SEQ_BG	(1)
-#define COL_SEQ_COUNT	(2)
 
 struct color_rgb {
     unsigned int red, green, blue;
@@ -2778,11 +2760,7 @@ enum {
     /* Raw output: use stdout rather than shout */
     TSC_RAW = 0x0001,
     /* Output to current prompt buffer: only used when assembling prompt */
-    TSC_PROMPT = 0x0002,
-    /* Mask to get the output mode */
-    TSC_OUTPUT_MASK = 0x0003,
-    /* Change needs reset of other attributes */
-    TSC_DIRTY = 0x0004
+    TSC_PROMPT = 0x0002
 };
 
 /****************************************/
@@ -3006,7 +2984,7 @@ enum errflag_bits {
 /* Sorting */
 /***********/
 
-typedef int (*CompareFn) _((const void *, const void *));
+typedef int (*CompareFn) (const void *, const void *);
 
 enum {
     SORTIT_ANYOLDHOW = 0,	/* Defaults */
@@ -3064,13 +3042,13 @@ struct hist_stack {
     short *chwords;
     int chwordlen;
     int chwordpos;
-    int (*hgetc) _((void));
-    void (*hungetc) _((int));
-    void (*hwaddc) _((int));
-    void (*hwbegin) _((int));
-    void (*hwabort) _((void));
-    void (*hwend) _((void));
-    void (*addtoline) _((int));
+    int (*hgetc) (void);
+    void (*hungetc) (int);
+    void (*hwaddc) (int);
+    void (*hwbegin) (int);
+    void (*hwabort) (void);
+    void (*hwend) (void);
+    void (*addtoline) (int);
     unsigned char *cstack;
     int csp;
     int hist_keep_comment;
@@ -3240,7 +3218,7 @@ enum {
 
 /* compctl entry point pointers */
 
-typedef int (*CompctlReadFn) _((char *, char **, Options, char *));
+typedef int (*CompctlReadFn) (char *, char **, Options, char *);
 
 /* ZLE entry point pointer */
 
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 6f4efce96..5c004d53e 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -82,12 +82,6 @@
  */
 #define _STRPTIME_DONTZERO
 
-#ifdef PROTOTYPES
-# define _(Args) Args
-#else
-# define _(Args) ()
-#endif
-
 #ifndef HAVE_ALLOCA
 # define alloca zhalloc
 #else
@@ -101,7 +95,7 @@
  #   pragma alloca
 #   else
 #    ifndef alloca
-char *alloca _((size_t));
+char *alloca (size_t);
 #    endif
 #   endif
 #  endif
@@ -783,7 +777,8 @@ extern char **environ;
  * We always need setenv and unsetenv in pairs, because
  * we don't know how to do memory management on the values set.
  */
-#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__)
+#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) \
+    && !defined(SETENV_MANGLES_EQUAL)
 # define USE_SET_UNSET_ENV
 #endif
 
diff --git a/Src/ztype.h b/Src/ztype.h
index 5c85b0cd7..4675f73a9 100644
--- a/Src/ztype.h
+++ b/Src/ztype.h
@@ -43,7 +43,8 @@
 #define IWSEP    (1 << 13)
 #define INULL    (1 << 14)
 #define IPATTERN (1 << 15)
-#define zistype(X,Y) (typtab[STOUC(X)] & Y)
+#define INAMESPC (1 << 16)
+#define zistype(X,Y) (typtab[(unsigned char) (X)] & Y)
 #define idigit(X) zistype(X,IDIGIT)
 #define ialnum(X) zistype(X,IALNUM)
 #define iblank(X) zistype(X,IBLANK)	/* blank, not including \n */