about summary refs log tree commit diff
path: root/Src/Zle
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle')
-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
16 files changed, 419 insertions, 484 deletions
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')))