about summary refs log tree commit diff
path: root/Src/Zle/computil.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/computil.c')
-rw-r--r--Src/Zle/computil.c456
1 files changed, 364 insertions, 92 deletions
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 5c4fc3ed5..7548a87bf 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -35,6 +35,7 @@
 
 typedef struct cdset *Cdset;
 typedef struct cdstr *Cdstr;
+typedef struct cdrun *Cdrun;
 
 struct cdstate {
     int showd;			/* != 0 if descriptions should be shown */
@@ -43,6 +44,11 @@ struct cdstate {
     Cdset sets;			/* the sets of matches */
     int pre;                    /* longest prefix (before description) */
     int suf;                    /* longest suffix (description) */
+    int maxg;                   /* size of largest group */
+    int groups;                 /* number of groups */
+    int descs;                  /* number of non-group matches with desc */
+    int gpre;                   /* prefix length for group display */
+    Cdrun runs;                 /* runs to report to shell code */
 };
 
 struct cdstr {
@@ -50,10 +56,25 @@ struct cdstr {
     char *str;                  /* the string to display */
     char *desc;                 /* the description or NULL */
     char *match;                /* the match to add */
+    int len;                    /* length of str or match */
     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 */
+    Cdstr run;                  /* next in this run */
 };
 
+struct cdrun {
+    Cdrun next;                 /* ... */
+    int type;                   /* see CRT_* below */
+    Cdstr strs;                 /* strings in this run */
+    int count;                  /* number of strings in this run */
+};
+
+#define CRT_SIMPLE 0
+#define CRT_DESC   1
+#define CRT_SPEC   2
+#define CRT_DUMMY  3
+
 struct cdset {
     Cdset next;			/* guess what */
     char **opts;		/* the compadd-options */
@@ -62,11 +83,6 @@ struct cdset {
     int desc;                   /* number of matches with description */
 };
 
-/* Maximum string length when used with descriptions. */
-
-#define CD_MAXLEN 30
-
-
 static struct cdstate cd_state;
 static int cd_parsed = 0;
 
@@ -75,6 +91,7 @@ freecdsets(Cdset p)
 {
     Cdset n;
     Cdstr s, sn;
+    Cdrun r, rn;
 
     for (; p; p = n) {
 	n = p->next;
@@ -88,6 +105,10 @@ freecdsets(Cdset p)
                 zsfree(s->match);
             zfree(s, sizeof(*s));
         }
+        for (r = cd_state.runs; r; r = rn) {
+            rn = r->next;
+            zfree(r, sizeof(*r));
+        }
 	zfree(p, sizeof(*p));
     }
 }
@@ -99,14 +120,14 @@ cd_group()
 {
     Cdset set1, set2;
     Cdstr str1, str2, *strp;
-    int yep = 0;
-    char *buf;
+    int num;
 
     for (set1 = cd_state.sets; set1; set1 = set1->next) {
         for (str1 = set1->strs; str1; str1 = str1->next) {
             if (!str1->desc || str1->kind != 0)
                 continue;
 
+            num = 1;
             strp = &(str1->other);
 
             for (set2 = set1; set2; set2 = set2->next)
@@ -115,33 +136,18 @@ cd_group()
                     if (str2->desc && !strcmp(str1->desc, str2->desc)) {
                         str1->kind = 1;
                         str2->kind = 2;
-                        zsfree(str2->desc);
-                        str2->desc = ztrdup("|");
+                        num++;
                         *strp = str2;
                         strp = &(str2->other);
-                        yep = 1;
                     }
             *strp = NULL;
-        }
-    }
-    if (!yep)
-        return;
-
-    for (set1 = cd_state.sets; set1; set1 = set1->next) {
-        for (str1 = set1->strs; str1; str1 = str1->next) {
-            if (str1->kind != 1)
-                continue;
-
-            buf = str1->str;
-            for (str2 = str1->other; str2; str2 = str2->other)
-                buf = zhtricat(buf, ", ", str2->str);
+            if (num > 1)
+                cd_state.groups++;
+            else
+                cd_state.descs++;
 
-            for (str2 = str1; str2; str2 = str2->other) {
-                if (str2->str == str2->match)
-                    str2->match = ztrdup(str2->match);
-                zsfree(str2->str);
-                str2->str = ztrdup(buf);
-            }
+            if (num > cd_state.maxg)
+                cd_state.maxg = num;
         }
     }
 }
@@ -171,14 +177,172 @@ cd_calc()
             }
         }
     }
