about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Src/Zle/complist.c3
-rw-r--r--Src/Zle/compmatch.c84
-rw-r--r--Src/Zle/zle_main.c1
4 files changed, 90 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index d4190cfd8..4f537abe4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2005-10-02  Peter Stephenson  <pws@pwstephenson.fsnet.co.uk>
 
+	* 21802: Src/Zle/complist.c, Src/Zle/compmatch.c,
+	Src/Zle/zle_main.c: attempt to prevent matches ending in
+	the middle of multibyte characters, also some minor tidy-ups.
+
 	* unposted, c.f. 21799: Doc/Zsh/func.yo: documentation for
 	returns from TRAPNAL functions was wayward.
 
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 0b6601cea..a9d57ae43 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -575,7 +575,8 @@ clnicezputs(Listcols colors, char *s, int ml)
      * ps is the shift state of the conversion to wide characters.
      */
     char *ums, *uptr, *sptr, *wptr;
-    int ret, umleft, umlen, width;
+    int ret, umleft, umlen;
+    size_t width;
     mbstate_t ps;
 
     memset(&ps, 0, sizeof(ps));
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 7054feb6d..e4c5a438f 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -1584,6 +1584,11 @@ sub_match(Cmdata md, char *str, int len, int sfx)
 {
     int ret = 0, l, ind, add;
     char *p, *q;
+#ifdef ZLE_UNICODE_SUPPORT
+    int fulllen = len;
+    char *fullstr = str;
+    mbstate_t ps;
+#endif
 
     if (sfx) {
 	str += len;
@@ -1614,6 +1619,85 @@ sub_match(Cmdata md, char *str, int len, int sfx)
 		   || (l < md->len && q[-1] == Meta)))
 		l--;
 	}
+#ifdef ZLE_UNICODE_SUPPORT
+	/*
+	 * Make sure we don't end in the middle of a multibyte character.
+	 * Don't need to do this if the match ended at the start
+	 * of the original string.
+	 *
+	 * Let q be the match point we've found.
+	 */
+	q = sfx ? str - l : str + l;
+	if (q != fullstr) {
+	    memset(&ps, 0, sizeof(ps));
+	    /*
+	     * Otherwise read characters from the start of the original
+	     * string until we reach or pass the match point.  This
+	     * is rather inefficient, but in general only reading
+	     * the full string can keep track of where we are in
+	     * a character.  With a prefix we could be more efficient,
+	     * but it's difficult with a suffix where the match point
+	     * moves backwards.
+	     */
+	    for (p = fullstr; p < fullstr + fulllen; ) {
+		wchar_t wc;
+		/*
+		 * ret must, in fact, be set by the current logic,
+		 * but gcc doesn't realise (at least some versions don't).
+		 */
+		int ret = -1, diff;
+		char *p2;
+
+		/*
+		 * Because the string is metafied, we need to
+		 * assembled wide characters a byte at a time.
+		 */
+		for (p2 = p; p2 < fullstr + fulllen; p2++) {
+		  char curchar = (*p2 == Meta) ? (*++p2 ^ 32) : *p2;
+		  ret = mbrtowc(&wc, &curchar, 1, &ps);
+		  /*
+		   * Continue while character is incomplete.
+		   */
+		  if (ret != -2)
+		    break;
+		}
+		if (ret < 0) {
+		    /* not a valid character, give up test */
+		    break;
+		}
+		/* increment p2 for last byte read */
+		diff = ++p2 - q;
+		if (diff == 0) {
+		    /*
+		     * Prefix or suffix matches at end of multbyte character,
+		     * so OK.
+		     */
+		    break;
+		} else if (diff > 0) {
+		    /*
+		     * The prefix or suffix finishes in the middle
+		     * of a character.  Shorten it until it doesn't.
+		     */
+		    if (sfx) {
+			/*
+			 * We need to remove the trailing part of
+			 * the character from the suffix.
+			 */
+			l -= diff;
+		    } else {
+			/*
+			 * We need to remove the initial part of
+			 * the character from the prefix.
+			 */
+			l -= (q - p);
+		    }
+		    break;
+		}
+		/* Advance over full character */
+		p += ret;
+	    }
+	}
+#endif
 	if (l) {
 	    /* There was a common prefix, use it. */
 	    md->len -= l; len -= l;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 683771701..591b2d585 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -758,7 +758,6 @@ getfullchar(int keytmout)
 mod_export ZLE_INT_T
 getrestchar(int inchar)
 {
-    /* char cnull = '\0'; */
     char c = inchar;
     wchar_t outchar;
     int ret, timeout;