From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 7260 invoked from network); 25 Jul 2001 08:50:36 -0000 Received: from sunsite.dk (130.225.51.30) by ns1.primenet.com.au with SMTP; 25 Jul 2001 08:50:36 -0000 Received: (qmail 5416 invoked by alias); 25 Jul 2001 08:50:29 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 15477 Received: (qmail 5405 invoked from network); 25 Jul 2001 08:50:28 -0000 From: Sven Wischnowsky Date: Wed, 25 Jul 2001 10:49:59 +0200 (MET DST) Message-Id: <200107250849.KAA17837@beta.informatik.hu-berlin.de> To: zsh-workers@sunsite.dk Subject: Re: About the new long/short options changes In-Reply-To: <1010720092626.ZM6384@candle.brasslantern.com> Bart Schaefer wrote: > ... > > } I have some ideas how we could achive this, part of the stuff we need is > } already there. Think of some combination of list-packed, list-rows-first > } and `dummy' matches that get slots in the listing but show nothing and > } can't be selected. > > Yes, that is what I was thinking, though rather than dummy matches I > was thinking about chopping up the display into groups with different > columnations, as already happens when matches are explicitly added in > different groups. I first thought about that, too, but it would require some serious changes to the listing code, so I decided to first try it with only those dummy matches I mentioned. The result is the patch below, which is already quite satisfying, I think. It took a bit longer and the patch is a bit larger, because of the needed re-implementation of the guts of compdescribe. And because I wasn't too happy with the amount of screen space wasted due to LIST_TYPES. So I dug into the listing code to make it add that extra space only for groups that contain at least one file [*] and I also made it try a bit harder to find an optimal columnation for LIST_PACKED. If you try it, you'll see that this now behaves slightly different when completion options with and without groups in menu selection. In the first case, only the matches are highlighted, in the latter case the whole lines are highlighted. I'm torn in two here, because I like it that the description is highlighted, too if there is a one-to-one correspondence between matches and descriptions. If I get complaints, I can change it quite easily, though. But then I'd prefer to find a solution that makes that more easily usable from shell code (and I'm not happy that the code that uses such line-oriented dummy matches currently has to take care of things like the screen width itself), so that we could use it for, e.g., process listings and the like, too. I was thinking about allowing display strings to be defined as `expandable', meaning that we would like to see as much of the strings as the screen width permits, but that they may be shortened to make them fit on the current line of the completion listing. But that, too, would require some serious changes to the listing code. Hm. Bye Sven [*] This does not change it to not add a space to normal files -- the thing we discussed some time ago, when comparing it to what GNU ls does. Index: Completion/Base/Utility/_describe =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Base/Utility/_describe,v retrieving revision 1.3 diff -u -r1.3 _describe --- Completion/Base/Utility/_describe 2001/07/19 08:40:37 1.3 +++ Completion/Base/Utility/_describe 2001/07/25 08:36:54 @@ -2,8 +2,9 @@ # This can be used to add options or values with descriptions as matches. -local _opt _expl _tmps _tmpd _tmph _tmpmd _tmpms _tmpmh -local _type=values _descr _ret=1 _showd _nm _hide _args _grp +local _opt _expl _tmpm _tmpd +local _type=values _descr _ret=1 _showd _nm _hide _args _grp _sep +local csl="$compstate[list]" csl2 # Get the option. @@ -27,6 +28,7 @@ _argv=( "$@" ) _grp=(-g) + _sep='-- ' _new=( "$1" ) shift @@ -66,43 +68,33 @@ set - "$_argv[@]" else _grp=() + _sep=' -- ' fi _descr="$1" shift [[ "$_type" = options ]] && - zstyle -t ":completion:${curcontext}:options" prefix-hidden && _hide=yes + zstyle -t ":completion:${curcontext}:options" prefix-hidden && + _hide="${(M)PREFIX##(--|[-+])}" _tags "$_type" while _tags; do while _next_label "$_type" _expl "$_descr"; do if [[ -n "$_showd" ]]; then - compdescribe -I ' -- ' "$_grp[@]" "$@" + compdescribe -I "$_hide" "$_sep" _expl "$_grp[@]" "$@" else - compdescribe -i "$@" + compdescribe -i "$_hide" "$@" fi - while compdescribe -g _args _tmpd _tmpmd _tmph _tmpmh _tmps _tmpms; do + compstate[list]="$csl" - # See if we should remove the option prefix characters. + while compdescribe -g csl2 _args _tmpm _tmpd; do - if [[ -n "$_hide" ]]; then - if [[ "$PREFIX" = --* ]]; then - _tmpd=( "${(@)_tmpd#--}" ) - _tmph=( "${(@)_tmph#--}" ) - _tmps=( "${(@)_tmps#--}" ) - elif [[ "$PREFIX" = [-+]* ]]; then - _tmpd=( "${(@)_tmpd#[-+]}" ) - _tmph=( "${(@)_tmph#[-+]}" ) - _tmps=( "${(@)_tmps#[-+]}" ) - fi - fi - - compadd "$_args[@]" "$_expl[@]" -ld _tmpd -a _tmpmd && _ret=0 - compadd -n "$_args[@]" "$_expl[@]" -ld _tmph -a _tmpmh && _ret=0 - compadd "$_args[@]" "$_expl[@]" -d _tmps -a _tmpms && _ret=0 + compstate[list]="$csl $csl2" + + compadd "$_args[@]" -d _tmpd -a _tmpm && _ret=0 done done (( _ret )) || return 0 Index: Doc/Zsh/compwid.yo =================================================================== RCS file: /cvsroot/zsh/zsh/Doc/Zsh/compwid.yo,v retrieving revision 1.30 diff -u -r1.30 compwid.yo --- Doc/Zsh/compwid.yo 2001/04/01 16:19:17 1.30 +++ Doc/Zsh/compwid.yo 2001/07/25 08:36:57 @@ -441,6 +441,7 @@ xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ] [ tt(-x) var(message) ]) xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ]) xitem([ tt(-D) var(array) ] [ tt(-O) var(array) ] [ tt(-A) var(array) ]) +xitem([ tt(-E) var(number) ]) item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])( This builtin command can be used to add matches directly and control @@ -663,6 +664,17 @@ match. If no string is given, it will be shown as a string containing the strings that would be inserted for the other matches, truncated to the width of the screen. +) +item(tt(-E))( +This option adds var(number) empty matches after the var(words) have +been added. An empty match takes up space in completion listings but +will never be inserted in the line and can't be selected with menu +completion or menu selection. This makes empty matches only useful to +format completion lists and to make explanatory string be shown in +completion lists (since empty matches can be given display strings +with the tt(-d) option). And because all but one empty string would +otherwise be removed, this option implies the tt(-V) and tt(-1) +options (even if an explicit tt(-J) option is given). ) xitem(tt(-)) item(tt(-)tt(-))( Index: Src/Zle/comp.h =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/comp.h,v retrieving revision 1.10 diff -u -r1.10 comp.h --- Src/Zle/comp.h 2001/01/11 10:06:50 1.10 +++ Src/Zle/comp.h 2001/07/25 08:36:58 @@ -85,6 +85,7 @@ #define CGF_UNIQCON 16 /* remove consecutive duplicates */ #define CGF_PACKED 32 /* LIST_PACKED for this group */ #define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */ +#define CGF_FILES 128 /* contains file names */ /* This is the struct used to hold matches. */ @@ -125,6 +126,7 @@ #define CMF_MULT (1<<11) /* string appears more than once */ #define CMF_FMULT (1<<12) /* first of multiple equal strings */ #define CMF_ALL (1<<13) /* a match representing all other matches */ +#define CMF_DUMMY (1<<14) /* unselectable dummy match */ /* Stuff for completion matcher control. */ @@ -264,6 +266,7 @@ char *dpar; /* array to delete non-matches in (-D) */ char *disp; /* array with display lists (-d) */ char *mesg; /* message to show unconditionally (-x) */ + int dummies; /* add that many dummy matches */ }; /* List data. */ Index: Src/Zle/compcore.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/compcore.c,v retrieving revision 1.46 diff -u -r1.46 compcore.c --- Src/Zle/compcore.c 2001/07/10 09:25:43 1.46 +++ Src/Zle/compcore.c 2001/07/25 08:37:12 @@ -1558,6 +1558,40 @@ return ret; } +static void +addmatch(char *str, int flags, char ***dispp, int line) +{ + Cmatch cm = (Cmatch) zhalloc(sizeof(struct cmatch)); + char **disp = *dispp; + + memset(cm, 0, sizeof(struct cmatch)); + cm->str = dupstring(str); + cm->flags = (flags | + (complist ? + ((strstr(complist, "packed") ? CMF_PACKED : 0) | + (strstr(complist, "rows") ? CMF_ROWS : 0)) : 0)); + if (disp) { + if (!*++disp) + disp = NULL; + if (disp) + cm->disp = dupstring(*disp); + } else if (line) { + cm->disp = dupstring(""); + cm->flags |= CMF_DISPLINE; + } + mnum++; + ainfo->count++; + if (curexpl) + curexpl->count++; + + addlinknode(matches, cm); + + newmatches = 1; + mgroup->new = 1; + + *dispp = disp; +} + /* This is used by compadd to add a couple of matches. The arguments are * the strings given via options. The last argument is the array with * the matches. */ @@ -1583,7 +1617,7 @@ Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl; Heap oldheap; - if (!*argv && !(dat->aflags & CAF_ALL)) { + if (!*argv && !dat->dummies && !(dat->aflags & CAF_ALL)) { SWITCHHEAPS(oldheap, compheap) { /* Select the group in which to store the matches. */ gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | @@ -1602,6 +1636,8 @@ return 1; } + if (dat->dummies) + dat->aflags = dat->aflags | CAF_NOSORT | CAF_UNIQALL; for (bp = brbeg; bp; bp = bp->next) bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos); for (bp = brend; bp; bp = bp->next) @@ -2022,35 +2058,12 @@ if (dat->exp) addexpl(); if (!hasallmatch && (dat->aflags & CAF_ALL)) { - Cmatch cm = (Cmatch) zhalloc(sizeof(struct cmatch)); - - memset(cm, 0, sizeof(struct cmatch)); - cm->str = dupstring(""); - cm->flags = (dat->flags | CMF_ALL | - (complist ? - ((strstr(complist, "packed") ? CMF_PACKED : 0) | - (strstr(complist, "rows") ? CMF_ROWS : 0)) : 0)); - if (disp) { - if (!*++disp) - disp = NULL; - if (disp) - cm->disp = dupstring(*disp); - } else { - cm->disp = dupstring(""); - cm->flags |= CMF_DISPLINE; - } - mnum++; - ainfo->count++; - if (curexpl) - curexpl->count++; - - addlinknode(matches, cm); - - newmatches = 1; - mgroup->new = 1; - + addmatch("", dat->flags | CMF_ALL, &disp, 1); hasallmatch = 1; } + while (dat->dummies--) + addmatch("", dat->flags | CMF_DUMMY, &disp, 0); + } SWITCHBACKHEAPS(oldheap); /* We switched back to the current heap, now restore the stack of Index: Src/Zle/complete.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/complete.c,v retrieving revision 1.16 diff -u -r1.16 complete.c --- Src/Zle/complete.c 2001/05/31 09:44:00 1.16 +++ Src/Zle/complete.c 2001/07/25 08:37:16 @@ -435,6 +435,7 @@ dat.match = NULL; dat.flags = 0; dat.aflags = CAF_MATCH; + dat.dummies = 0; for (; *argv && **argv == '-'; argv++) { if (!(*argv)[1]) { @@ -564,6 +565,23 @@ break; case 'l': dat.flags |= CMF_DISPLINE; + break; + case 'E': + if (p[1]) { + dat.dummies = atoi(p + 1); + p = "" - 1; + } else if (argv[1]) { + argv++; + dat.dummies = atoi(*argv); + p = "" - 1; + } else { + zwarnnam(name, "number expected after -%c", NULL, *p); + return 1; + } + if (dat.dummies < 0) { + zwarnnam(name, "invalid number: %d", NULL, dat.dummies); + return 1; + } break; case '-': argv++; Index: Src/Zle/complist.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/complist.c,v retrieving revision 1.43 diff -u -r1.43 complist.c --- Src/Zle/complist.c 2001/07/18 13:10:10 1.43 +++ Src/Zle/complist.c 2001/07/25 08:37:22 @@ -1394,7 +1394,7 @@ mlastm = m->gnum; if (m->disp && (m->flags & CMF_DISPLINE)) { - if (mselect >= 0) { + if (mselect >= 0 && !(m->flags & CMF_DUMMY)) { int mm = (mcols * ml), i; for (i = mcols; i--; ) { @@ -1441,7 +1441,7 @@ } else mx = mc * g->width; - if (mselect >= 0) { + if (mselect >= 0 && !(m->flags & CMF_DUMMY)) { int mm = mcols * ml, i; for (i = (width ? width : mcols); i--; ) { @@ -1482,7 +1482,7 @@ len = niceztrlen(m->disp ? m->disp : m->str); mlprinted = len / columns; - if (isset(LISTTYPES) && buf) { + if ((g->flags & CGF_FILES) && buf) { if (m->gnum != mselect) { zcoff(); zcputs(&mcolors, g->name, COL_TC); @@ -1684,6 +1684,7 @@ noselect = 1; while ((menuacc && !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || + ((*minfo.cur)->flags & CMF_DUMMY) || (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && (!(*minfo.cur)->str || !*(*minfo.cur)->str))) do_menucmp(0); Index: Src/Zle/compresult.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/compresult.c,v retrieving revision 1.38 diff -u -r1.38 compresult.c --- Src/Zle/compresult.c 2001/07/18 13:10:10 1.38 +++ Src/Zle/compresult.c 2001/07/25 08:37:30 @@ -1160,6 +1160,7 @@ } } while ((menuacc && !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || + ((*minfo.cur)->flags & CMF_DUMMY) || (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && (!(*minfo.cur)->str || !*(*minfo.cur)->str))); /* ... and insert it into the command line. */ @@ -1183,6 +1184,7 @@ minfo.cur--; } while ((menuacc && !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || + ((*minfo.cur)->flags & CMF_DUMMY) || (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && (!(*minfo.cur)->str || !*(*minfo.cur)->str))); metafy_line(); @@ -1378,7 +1380,7 @@ Cmgroup g; Cmatch *p, m; Cexpl *e; - int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES); + int hidden = 0, nlist = 0, nlines = 0, add; int max = 0, i; VARARR(int, mlens, nmatches + 1); @@ -1392,6 +1394,7 @@ for (g = amatches; g; g = g->next) { char **pp = g->ylist; int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0; + int hasf = 0; g->flags |= CGF_PACKED | CGF_ROWS; @@ -1437,6 +1440,8 @@ } } else if (!onlyexpl) { for (p = g->matches; (m = *p); p++) { + if (m->flags & CMF_FILE) + hasf = 1; if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) { m->flags |= CMF_HIDE; continue; @@ -1496,6 +1501,11 @@ e++; } } + if (isset(LISTTYPES) && hasf) { + g->flags |= CGF_FILES; + add = 3; + } else + add = 2; g->totl = totl + (ndisp * add); g->dcount = ndisp; g->width = glong + add; @@ -1513,6 +1523,7 @@ int *ws, tlines, tline, tcols, maxlen, nth, width, glines; for (g = amatches; g; g = g->next) { + add = 2 + !!(g->flags & CGF_FILES); glines = 0; zfree(g->widths, 0); @@ -1523,7 +1534,8 @@ if (g->cols) { glines += (arrlen(pp) + g->cols - 1) / g->cols; if (g->cols > 1) - g->width += (max - (g->width * g->cols - add)) / g->cols; + g->width += ((max - (g->width * g->cols - add)) / + g->cols); } else { g->cols = 1; g->width = 1; @@ -1559,6 +1571,8 @@ if (!(g->flags & CGF_PACKED)) continue; + add = 2 + !!(g->flags & CGF_FILES); + ws = g->widths = (int *) zalloc(columns * sizeof(int)); memset(ws, 0, columns * sizeof(int)); tlines = g->lins; @@ -1666,7 +1680,7 @@ } else if (g->width) { if (g->flags & CGF_ROWS) { int addlen, count, tcol, maxlines = 0, llines, i; - int beg = columns / g->shortest, end = g->cols; + int beg = columns / g->shortest, end = g->cols, fe = 1; Cmatch *first; while (1) { @@ -1677,7 +1691,8 @@ count = g->dcount; count > 0; count--) { m = *p; - addlen = mlens[m->gnum] + add; + addlen = (mlens[m->gnum] + + (tcol == tcols - 1 ? 0 : add)); if (addlen > maxlen) maxlen = addlen; for (i = tcols; i && *p; i--) @@ -1706,15 +1721,21 @@ break; if (beg == end) { - beg--; - end--; + if (fe) { + beg += 2; + end += 2; + fe = 0; + } else { + beg--; + end--; + } } else if (width < columns) { if ((end = tcols) == beg - 1) end++; } else { if ((beg = tcols) - 1 == end) end++; - } + } } if (tcols > g->cols) tlines = maxlines; @@ -1723,7 +1744,7 @@ int smask = ((showall ? 0 : (CMF_NOLIST | CMF_MULT)) | CMF_HIDE); int beg = ((g->totl + columns) / columns); - int end = g->lins; + int end = g->lins, fe = 1; while (1) { tlines = (beg + end) >> 1; @@ -1755,8 +1776,14 @@ break; if (beg == end) { - beg++; - end++; + if (fe) { + beg -= 2; + end -= 2; + fe = 0; + } else { + beg++; + end++; + } } else if (width < columns) { if ((end = tlines) == beg + 1) end--; @@ -1783,6 +1810,7 @@ } } for (g = amatches; g; g = g->next) { + add = 2 + !!(g->flags & CGF_FILES); if (g->widths) { int *p, a = (max - g->totl + add) / g->cols; @@ -2152,7 +2180,7 @@ nicezputs(m->str, shout); len = niceztrlen(m->str); - if (isset(LISTTYPES) && buf) { + if ((g->flags & CGF_FILES) && buf) { putc(file_type(buf->st_mode), shout); len++; } Index: Src/Zle/computil.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Zle/computil.c,v retrieving revision 1.66 diff -u -r1.66 computil.c --- Src/Zle/computil.c 2001/07/20 10:54:38 1.66 +++ Src/Zle/computil.c 2001/07/25 08:37:34 @@ -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 @@ 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 @@ 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 @@ 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 @@ { Cdset n; Cdstr s, sn; + Cdrun r, rn; for (; p; p = n) { n = p->next; @@ -88,6 +105,10 @@ 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 @@ { 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 @@ 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; + if (num > 1) + cd_state.groups++; + else + cd_state.descs++; - buf = str1->str; - for (str2 = str1->other; str2; str2 = str2->other) - buf = zhtricat(buf, ", ", str2->str); - - 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; } } } @@ -170,15 +176,173 @@ cd_state.suf = l; } } + } +} + +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; + } } - if (cd_state.pre > CD_MAXLEN) - cd_state.pre = CD_MAXLEN; + *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_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 @@ str->kind = 0; str->other = NULL; + str->set = set; for (tmp = *ap; *tmp && *tmp != ':'; tmp++) if (*tmp == '\\' && tmp[1]) @@ -230,6 +396,7 @@ 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 @@ 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_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; + + 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; - if (str->kind == 2) { - *shp++ = disp; - *mhp++ = ztrdup(str->match); - } else { - *sdp++ = disp; - *mdp++ = ztrdup(str->match); + 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); - } - } - *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); - - cd_state.sets = set->next; - set->next = NULL; - freecdsets(set); + *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"; + } + } + setsparam(params[0], ztrdup(csl)); + setaparam(params[1], opts); + setaparam(params[2], mats); + setaparam(params[3], dpys); - return 0; + zfree(run, sizeof(*run)); + + return 0; } return 1; } @@ -341,6 +596,8 @@ 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 @@ } 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; } -- Sven Wischnowsky wischnow@informatik.hu-berlin.de