-    if (cd_state.pre > CD_MAXLEN)
-        cd_state.pre = CD_MAXLEN;
+}
+
+static int
+cd_sort(const void *a, const void *b)
+{
+    return strcmp((*((Cdstr *) a))->str, (*((Cdstr *) b))->str);
+}
+
+static void
+cd_prep()
+{
+    Cdrun run, *runp;
+    Cdset set;
+    Cdstr str, *strp;
+
+    runp = &(cd_state.runs);
+
+    if (cd_state.groups) {
+        int lines = cd_state.groups + cd_state.descs;
+        VARARR(Cdstr, grps, lines);
+        VARARR(int, wids, cd_state.maxg);
+        Cdstr gs, gp, gn, *gpp;
+        int i, j;
+
+        memset(wids, 0, cd_state.maxg * sizeof(int));
+        strp = grps;
+
+        for (set = cd_state.sets; set; set = set->next)
+            for (str = set->strs; str; str = str->next) {
+                if (str->kind != 1) {
+                    if (!str->kind && str->desc) {
+                        str->other = NULL;
+                        *strp++ = str;
+                    }
+                    continue;
+                }
+                gs = str;
+                gs->kind = 2;
+                gp = str->other;
+                gs->other = NULL;
+                for (; gp; gp = gn) {
+                    gn = gp->other;
+                    gp->other = NULL;
+                    for (gpp = &gs; *gpp && (*gpp)->len > gp->len;
+                         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;
+
+                *strp++ = gs;
+            }
+
+        qsort(grps, lines, sizeof(Cdstr), cd_sort);
+
+        for (i = lines, strp = grps; i; i--, strp++) {
+            for (j = 0, gs = *strp; gs->other; gs = gs->other, j++) {
+                *runp = run = (Cdrun) zalloc(sizeof(*run));
+                runp = &(run->next);
+                run->type = CRT_SPEC;
+                run->strs = gs;
+                gs->run = NULL;
+                run->count = 1;
+            }
+            *runp = run = (Cdrun) zalloc(sizeof(*run));
+            runp = &(run->next);
+            run->type = CRT_DUMMY + cd_state.maxg - j - 1;
+            run->strs = gs;
+            gs->run = NULL;
+            run->count = 1;
+        }
+        for (set = cd_state.sets; set; set = set->next) {
+            for (i = 0, gs = NULL, gpp = &gs, str = set->strs;
+                 str; str = str->next) {
+                if (str->kind || str->desc)
+                    continue;
+
+                i++;
+                *gpp = str;
+                gpp = &(str->run);
+            }
+            *gpp = NULL;
+            if (i) {
+                *runp = run = (Cdrun) zalloc(sizeof(*run));
+                runp = &(run->next);
+                run->type = CRT_SIMPLE;
+                run->strs = gs;
+                run->count = i;
+            }
+        }
+        cd_state.gpre = 0;
+        for (i = 0; i < cd_state.maxg; i++)
+            cd_state.gpre += wids[i] + 2;
+    } else if (cd_state.showd) {
+        for (set = cd_state.sets; set; set = set->next) {
+            if (set->desc) {
+                *runp = run = (Cdrun) zalloc(sizeof(*run));
+                runp = &(run->next);
+                run->type = CRT_DESC;
+                strp = &(run->strs);
+                for (str = set->strs; str; str = str->next)
+                    if (str->desc) {
+                        *strp = str;
+                        strp = &(str->run);
+                    }
+                *strp = NULL;
+                run->count = set->desc;
+            }
+            if (set->desc != set->count) {
+                *runp = run = (Cdrun) zalloc(sizeof(*run));
+                runp = &(run->next);
+                run->type = CRT_SIMPLE;
+                strp = &(run->strs);
+                for (str = set->strs; str; str = str->next)
+                    if (!str->desc) {
+                        *strp = str;
+                        strp = &(str->run);
+                    }
+                *strp = NULL;
+                run->count = set->count - set->desc;
+            }
+        }
+    } else {
+        for (set = cd_state.sets; set; set = set->next)
+            if (set->count) {
+                *runp = run = (Cdrun) zalloc(sizeof(*run));
+                runp = &(run->next);
+                run->type = CRT_SIMPLE;
+                run->strs = set->strs;
+                for (str = set->strs; str; str = str->next)
+                    str->run = str->next;
+                run->count = set->count;
+            }
+    }
+    *runp = NULL;
+}
+
+/* Duplicate and concatenate two arrays.  Return the result. */
+
+static char **
+cd_arrcat(char **a, char **b)
+{
+    if (!b)
+        return zarrdup(a);
+    else {
+        char **r = (char **) zalloc((arrlen(a) + arrlen(b) + 1) *
+                                    sizeof(char *));
+        char **p = r;
+
+        for (; *a; a++)
+            *p++ = ztrdup(*a);
+        for (; *b; b++)
+            *p++ = ztrdup(*b);
+
+        *p = NULL;
+
+        return r;
+    }
 }
 
 /* Initialisation. Store and calculate the string and matches and so on. */
 
 static int
-cd_init(char *nam, char *sep, char **args, int disp)
+cd_init(char *nam, char *hide, char *sep, char **opts, char **args, int disp)
 {
     Cdset *setp, set;
     Cdstr *strp, str;
@@ -195,6 +359,7 @@ cd_init(char *nam, char *sep, char **args, int disp)
     cd_state.slen = ztrlen(sep);
     cd_state.sets = NULL;
     cd_state.showd = disp;
+    cd_state.maxg = cd_state.groups = cd_state.descs = 0;
 
     if (*args && !strcmp(*args, "-g")) {
         args++;
@@ -219,6 +384,7 @@ cd_init(char *nam, char *sep, char **args, int disp)
 
             str->kind = 0;
             str->other = NULL;
+            str->set = set;
 
             for (tmp = *ap; *tmp && *tmp != ':'; tmp++)
                 if (*tmp == '\\' && tmp[1])
@@ -230,6 +396,7 @@ cd_init(char *nam, char *sep, char **args, int disp)
                 str->desc = NULL;
             *tmp = '\0';
             str->str = str->match = ztrdup(rembslash(*ap));
+            str->len = strlen(str->str);
         }
         if (str)
             str->next = NULL;
@@ -246,13 +413,23 @@ cd_init(char *nam, char *sep, char **args, int disp)
 
 	    args++;
 	}
+        if (hide && *hide) {
+            for (str = set->strs; str; str = str->next) {
+                if (str->str == str->match)
+                    str->str = ztrdup(str->str);
+                if (hide[1] && str->str[0] == '-' && str->str[1] == '-')
+                    strcpy(str->str, str->str + 2);
+                else if (str->str[0] == '-' || str->str[0] == '+')
+                    strcpy(str->str, str->str + 1);
+            }
+        }
 	for (ap = args; *args &&
 		 (args[0][0] != '-' || args[0][1] != '-' || args[0][2]);
 	     args++);
 
 	tmp = *args;
 	*args = NULL;
-	set->opts = zarrdup(ap);
+	set->opts = cd_arrcat(ap, opts);
 	if ((*args = tmp))
 	    args++;
     }
@@ -260,79 +437,157 @@ cd_init(char *nam, char *sep, char **args, int disp)
         cd_group();
 
     cd_calc();
+    cd_prep();
 
     cd_parsed = 1;
     return 0;
 }
 
