about summary refs log tree commit diff
path: root/Src/Zle/computil.c
diff options
context:
space:
mode:
authorAndrey Borzenkov <bor@users.sourceforge.net>2006-09-23 14:43:57 +0000
committerAndrey Borzenkov <bor@users.sourceforge.net>2006-09-23 14:43:57 +0000
commit71fa876defaafe49874b93fdff0c7c4a4e72c878 (patch)
tree3975b51121b37df489a760faf78e0a0fc8aa39f8 /Src/Zle/computil.c
parentfa68d6fa0a214391154288f0450f2f8fa234fdf7 (diff)
downloadzsh-71fa876defaafe49874b93fdff0c7c4a4e72c878.tar.gz
zsh-71fa876defaafe49874b93fdff0c7c4a4e72c878.tar.xz
zsh-71fa876defaafe49874b93fdff0c7c4a4e72c878.zip
22761: fix compdescrie for multibyte characters
Diffstat (limited to 'Src/Zle/computil.c')
-rw-r--r--Src/Zle/computil.c186
1 files changed, 129 insertions, 57 deletions
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 5ecc4b6be..17eb7a8df 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -33,6 +33,13 @@
 
 /* Help for `_describe'. */
 
+/*
+ * FIXME this should be defined globally. I have to find other places
+ * where it is used
+ * */
+
+#define INTERMATCH_GAP 2
+
 typedef struct cdset *Cdset;
 typedef struct cdstr *Cdstr;
 typedef struct cdrun *Cdrun;
@@ -40,16 +47,18 @@ typedef struct cdrun *Cdrun;
 struct cdstate {
     int showd;			/* != 0 if descriptions should be shown */
     char *sep;			/* the separator string */
-    int slen;			/* its length */
+    int slen;			/* its metafied length */
+    int swidth;			/* its screen width */
     int maxmlen;                /* maximum length to allow for the matches */
     Cdset sets;			/* the sets of matches */
-    int pre;                    /* longest prefix (before description) */
+    int pre;                    /* longest prefix length (before description) */
+    int premaxw;		/* ... and its screen width */
     int suf;                    /* longest suffix (description) */
     int maxg;                   /* size of largest group */
     int maxglen;                /* columns for matches of largest group */
     int groups;                 /* number of groups */
     int descs;                  /* number of non-group matches with desc */
-    int gpre;                   /* prefix length for group display */
+    int gprew;                   /* prefix screen width for group display */
     Cdrun runs;                 /* runs to report to shell code */
 };
 
