zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: optimise string conversions in history etc.
@ 2008-04-20 21:11 Peter Stephenson
  2008-05-05  1:20 ` Wayne Davison
  0 siblings, 1 reply; 2+ messages in thread
From: Peter Stephenson @ 2008-04-20 21:11 UTC (permalink / raw)
  To: Zsh hackers list

This is kind of long for a patch that should have very little effect,
but I think it's worth doing.  It removes conversions between multibyte
and wide character string types in inner loops of searching through the
history.  There's still plenty of conversion for displaying, setting the
new line, etc., which shouldn't matter so much because this is all
limited by the speed of human reactions anyway, but while the shell is
scanning for matches between a test string and a history line
everything is kept as metafied strings so this bit is fast again.

Various functions have been simplified as a result of this; the big
disadvantage was that I had to add some additional library functions for
metafied strings, with corresponding chances of a screw-up.

Unfortunately this touches quite a lot of things so there could easily
be bugs.  However, I tested the basic history functions and
execute-named-command and the essentials seemed to work.  In fact, I
discovered one bug in execute-named-command: if you have a list of
completions and you type a character, the list isn't cleared but the
first character is removed.  There are other case where the list isn't
cleared but they aren't so crucial.

This will make it relatively, but not completely, straightforward to add
pattern matching to isearch.

(Note the tests for combining chars added here are as before; I will
eventually track them all down and update them altogether as agreed.
Hence two added HERE's.)

Index: Src/utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/utils.c,v
retrieving revision 1.185
diff -u -r1.185 utils.c
--- Src/utils.c	17 Apr 2008 12:52:17 -0000	1.185
+++ Src/utils.c	20 Apr 2008 20:52:39 -0000
@@ -3968,6 +3968,50 @@
 
 
 /*
+ * The guts of mb_metacharlenconv().  This version assumes we are
+ * processing a true multibyte character string without tokens, and
+ * takes the shift state as an argument.
+ */
+
+/**/
+mod_export int
+mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
+{
+    size_t ret = MB_INVALID;
+    char inchar;
+    const char *ptr;
+    wchar_t wc;
+
+    for (ptr = s; *ptr; ) {
+	if (*ptr == Meta) {
+	    inchar = *++ptr ^ 32;
+	    DPUTS(!*ptr,
+		  "BUG: unexpected end of string in mb_metacharlen()\n");
+	} else
+	    inchar = *ptr;
+	ptr++;
+	ret = mbrtowc(&wc, &inchar, 1, mbsp);
+
+	if (ret == MB_INVALID)
+	    break;
+	if (ret == MB_INCOMPLETE)
+	    continue;
+	if (wcp)
+	    *wcp = wc;
+	return ptr - s;
+    }
+
+    if (wcp)
+	*wcp = WEOF;
+    /* No valid multibyte sequence */
+    memset(mbsp, 0, sizeof(*mbsp));
+    if (ptr > s) {
+	return 1 + (*s == Meta);	/* Treat as single byte character */
+    } else
+	return 0;		/* Probably shouldn't happen */
+}
+
+/*
  * Length of metafied string s which contains the next multibyte
  * character; single (possibly metafied) character if string is not null
  * but character is not valid (e.g. possibly incomplete at end of string).
@@ -3982,11 +4026,6 @@
 mod_export int
 mb_metacharlenconv(const char *s, wint_t *wcp)
 {
-    char inchar;
-    const char *ptr;
-    size_t ret;
-    wchar_t wc;
-
     if (!isset(MULTIBYTE)) {
 	/* treat as single byte, possibly metafied */
 	if (wcp)
@@ -4009,37 +4048,7 @@
 	return 1;
     }
 
-    ret = MB_INVALID;
-    for (ptr = s; *ptr; ) {
-	if (*ptr == Meta) {
-	    inchar = *++ptr ^ 32;
-#ifdef DEBUG
-	    if (!*ptr)
-		fprintf(stderr,
-			"BUG: unexpected end of string in mb_metacharlen()\n");
-#endif
-	} else
-	    inchar = *ptr;
-	ptr++;
-	ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate);
-
-	if (ret == MB_INVALID)
-	    break;
-	if (ret == MB_INCOMPLETE)
-	    continue;
-	if (wcp)
-	    *wcp = wc;
-	return ptr - s;
-    }
-
-    if (wcp)
-	*wcp = WEOF;
-    /* No valid multibyte sequence */
-    memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
-    if (ptr > s) {
-	return 1 + (*s == Meta);	/* Treat as single byte character */
-    } else
-	return 0;		/* Probably shouldn't happen */
+    return mb_metacharlenconv_r(s, wcp, &mb_shiftstate);
 }
 
 /*
Index: Src/zsh.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v
retrieving revision 1.122
diff -u -r1.122 zsh.h
--- Src/zsh.h	17 Apr 2008 10:23:53 -0000	1.122
+++ Src/zsh.h	20 Apr 2008 20:52:41 -0000
@@ -1606,12 +1606,11 @@
 
     Histent up;			/* previous line (moving upward)    */
     Histent down;		/* next line (moving downward)      */
-#ifdef MULTIBYTE_SUPPORT	/* (Note: must match ZLE_STRING_T!) */
-    wchar_t *zle_text;		/* the edited history line          */
-#else
-    char *zle_text;		/* the edited history line          */
-#endif
-    int zle_len;		/* length of zle_text */
+    char *zle_text;		/* the edited history line,
+				 * a metafied, NULL-terminated string,
+				 * i.e the same format as the original
+				 * entry
+				 */
     time_t stim;		/* command started time (datestamp) */
     time_t ftim;		/* command finished time            */
     short *words;		/* Position of words in history     */
Index: Src/Zle/complist.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/complist.c,v
retrieving revision 1.111
diff -u -r1.111 complist.c
--- Src/Zle/complist.c	20 Apr 2008 06:41:23 -0000	1.111
+++ Src/Zle/complist.c	20 Apr 2008 20:52:42 -0000
@@ -2432,7 +2432,7 @@
         }
         first = 0;
         if (mode == MM_INTER)
-	    statusline = stringaszleline(status, 0, &statusll, NULL, NULL);
+	    statusline = status;
         else if (mode) {
             int l = sprintf(status, "%s%sisearch%s: ",
                             ((msearchstate & MS_FAILED) ? "failed " : ""),
@@ -2441,17 +2441,12 @@
 
             strncat(status, msearchstr, MAX_STATUS - l - 1);
 
-            statusline = stringaszleline(status, 0, &statusll, NULL, NULL);
+            statusline = status;
         } else {
             statusline = NULL;
-            statusll = 0;
         }
         zrefresh();
-	if (statusline) {
-	    free(statusline);
-	    statusline = NULL;
-	    statusll = 0;
-	}
+	statusline = NULL;
         inselect = 1;
         if (noselect) {
             broken = 1;
@@ -2622,12 +2617,10 @@
 	    if (nmatches < 1 || !minfo.cur || !*(minfo.cur)) {
 		nolist = 1;
                 if (mode == MM_INTER) {
-                    statusline = stringaszleline(status, 0,
-						 &statusll, NULL, NULL);
+                    statusline = status;
                 } else {
 		    /* paranoia */
 		    statusline = NULL;
-		    statusll = 0;
 		}
 		if (nmessages) {
 		    showinglist = -2;
@@ -2645,11 +2638,7 @@
 		    zrefresh();
 		    showinglist = clearlist = 0;
 		}
-		if (statusline) {
-		    free(statusline);
-		    statusline = NULL;
-		    statusll = 0;
-		}
+		statusline = NULL;
 
 		goto getk;
 	    }
@@ -2763,19 +2752,13 @@
 
             if (nolist) {
                 if (mode == MM_INTER) {
-                    statusline = stringaszleline(status, 0,
-						 &statusll, NULL, NULL);
+                    statusline = status;
                 } else {
 		    /* paranoia */
 		    statusline = NULL;
-		    statusll = 0;
 		}
                 zrefresh();
-		if (statusline) {
-		    free(statusline);
-		    statusline = NULL;
-		    statusll = 0;
-		}
+		statusline = NULL;
                 goto getk;
             }
             if (mode)
Index: Src/Zle/zle.h
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle.h,v
retrieving revision 1.38
diff -u -r1.38 zle.h
--- Src/Zle/zle.h	15 Apr 2008 16:49:55 -0000	1.38
+++ Src/Zle/zle.h	20 Apr 2008 20:52:42 -0000
@@ -74,10 +74,22 @@
 #define LASTFULLCHAR	lastchar_wide
 #define LASTFULLCHAR_T  ZLE_INT_T
 
-/* We may need to handle combining character alignment */
+/*
+ * We may need to handle combining character alignment.
+ * The following fix up the position of the cursor so that it
+ * never ends up over a zero-width punctuation character following
+ * an alphanumeric character.  The first is used if we were
+ * moving the cursor left, the second if we were moving right or
+ * if something under the cursor may have changed.
+ */
 #define CCLEFT()	alignmultiwordleft(&zlecs, 1)
 #define CCRIGHT()	alignmultiwordright(&zlecs, 1)
 /*
+ * Same for any other position
+ */
+#define CCLEFTPOS(pos)	alignmultiwordleft(&pos, 1)
+#define CCRIGHTPOS(pos)	alignmultiwordright(&pos, 1)
+/*
  * Increment or decrement the cursor position, skipping over
  * combining characters.
  */
@@ -151,6 +163,8 @@
 /* Combining character alignment: none in this mode */
 #define CCLEFT()
 #define CCRIGHT()
+#define CCLEFTPOS()
+#define CCRIGHTPOS()
 /*
  * Increment or decrement the cursor position: simple in this case.
  */
@@ -235,7 +249,13 @@
 
 #define CUT_FRONT   (1<<0)   /* Text goes in front of cut buffer */
 #define CUT_REPLACE (1<<1)   /* Text replaces cut buffer */
-#define CUT_RAW     (1<<2)   /* Raw character counts (not used in cut itself) */
+#define CUT_RAW     (1<<2)   /*
+			      * Raw character counts (not used in cut itself).
+			      * This is used when the values are offsets
+			      * into the zleline array rather than numbers
+			      * of visible characters directly input by
+			      * the user.
+			      */
 
 /* undo system */
 
Index: Src/Zle/zle_hist.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_hist.c,v
retrieving revision 1.40
diff -u -r1.40 zle_hist.c
--- Src/Zle/zle_hist.c	14 Apr 2008 14:57:53 -0000	1.40
+++ Src/Zle/zle_hist.c	20 Apr 2008 20:52:42 -0000
@@ -54,8 +54,9 @@
 
 
 struct zle_text {
-    ZLE_STRING_T text;
-    int len;
+    /* Metafied, NULL-terminated string */
+    char *text;
+    /* 1 if we have allocated space for text */
     int alloced;
 };
 
@@ -67,30 +68,18 @@
  * Each use of this must have a matching zletextfree() in order
  * to free up the allocated line, if any.  (N.B.: each use *of
  * the function*, not just each use of a struct zle_text.)
- *
- * TODO: This is quite inefficient.  We could convert zlinecmp and
- * zlinefind to take a metafied string as input and acquire a (wide)
- * character from it whenever needed, which would also require storing
- * zle_text as a metafied string in remember_edits().  However, the
- * following is good enough for now (although searching a really huge
- * history might not be so much fun).
  */
 
 static void
 zletext(Histent ent, struct zle_text *zt)
 {
-    char *duptext;
-
     if (ent->zle_text) {
 	zt->text = ent->zle_text;
-	zt->len = ent->zle_len;
 	zt->alloced = 0;
 	return;
     }
 
-    duptext = ztrdup(ent->node.nam);
-    zt->text = stringaszleline(duptext, 0, &zt->len, NULL, NULL);
-    zsfree(duptext);
+    zt->text = ztrdup(ent->node.nam);
     zt->alloced = 1;
 }
 
@@ -111,14 +100,15 @@
 {
     Histent ent = quietgethist(histline);
     if (ent) {
-	if (!ent->zle_text || ent->zle_len != zlell ||
-	    ZS_memcmp(ent->zle_text, zleline, zlell) != 0) {
+	char *line = 
+	    zlemetaline ? zlemetaline :
+	    zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
+	if (!ent->zle_text || strcmp(line, ent->zle_text) != 0) {
 	    if (ent->zle_text)
 		free(ent->zle_text);
-	    ent->zle_text = zalloc(zlell * ZLE_CHAR_SIZE);
-	    ent->zle_len = zlell;
-	    ZS_memcpy(ent->zle_text, zleline, zlell);
-	}
+	    ent->zle_text = zlemetaline ? ztrdup(line) : line;
+	} else if (!zlemetaline)
+	    free(line);
     }
 }
 
@@ -132,7 +122,6 @@
 	if (he->zle_text) {
 	    free(he->zle_text);
 	    he->zle_text = NULL;
-	    he->zle_len = 0;
 	}
     }
 }
@@ -150,65 +139,99 @@
  */
 
 static int
-zlinecmp(ZLE_STRING_T histp, int histl, ZLE_STRING_T inputp, int inputl)
+zlinecmp(const char *histp, const char *inputp)
 {
-    int cnt;
+    const char *hptr = histp, *iptr = inputp;
+#ifdef MULTIBYTE_SUPPORT
+    mbstate_t hstate, istate;
+#endif
 
-    if (histl < inputl) {
-	/* Not identical, second string is not a prefix. */
-	return 3;
+    while (*hptr == *iptr) {
+	hptr++;
+	iptr++;
     }
 
-    if (!ZS_memcmp(histp, inputp, inputl)) {
-	/* Common prefix is identical */
-	/* If lines are identical return 0 */
-	if (histl == inputl)
+    if (!*iptr) {
+	if (!*hptr) {
+	    /* strings are the same */
 	    return 0;
-	/* Second string is a prefix of the first */
-	return -1;
+	} else {
+	    /* inputp is a prefix */
+	    return -1;
+	}
     }
 
-    for (cnt = inputl; cnt; cnt--) {
-	if ((ZLE_INT_T)*inputp++ != ZC_tolower(*histp++))
+#ifdef MULTIBYTE_SUPPORT
+    memset(&hstate, 0, sizeof(hstate));
+    memset(&istate, 0, sizeof(istate));
+#endif
+
+    /* look for lower case versions */
+    while (*histp && *inputp) {
+#ifdef MULTIBYTE_SUPPORT
+	wint_t hwc, iwc;
+	int hlen, ilen;
+
+	hlen = mb_metacharlenconv_r(histp, &hwc, &hstate);
+	ilen = mb_metacharlenconv_r(inputp, &iwc, &istate);
+
+	if (hwc == WEOF || iwc == WEOF) {
+	    /* can't convert, compare input characters */
+	    if (ilen != hlen || memcmp(histp, inputp, hlen) != 0)
+		return 3;
+	} else if (towlower(hwc) != iwc)
 	    return 3;
+
+	histp += hlen;
+	inputp += ilen;
+#else
+    	if (tulower(*histp++) != *inputp++)
+	    return 3;
+#endif
     }
-    /* Is second string is lowercase version of first? */
-    if (histl == inputl)
-	return 1;
-    /* Second string is lowercase prefix of first */
-    return 2;
+    if (!*inputp) {
+	/* one string finished, if it's the input... */
+	if (!*histp)
+	    return 1;		/* ...same, else */
+	else
+	    return 2;		/* ...prefix */
+    }
+    /* Different */
+    return 3;
 }
 
 
 /*
- * Search for needle in haystack.  Haystack and needle are ZLE strings
- * of the indicated length.  Start the search at position
- * pos in haystack.  Search forward if dir > 0, otherwise search
- * backward.  sens is used to test against the return value of linecmp.
+ * Search for needle in haystack.  Haystack and needle are metafied strings.
+ * Start the search at position pos in haystack.
+ * Search forward if dir > 0, otherwise search backward.
+ * sens is used to test against the return value of linecmp.
+ *
+ * Return the pointer to the location in haystack found, else NULL.
+ *
+ * We assume we'll only find needle at some sensible position in a multibyte
+ * string, so we don't bother calculating multibyte character lengths for
+ * incrementing and decrementing the search position.
  */
 
-static ZLE_STRING_T
-zlinefind(ZLE_STRING_T haystack, int haylen, int pos,
-	  ZLE_STRING_T needle, int needlen, int dir, int sens)
+static char *
+zlinefind(char *haystack, int pos, char *needle, int dir, int sens)
 {
-    ZLE_STRING_T s = haystack + pos;
-    int slen = haylen - pos;
+    char *s = haystack + pos;
 
     if (dir > 0) {
-	while (slen) {
-	    if (zlinecmp(s, slen, needle, needlen) < sens)
+	while (*s) {
+	    if (zlinecmp(s, needle) < sens)
 		return s;
 	    s++;
-	    slen--;
 	}
     } else {
 	for (;;) {
-	    if (zlinecmp(s, slen, needle, needlen) < sens)
+	    if (zlinecmp(s, needle) < sens)
 		return s;
 	    if (s == haystack)
 		break;
 	    s--;
-	    slen++;
 	}
     }
 
@@ -257,7 +280,7 @@
 	if ((zlecs += lastcol) >= x) {
 	    zlecs = x;
 	    if (zlecs > findbol() && invicmdmode())
-		zlecs--;
+		DECCS();
 	}
     }
     return n;
@@ -341,7 +364,7 @@
 	if ((zlecs += lastcol) >= x) {
 	    zlecs = x;
 	    if (zlecs > findbol() && invicmdmode())
-		zlecs--;
+		DECCS();
 	}
     }
     return n;
@@ -421,16 +444,26 @@
     return 0;
 }
 
-static int histpos, srch_hl, srch_cs = -1;
-static ZLE_STRING_T srch_str;
+/*
+ * Values remembered for history searches to enable repetition.
+ * srch_hl remembers the old value of histline, to see if it's changed
+ *   since the last search.
+ * srch_cs remembers the old value of zlecs for the same purpose (it is
+ *   not use for any other purpose, i.e. does not need to be a valid
+ *   index into anything).
+ * srch_str is the metafied search string, as extracted from the start
+ *   of zleline.
+ */
+static int srch_hl, srch_cs = -1;
+static char *srch_str;
 
 /**/
 int
 historysearchbackward(char **args)
 {
     Histent he;
-    int n = zmult, hp;
-    ZLE_STRING_T str;
+    int n = zmult, histpos;
+    char *str;
     struct zle_text zt;
 
     if (zmult < 0) {
@@ -440,46 +473,45 @@
 	zmult = n;
 	return ret;
     }
-    if (*args)
-	str = stringaszleline(*args, 0, &hp, NULL, NULL);
-    else {
+    if (*args) {
+	str = *args;
+    } else {
+	char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
 	if (histline == curhist || histline != srch_hl || zlecs != srch_cs ||
-	    mark != 0 || ZS_memcmp(srch_str, zleline, histpos) != 0) {
-	    zfree(srch_str, histpos);
-	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]); histpos++) ;
+	    mark != 0 || strncmp(srch_str, line, histpos) != 0) {
+	    free(srch_str);
+	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]);
+		 histpos++)
+		;
 	    if (histpos < zlell)
 		histpos++;
-	    srch_str = zalloc(histpos * ZLE_CHAR_SIZE);
-	    ZS_memcpy(srch_str, zleline, histpos);
+	    /* ensure we're not on a combining character */
+	    CCRIGHTPOS(histpos);
+	    /* histpos from now on on is an index into the metafied string */
+	    srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0);
 	}
+	free(line);
 	str = srch_str;
-	hp = histpos;
     }
-    if (!(he = quietgethist(histline))) {
-	if (*args)
-	    free(str);
+    if (!(he = quietgethist(histline)))
 	return 1;
-    }
+
     while ((he = movehistent(he, -1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 	    continue;
 	zletext(he, &zt);
-	if (zlinecmp(zt.text, zt.len, str, hp) < 0 &&
-	    (*args || zlell != zt.len || ZS_memcmp(zt.text, str, zlell))) {
+	if (zlinecmp(zt.text, str) < 0 &&
+	    (*args || strcmp(zt.text, str) != 0)) {
 	    if (--n <= 0) {
 		zle_setline(he);
 		srch_hl = histline;
 		srch_cs = zlecs;
-		if (*args)
-		    free(str);
 		zletextfree(&zt);
 		return 0;
 	    }
 	}
 	zletextfree(&zt);
     }
-    if (*args)
-	free(str);
     return 1;
 }
 
@@ -488,8 +520,8 @@
 historysearchforward(char **args)
 {
     Histent he;
-    int n = zmult, hp;
-    ZLE_STRING_T str;
+    int n = zmult, histpos;
+    char *str;
     struct zle_text zt;
 
     if (zmult < 0) {
@@ -499,46 +531,43 @@
 	zmult = n;
 	return ret;
     }
-    if (*args)
-	str = stringaszleline(*args, 0, &hp, NULL, NULL);
-    else {
+    if (*args) {
+	str = *args;
+    } else {
+	char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0);
 	if (histline == curhist || histline != srch_hl || zlecs != srch_cs ||
-	    mark != 0 || ZS_memcmp(srch_str, zleline, histpos) != 0) {
-	    zfree(srch_str, histpos * ZLE_CHAR_SIZE);
-	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]); histpos++) ;
+	    mark != 0 || strncmp(srch_str, line, histpos) != 0) {
+	    free(srch_str);
+	    for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]);
+		 histpos++)
+		;
 	    if (histpos < zlell)
 		histpos++;
-	    srch_str = zalloc(histpos * ZLE_CHAR_SIZE);
-	    ZS_memcpy(srch_str, zleline, histpos);
+	    CCRIGHT();
+	    srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0);
 	}
+	free(line);
 	str = srch_str;
-	hp = histpos;
     }
-    if (!(he = quietgethist(histline))) {
-	if (*args)
-	    free(str);
+    if (!(he = quietgethist(histline)))
 	return 1;
-    }
+
     while ((he = movehistent(he, 1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 	    continue;
 	zletext(he, &zt);
-	if (zlinecmp(zt.text, zt.len, str, hp) < (he->histnum == curhist) &&
-	    (*args || zlell != zt.len || ZS_memcmp(zt.text, str, zlell))) {
+	if (zlinecmp(zt.text, str) < (he->histnum == curhist) &&
+	    (*args || strcmp(zt.text, str) != 0)) {
 	    if (--n <= 0) {
 		zle_setline(he);
 		srch_hl = histline;
 		srch_cs = zlecs;
-		if (*args)
-		    free(str);
 		zletextfree(&zt);
 		return 0;
 	    }
 	}
 	zletextfree(&zt);
     }
-    if (*args)
-	free(str);
     return 1;
 }
 
@@ -741,29 +770,21 @@
 void
 zle_setline(Histent he)
 {
+    int remetafy;
+    if (zlemetaline) {
+	unmetafy_line();
+	remetafy = 1;
+    } else
+	remetafy = 0;
     remember_edits();
     mkundoent();
     histline = he->histnum;
 
-    if (he->zle_text) {
-	/*
-	 * Optimise out conversion to metafied string and back.
-	 * Remember convention of extra 2 characters spare.
-	 */
-	free(zleline);
-	linesz = zlell = he->zle_len;
-	zleline = zalloc((zlell + 2) * ZLE_CHAR_SIZE);
-	ZS_memcpy(zleline, he->zle_text, zlell);
-
-	if ((zlecs = zlell) && invicmdmode())
-	    DECCS();
-    } else {
-	setline(he->node.nam, ZSL_COPY|ZSL_TOEND);
-    }
-    /* Move right if we're on a zero-width combining character */
-    CCRIGHT();
+    setline(he->zle_text ? he->zle_text : he->node.nam, ZSL_COPY|ZSL_TOEND);
     setlastline();
     clearlist = 1;
+    if (remetafy)
+	metafy_line();
 }
 
 /**/
@@ -783,6 +804,8 @@
 zle_goto_hist(int ev, int n, int skipdups)
 {
     Histent he = quietgethist(ev);
+    char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 1);
+
     if (!he || !(he = movehistent(he, n, hist_skip_flags)))
 	return 1;
     if (skipdups && n) {
@@ -793,7 +816,7 @@
 	    int ret;
 
 	    zletext(he, &zt);
-	    ret = zlinecmp(zt.text, zt.len, zleline, zlell);
+	    ret = zlinecmp(zt.text, line);
 	    zletextfree(&zt);
 	    if (ret)
 		break;
@@ -961,7 +984,7 @@
     *nomatch = (isrch_spots[num].flags & ISS_FAILING);
 }
 
-#define ISEARCH_PROMPT		ZWS("failing XXX-i-search: ")
+#define ISEARCH_PROMPT		"failing XXX-i-search: "
 #define NORM_PROMPT_POS		8
 #define FIRST_SEARCH_CHAR	(NORM_PROMPT_POS + 14)
 
@@ -969,17 +992,18 @@
 static void
 doisearch(char **args, int dir)
 {
-    ZLE_STRING_T ibuf = zhalloc(80 * ZLE_CHAR_SIZE);
-    ZLE_STRING_T sbuf = ibuf + FIRST_SEARCH_CHAR;
-    ZLE_STRING_T last_line = NULL;
+    char *ibuf = zhalloc(80);
+    char *sbuf = ibuf + FIRST_SEARCH_CHAR;
+    char *last_line = NULL;
     struct zle_text zt;
     int sbptr = 0, top_spot = 0, pos, sibuf = 80;
     int nomatch = 0, skip_line = 0, skip_pos = 0;
     int odir = dir, sens = zmult == 1 ? 3 : 1;
-    int hl = histline, savekeys = -1, feep = 0, last_len;
+    int hl = histline, savekeys = -1, feep = 0;
     Thingy cmd;
     char *okeymap;
     Histent he;
+    ZleIntFunc exitfn = (ZleIntFunc)0;
 
     if (!(he = quietgethist(hl)))
 	return;
@@ -994,18 +1018,21 @@
 	ungetbytes(arg, len);
     }
 
-    ZS_strcpy(ibuf, ISEARCH_PROMPT);
-    ZS_memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? ZWS("fwd") : ZWS("bck"), 3);
-    remember_edits();
+    strcpy(ibuf, ISEARCH_PROMPT);
+    /* careful with fwd/bck: we don't want the NULL copied */
+    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     okeymap = ztrdup(curkeymapname);
-    zletext(he, &zt);
     selectkeymap("main", 1);
-    pos = zlecs;
+
+    metafy_line();
+    remember_edits();
+    zletext(he, &zt);
+    pos = zlemetacs;
     for (;;) {
 	/* Remember the current values in case search fails (doesn't push). */
-	set_isrch_spot(top_spot, hl, pos, zlecs, sbptr, dir, nomatch);
-	if (sbptr == 1 && sbuf[0] == ZWC('^')) {
-	    zlecs = 0;
+	set_isrch_spot(top_spot, hl, pos, zlemetacs, sbptr, dir, nomatch);
+	if (sbptr == 1 && sbuf[0] == '^') {
+	    zlemetacs = 0;
     	    nomatch = 0;
 	    statusline = ibuf + NORM_PROMPT_POS;
 	} else if (sbptr > 0) {
@@ -1016,21 +1043,28 @@
 	     */
 	    if (last_line)
 		free(last_line);
-	    last_line = zalloc(zt.len * ZLE_CHAR_SIZE);
-	    ZS_memcpy(last_line, zt.text, zt.len);
-	    last_len = zt.len;
+	    last_line = ztrdup(zt.text);
 
 	    for (;;) {
-		ZLE_STRING_T t;
+		char *t;
 
+		/*
+		 * If instructed, move past a match position:
+		 * backwards if searching backwards (skipping
+		 * the line if we're at the start), forwards
+		 * if searching forwards (skipping a line if we're
+		 * at the end).
+		 */
 		if (skip_pos) {
 		    if (dir < 0) {
 			if (pos == 0)
 			    skip_line = 1;
 			else
-			    pos -= 1;
-		    } else if (sbuf[0] != ZWC('^')) {
-			if (pos >= zt.len - 1)
+			    pos = backwardmetafiedchar(zlemetaline,
+						       zlemetaline + pos,
+						       NULL) - zlemetaline;
+		    } else if (sbuf[0] != '^') {
+			if (pos >= strlen(zt.text) - 1)
 			    skip_line = 1;
 			else
 			    pos += 1;
@@ -1038,25 +1072,33 @@
 			skip_line = 1;
 		    skip_pos = 0;
 		}
-		if (!skip_line && ((sbuf[0] == ZWC('^')) ?
-		    (t = zlinecmp(zt.text, zt.len, sbuf + 1, sbptr - 1) < sens
-		     ? zt.text : NULL) :
-		    (t = zlinefind(zt.text, zt.len, pos, sbuf,
-				   sbptr, dir, sens)))) {
+		/*
+		 * First search for a(nother) match within the
+		 * current line, unless we've been told to skip it.
+		 */
+		sbuf[sbptr] = '\0';
+		if (!skip_line && ((sbuf[0] == '^') ?
+				   (t = (zlinecmp(zt.text, sbuf + 1) < sens
+					 ? zt.text : NULL)) :
+		    (t = zlinefind(zt.text, pos, sbuf, dir, sens)))) {
 		    zle_setline(he);
 		    pos = t - zt.text;
-		    zlecs = pos +
-			(dir == 1 ? sbptr - (sbuf[0] == ZWC('^')) : 0);
+		    zlemetacs = pos +
+			(dir == 1 ? sbptr - (sbuf[0] == '^') : 0);
 	    	    nomatch = 0;
 		    statusline = ibuf + NORM_PROMPT_POS;
 		    break;
 		}
+		/*
+		 * If not found within that line, move through
+		 * the history to try again.
+		 */
 		if (!(zlereadflags & ZLRF_HISTORY)
 		 || !(he = movehistent(he, dir, hist_skip_flags))) {
 		    if (sbptr == (int)isrch_spots[top_spot-1].len
 		     && (isrch_spots[top_spot-1].flags & ISS_FAILING))
 			top_spot--;
-		    get_isrch_spot(top_spot, &hl, &pos, &zlecs, &sbptr,
+		    get_isrch_spot(top_spot, &hl, &pos, &zlemetacs, &sbptr,
 				   &dir, &nomatch);
 		    if (!nomatch) {
 			feep = 1;
@@ -1072,18 +1114,18 @@
 		hl = he->histnum;
 		zletextfree(&zt);
 		zletext(he, &zt);
-		pos = (dir == 1) ? 0 : zt.len;
-		skip_line = isset(HISTFINDNODUPS) ? !!(he->node.flags & HIST_DUP)
-		    : (zt.len == last_len &&
-		       !ZS_memcmp(zt.text, last_line, zt.len));
+		pos = (dir == 1) ? 0 : strlen(zt.text);
+		skip_line = isset(HISTFINDNODUPS)
+		    ? !!(he->node.flags & HIST_DUP)
+		    : !strcmp(zt.text, last_line);
 	    }
 	} else {
 	    top_spot = 0;
     	    nomatch = 0;
 	    statusline = ibuf + NORM_PROMPT_POS;
 	}
-	sbuf[sbptr] = ZWC('_');
-	statusll = sbuf - statusline + sbptr + 1;
+	sbuf[sbptr] = '_';
+	sbuf[sbptr+1] = '\0';
     ref:
 	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
@@ -1093,7 +1135,7 @@
 	    zle_setline(he);
 	    zletextfree(&zt);
 	    zletext(he, &zt);
-	    zlecs = i;
+	    zlemetacs = i;
 	    break;
 	}
 	if(cmd == Th(z_clearscreen)) {
@@ -1109,7 +1151,7 @@
 	} else if(cmd == Th(z_vibackwarddeletechar) ||
 	    	cmd == Th(z_backwarddeletechar)) {
 	    if (top_spot)
-		get_isrch_spot(--top_spot, &hl, &pos, &zlecs, &sbptr,
+		get_isrch_spot(--top_spot, &hl, &pos, &zlemetacs, &sbptr,
 			       &dir, &nomatch);
 	    else
 		feep = 1;
@@ -1120,67 +1162,66 @@
 	    he = quietgethist(hl);
 	    zletextfree(&zt);
 	    zletext(he, &zt);
-	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == ZWC('^'))) {
-		int i = zlecs;
+	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
+		int i = zlemetacs;
 		zle_setline(he);
-		zlecs = i;
+		zlemetacs = i;
 	    }
-	    ZS_memcpy(ibuf + NORM_PROMPT_POS,
-		      (dir == 1) ? ZWS("fwd") : ZWS("bck"), 3);
+	    memcpy(ibuf + NORM_PROMPT_POS,
+		   (dir == 1) ? "fwd" : "bck", 3);
 	    continue;
 	} else if(cmd == Th(z_acceptandhold)) {
-	    acceptandhold(zlenoargs);
+	    exitfn = acceptandhold;
 	    break;
 	} else if(cmd == Th(z_acceptandinfernexthistory)) {
-	    acceptandinfernexthistory(zlenoargs);
+	    exitfn = acceptandinfernexthistory;
 	    break;
 	} else if(cmd == Th(z_acceptlineanddownhistory)) {
-	    acceptlineanddownhistory(zlenoargs);
+	    exitfn = acceptlineanddownhistory;
 	    break;
 	} else if(cmd == Th(z_acceptline)) {
-	    acceptline(zlenoargs);
+	    exitfn = acceptline;
 	    break;
 	} else if(cmd == Th(z_historyincrementalsearchbackward)) {
-	    set_isrch_spot(top_spot++, hl, pos, zlecs, sbptr, dir, nomatch);
+	    set_isrch_spot(top_spot++, hl, pos, zlemetacs, sbptr, dir, nomatch);
 	    if (dir != -1)
 		dir = -1;
 	    else
 		skip_pos = 1;
 	    goto rpt;
 	} else if(cmd == Th(z_historyincrementalsearchforward)) {
-	    set_isrch_spot(top_spot++, hl, pos, zlecs, sbptr, dir, nomatch);
+	    set_isrch_spot(top_spot++, hl, pos, zlemetacs, sbptr, dir, nomatch);
 	    if (dir != 1)
 		dir = 1;
 	    else
 		skip_pos = 1;
 	    goto rpt;
 	} else if(cmd == Th(z_virevrepeatsearch)) {
-	    set_isrch_spot(top_spot++, hl, pos, zlecs, sbptr, dir, nomatch);
+	    set_isrch_spot(top_spot++, hl, pos, zlemetacs, sbptr, dir, nomatch);
 	    dir = -odir;
 	    skip_pos = 1;
 	    goto rpt;
 	} else if(cmd == Th(z_virepeatsearch)) {
-	    set_isrch_spot(top_spot++, hl, pos, zlecs, sbptr, dir, nomatch);
+	    set_isrch_spot(top_spot++, hl, pos, zlemetacs, sbptr, dir, nomatch);
 	    dir = odir;
 	    skip_pos = 1;
 	rpt:
 	    if (!sbptr && previous_search_len) {
 		if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) {
-		    ibuf = hrealloc((char *)ibuf, sibuf * ZLE_CHAR_SIZE,
-				    (sibuf + previous_search_len)
-				    * ZLE_CHAR_SIZE);
+		    ibuf = hrealloc((char *)ibuf, sibuf,
+				    (sibuf + previous_search_len));
 		    sbuf = ibuf + FIRST_SEARCH_CHAR;
 		    sibuf += previous_search_len;
 		}
-		ZS_memcpy(sbuf, previous_search, sbptr = previous_search_len);
+		memcpy(sbuf, previous_search, sbptr = previous_search_len);
 	    }
-	    ZS_memcpy(ibuf + NORM_PROMPT_POS,
-		      (dir == 1) ? ZWS("fwd") : ZWS("bck"), 3);
+	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
 	    continue;
 	} else if(cmd == Th(z_viquotedinsert) ||
 	    	cmd == Th(z_quotedinsert)) {
 	    if(cmd == Th(z_viquotedinsert)) {
-		sbuf[sbptr] = ZWC('^');
+		sbuf[sbptr] = '^';
+		sbuf[sbptr+1] = '\0';
 		zrefresh();
 	    }
 	    if (getfullchar(0) == ZLEEOF)
@@ -1213,10 +1254,13 @@
 		feep = 1;
 		continue;
 	    }
-	    set_isrch_spot(top_spot++, hl, pos, zlecs, sbptr, dir, nomatch);
-	    if (sbptr >= sibuf - FIRST_SEARCH_CHAR - 2) {
-		ibuf = hrealloc((char *)ibuf, sibuf * ZLE_CHAR_SIZE,
-				sibuf * 2 * ZLE_CHAR_SIZE);
+	    set_isrch_spot(top_spot++, hl, pos, zlemetacs, sbptr, dir, nomatch);
+	    if (sbptr >= sibuf - FIRST_SEARCH_CHAR - 2 
+#ifdef MULTIBYTE_SUPPORT
+		- 2 * MB_CUR_MAX
+#endif
+		) {
+		ibuf = hrealloc(ibuf, sibuf, sibuf * 2);
 		sbuf = ibuf + FIRST_SEARCH_CHAR;
 		sibuf *= 2;
 	    }
@@ -1224,7 +1268,7 @@
 	     * We've supposedly arranged above that lastchar_wide is
 	     * always valid at this point.
 	     */
-	    sbuf[sbptr++] = LASTFULLCHAR;
+	    sbptr += zlecharasstring(LASTFULLCHAR, sbuf + sbptr);
 	}
 	if (feep)
 	    handlefeep(zlenoargs);
@@ -1232,10 +1276,13 @@
     }
     if (sbptr) {
 	zfree(previous_search, previous_search_len);
-	previous_search = zalloc(sbptr * ZLE_CHAR_SIZE);
-	ZS_memcpy(previous_search, sbuf, previous_search_len = sbptr);
+	previous_search = zalloc(sbptr);
+	memcpy(previous_search, sbuf, previous_search_len = sbptr);
     }
     statusline = NULL;
+    unmetafy_line();
+    if (exitfn)
+	exitfn(zlenoargs);
     selectkeymap(okeymap, 1);
     zsfree(okeymap);
     /*
@@ -1252,17 +1299,20 @@
 static Histent
 infernexthist(Histent he, UNUSED(char **args))
 {
+    metafy_line();
     for (he = movehistent(he, -2, HIST_FOREIGN);
 	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
 	struct zle_text zt;
 	zletext(he, &zt);
 
-	if (!zlinecmp(zt.text, zt.len, zleline, zlell)) {
+	if (!zlinecmp(zt.text, zlemetaline)) {
+	    unmetafy_line();
 	    zletextfree(&zt);
 	    return movehistent(he, 1, HIST_FOREIGN);
 	}
 	zletextfree(&zt);
     }
+    unmetafy_line();
     return NULL;
 }
 
@@ -1321,7 +1371,7 @@
 static int
 getvisrchstr(void)
 {
-    ZLE_STRING_T sbuf = zhalloc(80 * ZLE_CHAR_SIZE);
+    char *sbuf = zhalloc(80);
     int sptr = 1, ret = 0, ssbuf = 80, feep = 0;
     Thingy cmd;
     char *okeymap = ztrdup(curkeymapname);
@@ -1337,11 +1387,11 @@
     }
     clearlist = 1;
     statusline = sbuf;
-    sbuf[0] = (visrchsense == -1) ? ZWC('?') : ZWC('/');
+    sbuf[0] = (visrchsense == -1) ? '?' : '/';
     selectkeymap("main", 1);
     while (sptr) {
-	sbuf[sptr] = ZWC('_');
-	statusll = sptr + 1;
+	sbuf[sptr] = '_';
+	sbuf[sptr] = '\0';
 	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    ret = 0;
@@ -1357,32 +1407,52 @@
 	    clearscreen(zlenoargs);
 	} else if(cmd == Th(z_acceptline) ||
 	    	cmd == Th(z_vicmdmode)) {
-	    int newlen;
-	    sbuf[sptr] = ZWC('\0');
-	    visrchstr = zlelineasstring(sbuf+1, sptr-1, 0, &newlen, NULL, 0);
-	    if (!newlen) {
-	        zsfree(visrchstr);
+	    if (sptr) {
+		sbuf[sptr] = ZWC('\0');
+		visrchstr = ztrdup(sbuf+1);
+	    } else {
 		visrchstr = ztrdup(vipenultsrchstr);
 	    }
 	    ret = 1;
 	    sptr = 0;
 	} else if(cmd == Th(z_backwarddeletechar) ||
-	    	cmd == Th(z_vibackwarddeletechar)) {
-	    sptr--;
+		  cmd == Th(z_vibackwarddeletechar)) {
+	    sptr = backwardmetafiedchar(sbuf+1, sbuf+sptr, NULL) - sbuf;
 	} else if(cmd == Th(z_backwardkillword) ||
-	    	cmd == Th(z_vibackwardkillword)) {
-	    while(sptr != 1 && ZC_iblank(sbuf[sptr - 1]))
-		sptr--;
-	    if(ZC_iident(sbuf[sptr - 1]))
-		while(sptr != 1 && ZC_iident(sbuf[sptr - 1]))
-		    sptr--;
-	    else
-		while(sptr != 1 && !ZC_iident(sbuf[sptr - 1]) &&
-		      !ZC_iblank(sbuf[sptr - 1]))
-		    sptr--;
+		  cmd == Th(z_vibackwardkillword)) {
+	    convchar_t cc;
+	    char *newpos;
+	    while (sptr != 1) {
+		newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
+		if (!ZC_iblank(cc))
+		    break;
+		sptr = newpos - sbuf;
+	    }
+	    if (sptr > 1) {
+		newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
+		if (ZC_iident(cc)) {
+		    for (;;) {
+			sptr = newpos - sbuf;
+			if (sptr == 1)
+			    break;
+			newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
+			if (!ZC_iident(cc))
+			    break;
+		    }
+		} else {
+		    for (;;) {
+			sptr = newpos - sbuf;
+			if (sptr == 1)
+			    break;
+			newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc);
+			if (ZC_iident(cc) || ZC_iblank(cc))
+			    break;
+		    }
+		}
+	    }
 	} else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) {
 	    if(cmd == Th(z_viquotedinsert)) {
-		sbuf[sptr] = ZWC('^');
+		sbuf[sptr] = '^';
 		zrefresh();
 	    }
 	    if (getfullchar(0) == ZLEEOF)
@@ -1405,12 +1475,11 @@
 	    }
 	  ins:
 	    if (sptr == ssbuf - 1) {
-		ZLE_STRING_T newbuf =
-		    (ZLE_STRING_T) zhalloc((ssbuf *= 2) * ZLE_CHAR_SIZE);
-		ZS_strcpy(newbuf, sbuf);
+		char *newbuf = (char *)zhalloc((ssbuf *= 2));
+		strcpy(newbuf, sbuf);
 		statusline = sbuf = newbuf;
 	    }
-	    sbuf[sptr++] = LASTFULLCHAR;
+	    sptr += zlecharasstring(LASTFULLCHAR, sbuf + sptr);
 	} else {
 	    feep = 1;
 	}
@@ -1471,8 +1540,6 @@
 virepeatsearch(UNUSED(char **args))
 {
     Histent he;
-    ZLE_STRING_T srcstr;
-    int srclen;
     int n = zmult;
     struct zle_text zt;
 
@@ -1482,28 +1549,26 @@
 	n = -n;
 	visrchsense = -visrchsense;
     }
-    srcstr = stringaszleline(visrchstr, 0, &srclen, NULL, NULL);
     if (!(he = quietgethist(histline)))
 	return 1;
+    metafy_line();
     while ((he = movehistent(he, visrchsense, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 	    continue;
 	zletext(he, &zt);
-	if (zlinecmp(zt.text, zt.len, zleline, zlell) &&
-	    (*visrchstr == '^'?
-	     (zt.len == srclen - 1 &&
-	      ZS_memcmp(zt.text, srcstr + 1, zt.len) == 0) :
-	     zlinefind(zt.text, zt.len, 0, srcstr, srclen, 1, 1) != 0)) {
+	if (zlinecmp(zt.text, zlemetaline) &&
+	    (*visrchstr == '^' ? strpfx(zt.text, visrchstr + 1) :
+	     zlinefind(zt.text, 0, visrchstr, 1, 1) != 0)) {
 	    if (--n <= 0) {
+		unmetafy_line();
 		zletextfree(&zt);
 		zle_setline(he);
-		free(srcstr);
 		return 0;
 	    }
 	}
 	zletextfree(&zt);
     }
-    free(srcstr);
+    unmetafy_line();
     return 1;
 }
 
@@ -1540,13 +1605,20 @@
     }
     if (!(he = quietgethist(histline)))
 	return 1;
+    metafy_line();
     while ((he = movehistent(he, -1, hist_skip_flags))) {
+	int tst;
+	char sav;
 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 	    continue;
 	zletext(he, &zt);
-	if (zlinecmp(zt.text, zt.len, zleline, zlecs) < 0 &&
-	    zlinecmp(zt.text, zt.len, zleline, zlell)) {
+	sav = zlemetaline[zlemetacs];
+	zlemetaline[zlemetacs] = '\0';
+	tst = zlinecmp(zt.text, zlemetaline);
+	zlemetaline[zlemetacs] = sav;
+	if (tst < 0 && zlinecmp(zt.text, zlemetaline)) {
 	    if (--n <= 0) {
+		unmetafy_line();
 		zletextfree(&zt);
 		zle_setline(he);
 		zlecs = cpos;
@@ -1556,6 +1628,7 @@
 	}
 	zletextfree(&zt);
     }
+    unmetafy_line();
     return 1;
 }
 
@@ -1580,14 +1653,20 @@
     }
     if (!(he = quietgethist(histline)))
 	return 1;
+    metafy_line();
     while ((he = movehistent(he, 1, hist_skip_flags))) {
+	char sav;
+	int tst;
 	if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP)
 	    continue;
 	zletext(he, &zt);
-	if (zlinecmp(zt.text, zt.len, zleline, zlecs) <
-	    (he->histnum == curhist) &&
-	    zlinecmp(zt.text, zt.len, zleline, zlell)) {
+	sav = zlemetaline[zlemetacs];
+	zlemetaline[zlemetacs] = '\0';
+	tst = zlinecmp(zt.text, zlemetaline) < (he->histnum == curhist);
+	zlemetaline[zlemetacs] = sav;
+	if (tst && zlinecmp(zt.text, zlemetaline)) {
 	    if (--n <= 0) {
+		unmetafy_line();
 		zletextfree(&zt);
 		zle_setline(he);
 		zlecs = cpos;
@@ -1597,5 +1676,6 @@
 	}
 	zletextfree(&zt);
     }
+    unmetafy_line();
     return 1;
 }
Index: Src/Zle/zle_main.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_main.c,v
retrieving revision 1.109
diff -u -r1.109 zle_main.c
--- Src/Zle/zle_main.c	20 Apr 2008 16:57:21 -0000	1.109
+++ Src/Zle/zle_main.c	20 Apr 2008 20:52:43 -0000
@@ -143,12 +143,10 @@
 /**/
 mod_export Widget compwidget;
 
-/* the status line, and its length */
+/* the status line, a null-terminated metafied string */
 
 /**/
-mod_export ZLE_STRING_T statusline;
-/**/
-mod_export int statusll;
+mod_export char *statusline;
 
 /* The current history line and cursor position for the top line *
  * on the buffer stack.                                          */
@@ -1240,12 +1238,16 @@
 int
 execzlefunc(Thingy func, char **args, int set_bindk)
 {
-    int r = 0, ret = 0;
+    int r = 0, ret = 0, remetafy = 0;
     Widget w;
     Thingy save_bindk = bindk;
 
     if (set_bindk)
 	bindk = func;
+    if (zlemetaline) {
+	unmetafy_line();
+	remetafy = 1;
+    }
 
     if(func->flags & DISABLED) {
 	/* this thingy is not the name of a widget */
@@ -1350,6 +1352,8 @@
      * directly.
      */
     CCRIGHT();
+    if (remetafy)
+	metafy_line();
     return ret;
 }
 
@@ -1632,8 +1636,7 @@
     if (statusline)
 	return 1;
     clearlist = 1;
-    statusline = ZWS("Describe key briefly: _");
-    statusll = ZS_strlen(statusline);
+    statusline = "Describe key briefly: _";
     zrefresh();
     seq = getkeymapcmd(curkeymap, &func, &str);
     statusline = NULL;
Index: Src/Zle/zle_misc.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_misc.c,v
retrieving revision 1.47
diff -u -r1.47 zle_misc.c
--- Src/Zle/zle_misc.c	15 Apr 2008 16:49:55 -0000	1.47
+++ Src/Zle/zle_misc.c	20 Apr 2008 20:52:43 -0000
@@ -914,24 +914,27 @@
     Thingy cmd;
     int l, len, feep = 0, listed = 0, curlist = 0;
     int ols = (listshown && validlist), olll = lastlistlen;
-    ZLE_STRING_T cmdbuf, ptr, zprmt;
+    char *cmdbuf, *ptr;
     char *okeymap = ztrdup(curkeymapname);
 
     clearlist = 1;
     /* prmt may be constant */
     prmt = ztrdup(prmt);
-    zprmt = stringaszleline(prmt, 0, &l, NULL, NULL);
-    cmdbuf = zhalloc((l + NAMLEN + 2) * ZLE_CHAR_SIZE);
-    ZS_memcpy(cmdbuf, zprmt, l);
-    free(zprmt);
+    l = strlen(prmt);
+    cmdbuf = (char *)zhalloc(l + NAMLEN + 2 +
+#ifdef MULTIBYTE_SUPPORT
+			     2 * MB_CUR_MAX
+#endif
+			     );
+    strcpy(cmdbuf, prmt);
     zsfree(prmt);
     statusline = cmdbuf;
     selectkeymap("main", 1);
     ptr = cmdbuf += l;
     len = 0;
     for (;;) {
-	*ptr = ZWC('_');
-	statusll = l + len + 1;
+	*ptr = '_';
+	ptr[1] = '\0';
 	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    statusline = NULL;
@@ -966,31 +969,45 @@
 		zmult = zmultsav;
 	    }
 	} else if(cmd == Th(z_viquotedinsert)) {
-	    *ptr = ZWC('^');
+	    *ptr = '^';
 	    zrefresh();
 	    getfullchar(0);
-	    if(LASTFULLCHAR == ZLEEOF || !LASTFULLCHAR || len == NAMLEN)
+	    if(LASTFULLCHAR == ZLEEOF || !LASTFULLCHAR || len >= NAMLEN)
 		feep = 1;
 	    else {
-		*ptr++ = LASTFULLCHAR, len++, curlist = 0;
+		int ret = zlecharasstring(LASTFULLCHAR, ptr);
+		len += ret;
+		ptr += ret;
+		curlist = 0;
 	    }
 	} else if(cmd == Th(z_quotedinsert)) {
 	    if(getfullchar(0) == ZLEEOF ||
 	       !LASTFULLCHAR || len == NAMLEN)
 		feep = 1;
 	    else {
-		*ptr++ = LASTFULLCHAR, len++, curlist = 0;
+		int ret = zlecharasstring(LASTFULLCHAR, ptr);
+		len += ret;
+		ptr += ret;
+		curlist = 0;
 	    }
 	} else if(cmd == Th(z_backwarddeletechar) ||
-	    	cmd == Th(z_vibackwarddeletechar)) {
+		  cmd == Th(z_vibackwarddeletechar)) {
 	    if (len) {
-		len--, ptr--, curlist = 0;
+		ptr = backwardmetafiedchar(cmdbuf, ptr, NULL);
+		len = ptr - cmdbuf;
+		curlist = 0;
 	    }
 	} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
 		  cmd == Th(z_vibackwardkillword)) {
 	    if (len)
 		curlist = 0;
-	    while (len && (len--, *--ptr != ZWC('-')));
+	    while (len) {
+		convchar_t cc;
+		ptr = backwardmetafiedchar(cmdbuf, ptr, &cc);
+		len = ptr - cmdbuf;
+		if (cc == ZWC('-'))
+		    break;
+	    }
 	} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
 	    	cmd == Th(z_backwardkillline)) {
 	    len = 0;
@@ -1003,10 +1020,7 @@
 		Thingy r;
 		unambiguous:
 		*ptr = 0;
-		namedcmdstr = zlelineasstring(cmdbuf, len, 0, NULL, NULL, 0);
-		r = rthingy(namedcmdstr);
-		free(namedcmdstr);
-		namedcmdstr = NULL;
+		r = rthingy(cmdbuf);
 		if (!(r->flags & DISABLED)) {
 		    unrefthingy(r);
 		    statusline = NULL;
@@ -1033,9 +1047,9 @@
 
 		namedcmdll = newlinklist();
 
-		namedcmdstr = zlelineasstring(cmdbuf, len, 0, NULL, NULL, 0);
+		*ptr = '\0';
+		namedcmdstr = cmdbuf;
 		scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
-		free(namedcmdstr);
 		namedcmdstr = NULL;
 
 		if (empty(namedcmdll)) {
@@ -1046,39 +1060,29 @@
 		} else if (cmd == Th(z_listchoices) ||
 		    cmd == Th(z_deletecharorlist)) {
 		    int zmultsav = zmult;
-		    *ptr = ZWC('_');
-		    statusll = l + len + 1;
+		    *ptr = '_';
+		    ptr[1] = '\0';
 		    zmult = 1;
 		    listlist(namedcmdll);
 		    listed = curlist = 1;
 		    showinglist = 0;
 		    zmult = zmultsav;
 		} else if (!nextnode(firstnode(namedcmdll))) {
-		    char *peekstr = ztrdup(peekfirst(namedcmdll));
-		    ZLE_STRING_T ztmp = stringaszleline(peekstr, 0, &len,
-							NULL, NULL);
-		    zsfree(peekstr);
-		    ZS_memcpy(ptr = cmdbuf, ztmp, len);
+		    strcpy(ptr = cmdbuf, peekfirst(namedcmdll));
+		    len = strlen(ptr);
 		    ptr += len;
-		    free(ztmp);
-		    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode))
+		    if (cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode))
 			goto unambiguous;
 		} else {
-		    int ltmp;
-		    char *peekstr = ztrdup(peekfirst(namedcmdll));
-		    ZLE_STRING_T ztmp = stringaszleline(peekstr, 0, &ltmp,
-							NULL, NULL);
-		    zsfree(peekstr);
-		    ZS_memcpy(cmdbuf, ztmp, ltmp);
-		    free(ztmp);
+		    strcpy(cmdbuf, peekfirst(namedcmdll));
 		    ptr = cmdbuf + namedcmdambig;
-		    *ptr = ZWC('_');
+		    *ptr = '_';
+		    ptr[1] = '\0';
 		    if (isset(AUTOLIST) &&
 			!(isset(LISTAMBIGUOUS) && namedcmdambig > len)) {
 			int zmultsav = zmult;
 			if (isset(LISTBEEP))
 			    feep = 1;
-			statusll = l + namedcmdambig + 1;
 			zmult = 1;
 			listlist(namedcmdll);
 			listed = curlist = 1;
@@ -1100,8 +1104,16 @@
 #endif
 		    if (ZC_icntrl(LASTFULLCHAR))
 			feep = 1;
-		    else
-			*ptr++ = LASTFULLCHAR, len++, curlist = 0;
+		    else {
+			int ret = zlecharasstring(LASTFULLCHAR, ptr);
+			len += ret;
+			ptr += ret;
+			if (listed) {
+			    clearlist = listshown = 1;
+			    listed = 0;
+			} else
+			    curlist = 0;
+		    }
 		}
 	    }
 	}
Index: Src/Zle/zle_move.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_move.c,v
retrieving revision 1.12
diff -u -r1.12 zle_move.c
--- Src/Zle/zle_move.c	15 Apr 2008 16:49:55 -0000	1.12
+++ Src/Zle/zle_move.c	20 Apr 2008 20:52:43 -0000
@@ -158,6 +158,144 @@
 }
 #endif
 
+
+/* Size of buffer in the following function */
+#define BMC_BUFSIZE MB_CUR_MAX
+/*
+ * For a metafied string that starts at "start" and where the
+ * current position is "ptr", go back one full character,
+ * taking account of combining characters if necessary.
+ */
+
+/**/
+char *
+backwardmetafiedchar(char *start, char *ptr, convchar_t *retchr)
+{
+#ifdef MULTIBYTE_SUPPORT
+    int charlen = 0;
+    char *last = NULL, *bufptr, *endptr = ptr;
+    convchar_t lastc;
+    mbstate_t mbs;
+    size_t ret;
+    wchar_t wc;
+    VARARR(char, buf, BMC_BUFSIZE);
+
+    bufptr = buf + BMC_BUFSIZE;
+    while (ptr > start) {
+	ptr--;
+	/*
+	 * Scanning backwards we're not guaranteed ever to find a
+	 * valid character.  If we've looked as far as we should
+	 * need to, give up.
+	 */
+	if (bufptr-- == buf)
+	    break;
+	charlen++;
+	if (ptr > start && ptr[-1] == Meta)
+	    *bufptr = *ptr-- ^ 32;
+	else
+	    *bufptr = *ptr;
+
+	/* we always need to restart the character from scratch */
+	memset(&mbs, 0, sizeof(mbs));
+	ret = mbrtowc(&wc, bufptr, charlen, &mbs);
+	if (ret == 0) {
+	    /* NULL: unlikely, but handle anyway. */
+	    if (last) {
+		if (retchr)
+		    *retchr = lastc;
+		return last;
+	    } else {
+		if (retchr)
+		    *retchr = wc;
+		return ptr;
+	    }
+	}
+	if (ret >= 0) {
+	    if (ret < charlen) {
+		/* The last character didn't convert, so use it raw. */
+		break;
+	    }
+	    if (!isset(COMBININGCHARS)) {
+		if (retchr)
+		    *retchr = wc;
+		return ptr;
+	    }
+ 	    /* HERE: test for combining char, fix when test changes */
+	    if (!iswpunct(wc) || wcwidth(wc) != 0) {
+		/* not a combining character... */
+		if (last) {
+		    /*
+		     * ... but we were looking for a suitable base character,
+		     * test it.
+		     */
+		    /* HERE this test will change too */
+		    if (iwsalnum(wc) && wcwidth(wc) > 0) {
+			/*
+			 * Yes, this will do.
+			 */
+			if (retchr)
+			    *retchr = wc;
+			return ptr;
+		    } else {
+			/* No, just return the first character we found */
+			if (retchr)
+			    *retchr = lastc;
+			return last;
+		    }
+		}
+		/* This is the first character, so just return it. */
+		if (retchr)
+		    *retchr = wc;
+		return ptr;    
+	    }
+	    if (!last) {
+		/* still looking for the character immediately before ptr */
+		last = ptr;
+	    }
+	    /* searching for base character of combining character */
+	    charlen = 0;
+	    bufptr = buf + BMC_BUFSIZE;
+	}
+	/*
+	 * Else keep scanning this character even if MB_INVALID:  we can't
+	 * expect MB_INCOMPLETE to work when moving backwards.
+	 */
+    }
+    /*
+     * Found something we didn't like, was there a good character
+     * immediately before ptr?
+     */
+    if (last) {
+	if (retchr)
+	    *retchr = lastc;
+	return last;
+    }
+    /*
+     * No, we couldn't find any good character, so just treat
+     * the last unmetafied byte we found as a character.
+     */
+#endif
+    if (endptr > start) {
+	if (endptr > start - 1 && endptr[-2] == Meta)
+	{
+	    if (retchr)
+		*retchr = (convchar_t)(endptr[-1] ^ 32);
+	    return endptr - 2;
+	}
+	else
+	{
+	    if (retchr)
+		*retchr = (convchar_t)endptr[-1];
+	    return endptr - 1;
+	}
+    }
+    if (retchr)
+	*retchr = (convchar_t)0;
+    return endptr;
+}
+
+
 /**/
 int
 beginningofline(char **args)
Index: Src/Zle/zle_refresh.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_refresh.c,v
retrieving revision 1.58
diff -u -r1.58 zle_refresh.c
--- Src/Zle/zle_refresh.c	18 Apr 2008 13:50:54 -0000	1.58
+++ Src/Zle/zle_refresh.c	20 Apr 2008 20:52:44 -0000
@@ -1387,11 +1387,15 @@
 	more_end = 1;
 
     if (statusline) {
+	int outll, outsz;
+	ZLE_STRING_T outputline =
+	    stringaszleline(statusline, 0, &outll, &outsz, NULL); 
+
 	rpms.tosln = rpms.ln + 1;
 	nbuf[rpms.ln][winw + 1] = zr_zr;	/* text not wrapped */
 	snextline(&rpms);
-	u = statusline;
-	for (; u < statusline + statusll; u++) {
+	u = outputline;
+	for (; u < outputline + outll; u++) {
 #ifdef MULTIBYTE_SUPPORT
 	    if (iswprint(*u)) {
 		int width = wcwidth(*u);
@@ -1449,6 +1453,7 @@
 	     */
 	    snextline(&rpms);
 	}
+	zfree(outputline, outsz);
     }
     *rpms.s = zr_zr;
 
Index: Src/Zle/zle_thingy.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_thingy.c,v
retrieving revision 1.33
diff -u -r1.33 zle_thingy.c
--- Src/Zle/zle_thingy.c	18 Dec 2007 10:42:37 -0000	1.33
+++ Src/Zle/zle_thingy.c	20 Apr 2008 20:52:44 -0000
@@ -406,16 +406,15 @@
 static int
 bin_zle_refresh(UNUSED(char *name), char **args, Options ops, UNUSED(char func))
 {
-    ZLE_STRING_T s = statusline;
-    int sl = statusll, ocl = clearlist;
+    char *s = statusline;
+    int ocl = clearlist;
 
     if (!zleactive)
 	return 1;
     statusline = NULL;
-    statusll = 0;
     if (*args) {
 	if (**args)
-	    statusline = stringaszleline(*args, 0, &statusll, NULL, NULL);
+	    statusline = *args;
 	if (*++args) {
 	    LinkList l = newlinklist();
 	    int zmultsav = zmult;
@@ -439,12 +438,8 @@
     }
     zrefresh();
 
-    if (statusline)
-	free(statusline);
-
     clearlist = ocl;
     statusline = s;
-    statusll = sl;
     return 0;
 }
 
Index: Src/Zle/zle_utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_utils.c,v
retrieving revision 1.48
diff -u -r1.48 zle_utils.c
--- Src/Zle/zle_utils.c	15 Apr 2008 16:49:55 -0000	1.48
+++ Src/Zle/zle_utils.c	20 Apr 2008 20:52:44 -0000
@@ -106,6 +106,54 @@
 }
 
 /*
+ * Convert a line editor character to a possibly multibyte character
+ * in a metafied string.  To be safe buf should have space for at least
+ * 2 * MB_CUR_MAX chars for multibyte mode and 2 otherwise.  Returns the
+ * length of the string added.
+ */
+
+/**/
+int
+zlecharasstring(ZLE_CHAR_T inchar, char *buf)
+{
+#ifdef MULTIBYTE_SUPPORT
+    size_t ret;
+    char *ptr;
+
+    ret = wctomb(buf, inchar);
+    if (ret <= 0) {
+	/* Ick. */
+	buf[0] = '?';
+	return 1;
+    }
+    ptr = buf + ret - 1;
+    for (;;) {
+	if (imeta(*ptr)) {
+	    char *ptr2 = buf + ret - 1;
+	    for (;;) {
+		ptr2[1] = ptr2[0];
+		if (ptr2 == ptr)
+		    break;
+		ptr2--;
+	    }
+	    *ptr = Meta;
+	    ret++;
+	}
+
+	if (ptr == buf)
+	    return ret;
+	ptr--;
+    }
+#else
+    if (imeta(inchar)) {
+	buf[0] = Meta;
+	buf[1] = inchar ^ 32;
+    } else
+	buf[0] = inchar;
+#endif
+}
+
+/*
  * Input a line in internal zle format, possibly using wide characters,
  * possibly not, together with its length and the cursor position.
  * The length must be accurate and includes all characters (no NULL
@@ -621,7 +669,7 @@
 setline(char *s, int flags)
 {
     char *scp;
-
+	
     if (flags & ZSL_COPY)
 	scp = ztrdup(s);
     else
@@ -639,7 +687,6 @@
     else if (zlecs > zlell)
 	zlecs = zlell;
     CCRIGHT();
-
     if (flags & ZSL_COPY)
 	free(scp);
 }
Index: Src/Zle/zle_vi.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_vi.c,v
retrieving revision 1.18
diff -u -r1.18 zle_vi.c
--- Src/Zle/zle_vi.c	20 Apr 2008 16:57:21 -0000	1.18
+++ Src/Zle/zle_vi.c	20 Apr 2008 20:52:45 -0000
@@ -853,8 +853,7 @@
 {
     clearlist = 1;
     zbeep();
-    statusline = ZWS("press a lowercase key to continue");
-    statusll = ZS_strlen(statusline);
+    statusline = "press a lowercase key to continue";
     zrefresh();
     while (!ZC_ilower(getfullchar(0)));
     statusline = NULL;
Index: Src/Zle/zle_word.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_word.c,v
retrieving revision 1.12
diff -u -r1.12 zle_word.c
--- Src/Zle/zle_word.c	18 Apr 2008 11:49:25 -0000	1.12
+++ Src/Zle/zle_word.c	20 Apr 2008 20:52:45 -0000
@@ -185,7 +185,6 @@
 	return ret;
     }
     while (n--) {
-	/* HERE: the zlecs + 1 here is suspect */
 	int pos;
 	while (zlecs != zlell) {
 	    pos = zlecs;


-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: PATCH: optimise string conversions in history etc.
  2008-04-20 21:11 PATCH: optimise string conversions in history etc Peter Stephenson
@ 2008-05-05  1:20 ` Wayne Davison
  0 siblings, 0 replies; 2+ messages in thread