+/* Copy an array with one element in reserve (at the beginning). */
+
+static char **
+cd_arrdup(char **a)
+{
+    char **r = (char **) zalloc((arrlen(a) + 2) * sizeof(char *));
+    char **p = r + 1;
+
+    while (*a)
+        *p++ = ztrdup(*a++);
+    *p = NULL;
+
+    return r;
+}
+
 /* Get the next set. */
 
 static int
 cd_get(char **params)
 {
-    Cdset set;
+    Cdrun run;
 
-    if ((set = cd_state.sets)) {
-	char **sd, **sdp, **md, **mdp, **sh, **shp, **mh, **mhp;
-        char **ss, **ssp, **ms, **msp;
+    if ((run = cd_state.runs)) {
         Cdstr str;
-        VARARR(char, buf, cd_state.pre + cd_state.suf + cd_state.slen + 1);
-        char *sufp = NULL, *disp;
+        char **mats, **mp, **dpys, **dp, **opts, *csl = "";
 
-        if (cd_state.showd) {
-            memcpy(buf + cd_state.pre, cd_state.sep, cd_state.slen);
-            sufp = buf + cd_state.pre + cd_state.slen;
-        }
-	sd = (char **) zalloc((set->desc + 1) * sizeof(char *));
-	md = (char **) zalloc((set->desc + 1) * sizeof(char *));
-	sh = (char **) zalloc((set->desc + 1) * sizeof(char *));
-	mh = (char **) zalloc((set->desc + 1) * sizeof(char *));
-	ss = (char **) zalloc((set->count + 1) * sizeof(char *));
-	ms = (char **) zalloc((set->count + 1) * sizeof(char *));
-
-        for (sdp = sd, mdp = md, shp = sh, mhp = mh,
-             ssp = ss, msp = ms, str = set->strs;
-             str;
-             str = str->next) {
-            if (cd_state.showd && str->desc) {
-                if (strlen(str->str) > CD_MAXLEN)
-                    disp = tricat(str->str, cd_state.sep, str->desc);
-                else {
-                    strcpy(sufp, str->desc);
-                    memset(buf, ' ', cd_state.pre);
-                    memcpy(buf, str->str, strlen(str->str));
-                    disp = ztrdup(buf);
+        cd_state.runs = run->next;
+
+        switch (run->type) {
+        case CRT_SIMPLE:
+            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) {
+                *mp++ = ztrdup(str->match);
+                *dp++ = ztrdup(str->str ? str->str : str->match);
+            }
+            *mp = *dp = NULL;
+            opts = zarrdup(run->strs->set->opts);
+            if (cd_state.groups) {
+                /* We are building a columnised list with dummy matches
+                 * but there are also matches without descriptions.
+                 * Those end up in a different group, so make sure that
+                 * groupd doesn't have an explanation. */
+
+                for (mp = dp = opts; *mp; mp++) {
+                    if (dp[0][0] == '-' && dp[0][1] == 'X') {
+                        if (!dp[0][2] && dp[1])
+                            mp++;
+                    } else
+                        *dp++ = *mp;
                 }
-                if (strlen(disp) >= columns)
-                    disp[columns - 1] = '\0';
+                *dp = NULL;
+            }
+            break;
 
-                if (str->kind == 2) {
-                    *shp++ = disp;
-                    *mhp++ = ztrdup(str->match);
-                } else {
-                    *sdp++ = disp;
-                    *mdp++ = ztrdup(str->match);
+        case CRT_DESC:
+            {
+                VARARR(char, buf,
+                       cd_state.pre + cd_state.suf + cd_state.slen + 1);
+                char *sufp = NULL;
+
+                memcpy(buf + cd_state.pre, cd_state.sep, cd_state.slen);
+                sufp = buf + cd_state.pre + cd_state.slen;
+
+                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) {
+                    *mp++ = ztrdup(str->match);
+                    memset(buf, ' ', cd_state.pre);
+                    memcpy(buf, str->str, str->len);
+                    strcpy(sufp, str->desc);
+                    if (strlen(buf) >= columns)
+                        buf[columns] = '\0';
+                    *dp++ = ztrdup(buf);
                 }
-            } else {
-                *ssp++ = ztrdup(str->str);
-                *msp++ = ztrdup(str->match);
+                *mp = *dp = NULL;
+                opts = cd_arrdup(run->strs->set->opts);
+                opts[0] = ztrdup("-l");
+                break;
+            }
+        case CRT_SPEC:
+            mats = (char **) zalloc(2 * sizeof(char *));
+            dpys = (char **) zalloc(2 * sizeof(char *));
+            mats[0] = ztrdup(run->strs->match);
+            dpys[0] = ztrdup(run->strs->str);
+            mats[1] = dpys[1] = NULL;
+            opts = cd_arrdup(run->strs->set->opts);
+            for (dp = opts + 1; *dp; dp++)
+                if (dp[0][0] == '-' && dp[0][1] == 'J')
+                    break;
+            if (*dp) {
+                char *s = tricat("-1V", "", dp[0] + 2);
+
+                zsfree(*dp);
+                *dp = s;
+
+                memmove(opts, opts + 1,
+                        (arrlen(opts + 1) + 1) * sizeof(char *));
+                
+            } else
+                opts[0] = ztrdup("-1V-default-");
+            csl = "packed rows";
+            break;
+
+        default:
+            {
+                int dlen = columns - cd_state.gpre - cd_state.slen;
+                VARARR(char, dbuf, dlen + cd_state.slen);
+                char buf[20];
+                int i = run->type - CRT_DUMMY;
+
+                sprintf(buf, "-E%d", i + 1);
+
+                mats = (char **) zalloc(2 * sizeof(char *));
+                dpys = (char **) zalloc((3 + i) * sizeof(char *));
+                mats[0] = ztrdup(run->strs->match);
+                dpys[0] = ztrdup(run->strs->str);
+                for (dp = dpys + 1; i; i--, dp++)
+                    *dp = ztrdup("");
+                memset(dbuf + cd_state.slen, ' ', dlen - 1);
+                dbuf[dlen + cd_state.slen - 1] = '\0';
+                strcpy(dbuf, cd_state.sep);
+                memcpy(dbuf + cd_state.slen,
+                       run->strs->desc,
+                       (strlen(run->strs->desc) >= dlen ? dlen - 1 :
+                        strlen(run->strs->desc)));
+                *dp++ = ztrdup(dbuf);
+                mats[1] = *dp = NULL;
+
+                opts = cd_arrdup(run->strs->set->opts);
+                opts[0] = ztrdup(buf);
+
+                csl = "packed rows";
             }
         }
-        *sdp = *mdp = *shp = *mhp = *ssp = *msp = NULL;
-
-	setaparam(params[0], zarrdup(set->opts));
-	setaparam(params[1], sd);
-	setaparam(params[2], md);
-	setaparam(params[3], sh);
-	setaparam(params[4], mh);
-	setaparam(params[5], ss);
-	setaparam(params[6], ms);
+        setsparam(params[0], ztrdup(csl));
+        setaparam(params[1], opts);
+        setaparam(params[2], mats);
+        setaparam(params[3], dpys);
 
-	cd_state.sets = set->next;
-	set->next = NULL;
-	freecdsets(set);
+        zfree(run, sizeof(*run));
 
-	return 0;
+        return 0;
     }
     return 1;
 }
@@ -341,6 +596,8 @@ cd_get(char **params)
 static int
 bin_compdescribe(char *nam, char **args, char *ops, int func)
 {
+    int n = arrlen(args);
+
     if (incompfunc != 1) {
 	zwarnnam(nam, "can only be called from completion function", NULL, 0);
 	return 1;
@@ -351,15 +608,30 @@ bin_compdescribe(char *nam, char **args, char *ops, int func)
     }
     switch (args[0][1]) {
     case 'i':
-	return cd_init(nam, "", args + 1, 0);
+        if (n < 2) {
+            zwarnnam(nam, "not enough arguments", NULL, 0);
+
+            return 1;
+        }
+	return cd_init(nam, args[1], "", NULL, args + 2, 0);
     case 'I':
-	return cd_init(nam, args[1], args + 2, 1);
+        if (n < 5) {
+            zwarnnam(nam, "not enough arguments", NULL, 0);
+
+            return 1;
+        } else {
+            char **opts;
+
+            if (!(opts = getaparam(args[3]))) {
+		zwarnnam(nam, "unknown parameter: %s", args[2], 0);
+		return 1;
+            }
+            return cd_init(nam, args[1], args[2], opts, args + 4, 1);
+        }
     case 'g':
 	if (cd_parsed) {
-	    int n = arrlen(args);
-
-	    if (n != 8) {
-		zwarnnam(nam, (n < 8 ? "not enough arguments" :
+	    if (n != 5) {
+		zwarnnam(nam, (n < 5 ? "not enough arguments" :
 			      "too many arguments"), NULL, 0);
 		return 1;
 	    }