@@ -58,7 +67,9 @@ struct cdstr {
     char *str;                  /* the string to display */
     char *desc;                 /* the description or NULL */
     char *match;                /* the match to add */
+    char *sortstr;		/* unmetafied string used to sort matches */
     int len;                    /* length of str or match */
+    int width;			/* ... and its screen width */
     Cdstr other;                /* next string with the same description */
     int kind;                   /* 0: not in a group, 1: the first, 2: other */
     Cdset set;                  /* the set this string is in */
@@ -102,6 +113,7 @@ freecdsets(Cdset p)
 	    freearray(p->opts);
         for (s = p->strs; s; s = sn) {
             sn = s->next;
+	    zfree(s->sortstr, strlen(s->str) + 1);
             zsfree(s->str);
             zsfree(s->desc);
             if (s->match != s->str)
@@ -123,7 +135,7 @@ cd_group(int maxg)
 {
     Cdset set1, set2;
     Cdstr str1, str2, *strp;
-    int num, len;
+    int num, width;
 
     cd_state.groups = cd_state.descs = cd_state.maxglen = 0;
     cd_state.maxg = 0;
@@ -140,20 +152,20 @@ cd_group(int maxg)
                 continue;
 
             num = 1;
-            len = str1->len + cd_state.slen;
-            if (len > cd_state.maxglen)
-                cd_state.maxglen = len;
+            width = str1->width + cd_state.swidth;
+            if (width > cd_state.maxglen)
+                cd_state.maxglen = width;
             strp = &(str1->other);
 
             for (set2 = set1; set2; set2 = set2->next) {
                 for (str2 = (set2 == set1 ? str1->next : set2->strs);
                      str2; str2 = str2->next)
                     if (str2->desc && !strcmp(str1->desc, str2->desc)) {
-                        len += 2 + str2->len;
-                        if (len > cd_state.maxmlen || num == maxg)
+                        width += INTERMATCH_GAP + str2->width;
+                        if (width > cd_state.maxmlen || num == maxg)
                             break;
-                        if (len > cd_state.maxglen)
-                            cd_state.maxglen = len;
+                        if (width > cd_state.maxglen)
+                            cd_state.maxglen = width;
                         str1->kind = 1;
                         str2->kind = 2;
                         num++;
@@ -194,6 +206,8 @@ cd_calc()
             set->count++;
             if ((l = strlen(str->str)) > cd_state.pre)
                 cd_state.pre = l;
+            if ((l = MB_METASTRWIDTH(str->str)) > cd_state.premaxw)
+                cd_state.premaxw = l;
             if (str->desc) {
                 set->desc++;
                 if ((l = strlen(str->desc)) > cd_state.suf)
@@ -206,7 +220,7 @@ cd_calc()
 static int
 cd_sort(const void *a, const void *b)
 {
-    return strcmp((*((Cdstr *) a))->str, (*((Cdstr *) b))->str);
+    return strpcmp(&(*((Cdstr *) a))->sortstr, &(*((Cdstr *) b))->sortstr);
 }
 
 static int
@@ -234,8 +248,8 @@ cd_prep()
             for (str = set->strs; str; str = str->next) {
                 if (str->kind != 1) {
                     if (!str->kind && str->desc) {
-                        if (str->len > wids[0])
-                            wids[0] = str->len;
+                        if (str->width > wids[0])
+                            wids[0] = str->width;
                         str->other = NULL;
                         *strp++ = str;
                     }
@@ -248,25 +262,34 @@ cd_prep()
                 for (; gp; gp = gn) {
                     gn = gp->other;
                     gp->other = NULL;
-                    for (gpp = &gs; *gpp && (*gpp)->len > gp->len;
+                    for (gpp = &gs; *gpp && (*gpp)->width > gp->width;
                          gpp = &((*gpp)->other));
                     gp->other = *gpp;
                     *gpp = gp;
                 }
                 for (gp = gs, i = 0; gp; gp = gp->other, i++)
-                    if (gp->len > wids[i])
-                        wids[i] = gp->len;
+                    if (gp->width > wids[i])
+                        wids[i] = gp->width;
 
                 *strp++ = gs;
             }
 
-        cd_state.gpre = 0;
-        for (i = 0; i < cd_state.maxg; i++)
-            cd_state.gpre += wids[i] + 2;
+        cd_state.gprew = 0;
+        for (i = 0; i < cd_state.maxg; i++) {
+            cd_state.gprew += wids[i] + INTERMATCH_GAP;
+	}
 
-        if (cd_state.gpre > cd_state.maxmlen && cd_state.maxglen > 1)
+        if (cd_state.gprew > cd_state.maxmlen && cd_state.maxglen > 1)
             return 1;
 
+	for (i = 0; i < lines; i++) {
+	    Cdstr s = grps[i];
+	    int dummy;
+
+	    s->sortstr = ztrdup(s->str);
+	    unmetafy(s->sortstr, &dummy);
+	}
+
         qsort(grps, lines, sizeof(Cdstr), cd_sort);
 
         for (i = lines, strp = grps; i > 1; i--, strp++) {
@@ -443,12 +466,13 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
     }
     setp = &(cd_state.sets);
     cd_state.sep = ztrdup(sep);
-    cd_state.slen = ztrlen(sep);
+    cd_state.slen = strlen(sep);
+    cd_state.swidth = MB_METASTRWIDTH(sep);
     cd_state.sets = NULL;
     cd_state.showd = disp;
     cd_state.maxg = cd_state.groups = cd_state.descs = 0;
     cd_state.maxmlen = atoi(mlen);
-    itmp = columns - cd_state.slen - 4;
+    itmp = columns - cd_state.swidth - 4;
     if (cd_state.maxmlen > itmp)
         cd_state.maxmlen = itmp;
     if (cd_state.maxmlen < 4)
@@ -489,6 +513,8 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
             *tmp = '\0';
             str->str = str->match = ztrdup(rembslash(*ap));
             str->len = strlen(str->str);
+            str->width = MB_METASTRWIDTH(str->str);
+	    str->sortstr = NULL;
         }
         if (str)
             str->next = NULL;
@@ -600,38 +626,61 @@ cd_get(char **params)
 
         case CRT_DESC:
             {
+		/*
+		 * The buffer size:
+		 *     max prefix length (cd_state.pre) +
+		 *     max padding (cd_state.premaxw generously :) +
+		 *     separator length (cd_state.slen) +
+		 *     inter matches gap (INTERMATCH_GAP) +
+		 *     max description length (cd_state.suf) +
+		 *     trailing \0
+		 */
                 VARARR(char, buf,
-                       cd_state.pre + cd_state.suf + cd_state.slen + 3);
-                char *sufp = NULL;
-
-                memcpy(buf + cd_state.pre + 2, cd_state.sep, cd_state.slen);
-                buf[cd_state.pre] = buf[cd_state.pre + 1] = ' ';
-                sufp = buf + cd_state.pre + cd_state.slen + 2;
-
+                       cd_state.pre + cd_state.suf +
+		       cd_state.premaxw + cd_state.slen + 3);
                 mats = mp = (char **) zalloc((run->count + 1) * sizeof(char *));
                 dpys = dp = (char **) zalloc((run->count + 1) * sizeof(char *));
 
                 for (str = run->strs; str; str = str->run) {
+		    char *p = buf, *pp, *d;
+		    int l, remw, w;
+
                     *mp++ = ztrdup(str->match);
-                    memset(buf, ' ', cd_state.pre);
-                    memcpy(buf, str->str, str->len);
-                    strcpy(sufp, str->desc);
-                    if (MB_METASTRWIDTH(buf) >= columns - 1) {
-			char *termptr = buf;
-			int w;
-			MB_METACHARINIT();
-			for (w = columns - 1; *termptr && w > 0; ) {
-			    convchar_t cchar;
-			    int cw;
-			    termptr += MB_METACHARLENCONV(termptr, &cchar);
-			    cw = WCWIDTH(cchar);
-			    if (cw >= 0)
-				w -= cw;
-			    else
-				w--;
+		    strcpy(p, str->str);
+		    p += str->len;
+                    memset(p, ' ', (l = (cd_state.premaxw - str->width + INTERMATCH_GAP)));
+		    p += l;
+		    strcpy(p, cd_state.sep);
+		    p += cd_state.slen;
+
+		    /*
+		     * copy a character at once until no more screen width
+		     * is available. Leave 1 character at the end of screen
+		     * as safety margin
+		     */
+		    remw = columns - cd_state.premaxw - cd_state.swidth - 3;
+		    d = str->desc;
+		    w = MB_METASTRWIDTH(d);
+		    if (w <= remw)
+			strcpy(p, d);
+		    else {
+			pp = p;
+			while (remw > 0 && *d) {
+			    l = MB_METACHARLEN(d);
+			    memcpy(pp, d, l);
+			    pp[l] = '\0';
+			    w = MB_METASTRWIDTH(pp);
+			    if (w > remw) {
+				*pp = '\0';
+				break;
+			    }
+
+			    pp += l;
+			    d += l;
+			    remw -= w;
 			}
-                        *termptr = '\0';
 		    }
+
                     *dp++ = ztrdup(buf);
                 }
                 *mp = *dp = NULL;
@@ -684,10 +733,10 @@ cd_get(char **params)
 	default: /* This silences the "might be used uninitialized" warnings */
         case CRT_EXPL:
             {
-                int dlen = columns - cd_state.gpre - cd_state.slen;
-                VARARR(char, dbuf, dlen + cd_state.slen);
-                char buf[20];
-                int i = run->count;
+		/* add columns as safety margin */
+                VARARR(char, dbuf, cd_state.suf + cd_state.slen + columns);
+                char buf[20], *p, *pp, *d;
+                int i = run->count, remw, w, l;
 
                 sprintf(buf, "-E%d", i);
 
@@ -699,13 +748,36 @@ cd_get(char **params)
                         *dp++ = ztrdup("");
                         continue;
                     }
-                    memset(dbuf + cd_state.slen, ' ', dlen - 1);
-                    dbuf[dlen + cd_state.slen - 1] = '\0';
+
                     strcpy(dbuf, cd_state.sep);
-                    memcpy(dbuf + cd_state.slen,
-                           str->desc,
-                           ((int)strlen(str->desc) >= dlen ? dlen - 1 :
-                            (int)strlen(str->desc)));
+		    remw = columns - cd_state.gprew - cd_state.swidth - INTERMATCH_GAP;
+		    p = pp = dbuf + cd_state.slen;
+		    d = str->desc;
+		    w = MB_METASTRWIDTH(d);
+		    if (w <= remw) {
+			strcpy(p, d);
+			remw -= w;
+			pp += strlen(d);
+		    } else
+			while (remw > 0 && *d) {
+			    l = MB_METACHARLEN(d);
+			    memcpy(pp, d, l);
+			    pp[l] = '\0';
+			    w = MB_METASTRWIDTH(pp);
+			    if (w > remw) {
+				*pp = '\0';
+				break;
+			    }
+
+			    pp += l;
+			    d += l;
+			    remw -= w;
+			}
+
+		    while (remw-- > 0)
+			*pp++ = ' ';
+		    *pp = '\0';
+
                     *dp++ = ztrdup(dbuf);
                 }
                 mats[0] = *dp = NULL;