From: Wayne Davison @ 2008-05-05  1:20 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh hackers list

On Sun, Apr 20, 2008 at 10:11:48PM +0100, Peter Stephenson wrote:
> +	ret = mbrtowc(&wc, bufptr, charlen, &mbs);
> +	if (ret == 0) {
[...]
> +	}
> +	if (ret >= 0) {

Since ret is a size_t, "ret >= 0" is always true.  I've checked in a fix
for that.

Note that I catch things like this due to having more compiler warnings
enabled (-W):

--- configure.ac	4 May 2008 18:43:01 -0000	1.100
+++ configure.ac	5 May 2008 01:06:21 -0000
@@ -385,13 +385,13 @@ dnl   else use -O
 if test -n "$auto_cflags" && test ."$ansi2knr" != .yes; then
   if test "${enable_zsh_debug}" = yes; then
     if test -n "$GCC"; then
-      CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -ggdb"
+      CFLAGS="$CFLAGS -Wall -W -Wno-unused-parameter -ggdb"
     else
       CFLAGS="$CFLAGS -g"
     fi
   else
     if test -n "$GCC"; then
-      CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -O2"
+      CFLAGS="$CFLAGS -Wall -W -Wno-unused-parameter -O2"
     else
       CFLAGS="$CFLAGS -O"
     fi

If we want to commit a change like that, we'd need to ensure that
-Wno-unused-parameter was accepted by the compiler (since it wasn't
in older gcc versions).

..wayne..


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2008-05-05  1:20 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-20 21:11 PATCH: optimise string conversions in history etc Peter Stephenson
2008-05-05  1:20 ` Wayne Davison

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).