26763: fix problem on failed cd -s to relative path
[zsh.git] / Src / Zle / complist.c
blob1b6dd083a9abbdc6ef0c6aa72d0879a2ee46a2a2
1 /*
2 * complist.c - completion listing enhancements
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1999 Sven Wischnowsky
7 * All rights reserved.
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
15 * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Sven Wischnowsky and the Zsh Development Group have been advised of
19 * the possibility of such damage.
21 * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose. The software
24 * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
30 #include "complist.mdh"
31 #include "complist.pro"
34 /* Information about the list shown. */
37 * noselect: 1 if complistmatches indicated we shouldn't do selection.
38 * Tested in domenuselect.
39 * mselect: Local copy of the index of the currently selected match.
40 * Initialised to the gnum entry of the current match for
41 * each completion.
42 * inselect: 1 if we already selecting matches; tested in complistmatches()
43 * mcol: The column for the selected completion. As we never scroll
44 * horizontally this applies both the screen and the logical array.
45 * mline: The line for the selected completion in the logical array of
46 * all matches, not all of which may be on screen at once.
47 * mcols: Local copy of columns used in sizing arrays.
48 * mlines: The number of lines in the logical array of all matches,
49 * initialised from listdat.nlines.
51 static int noselect, mselect, inselect, mcol, mline, mcols, mlines;
53 * selected: Used to signal between domenucomplete() and menuselect()
54 * that a selected entry has been found. Or something.
55 * mlbeg: The first line of the logical array of all matches that
56 * fits on screen. Setting this to -1 forces a redraw.
57 * mlend: The line after the last that fits on screen.
58 * mscroll: 1 if the scrolling prompt is shown on screen.
59 * mrestlines: The number of screen lines remaining to be processed.
61 static int selected, mlbeg = -1, mlend = 9999999, mscroll, mrestlines;
63 * mnew: 1 if a new selection menu is being displayed.
64 * mlastcols: stored value of mcols for use in calculating mnew.
65 * mlastlines: stored value of mlines for use in calculating mnew.
66 * mhasstat: Indicates if the status line is present (but maybe not
67 * yet printed).
68 * mfirstl: The first line of the logical array of all matches to
69 * be shown on screen, -1 if this has not yet been determined.
70 * mlastm: The index of the selected match in some circumstances; used
71 * if an explicit number for a match is passed to compprintfmt();
72 * initialised from the total number of matches. I realise this
73 * isn't very illuminating.
75 static int mnew, mlastcols, mlastlines, mhasstat, mfirstl, mlastm;
77 * mlprinted: Used to signal the number of additional lines printed
78 * when outputting matches (as argument passing is a bit
79 * screwy within the completion system).
80 * molbeg: The last value of mlbeg; -1 if invalid, -42 if, er, very
81 * invalid. Used in calculations of how much to draw.
82 * mocol: The last value of mcol.
83 * moline: The last value of mline.
84 * mstatprinted: Indicates that the status line has now been printed,
85 * c.f. mhasstat.
87 static int mlprinted, molbeg = -2, mocol = 0, moline = 0, mstatprinted;
89 * mstatus: The message printed when scrolling.
90 * mlistp: The message printed when merely listing.
92 static char *mstatus, *mlistp;
94 * mtab is the logical array of all matches referred to above. It
95 * contains mcols*mlines entries. These entries contain a pointer to
96 * the match structure which is in use at a particular point. Note
97 * that for multiple line entries lines after the first contain NULL.
99 * mmtabp is a pointer to the selected entry in mtab.
101 static Cmatch **mtab, **mmtabp;
103 * Used to indicate that the list has changed and needs redisplaying.
105 static int mtab_been_reallocated;
107 * Array and pointer for the match group in exactly the same layout
108 * as mtab and mmtabp.
110 static Cmgroup *mgtab, *mgtabp;
111 #ifdef DEBUG
113 * Allow us to keep track of pointer arithmetic for mgtab; could
114 * just as well have been for mtab but wasn't.
116 int mgtabsize;
117 #endif
120 * Used in mtab/mgtab, for explanations.
122 * UUUUUUUUUUUUUUURRRRGHHHHHHHHHH!!!!!!!!! --- pws
125 #define MMARK ((unsigned long) 1)
126 #define mmarked(v) (((unsigned long) (v)) & MMARK)
127 #define mtmark(v) ((Cmatch *) (((unsigned long) (v)) | MMARK))
128 #define mtunmark(v) ((Cmatch *) (((unsigned long) (v)) & ~MMARK))
129 #define mgmark(v) ((Cmgroup) (((unsigned long) (v)) | MMARK))
130 #define mgunmark(v) ((Cmgroup) (((unsigned long) (v)) & ~MMARK))
132 /* Information for in-string colours. */
134 /* Maximum number of in-string colours supported. */
136 #define MAX_POS 11
138 static int nrefs;
139 static int begpos[MAX_POS], curisbeg;
140 static int endpos[MAX_POS];
141 static int sendpos[MAX_POS], curissend; /* sorted end positions */
142 static char **patcols, *curiscols[MAX_POS];
143 static int curiscol;
145 /* The last color used. */
147 static char *last_cap;
150 /* We use the parameters ZLS_COLORS and ZLS_COLOURS in the same way as
151 * the color ls does. It's just that we don't support the `or' file
152 * type. */
156 * menu-select widget: used to test if it's already loaded.
158 static Widget w_menuselect;
160 * Similarly for the menuselect and listscroll keymaps.
162 static Keymap mskeymap, lskeymap;
164 /* Indixes into the terminal string arrays. */
166 #define COL_NO 0
167 #define COL_FI 1
168 #define COL_DI 2
169 #define COL_LN 3
170 #define COL_PI 4
171 #define COL_SO 5
172 #define COL_BD 6
173 #define COL_CD 7
174 #define COL_OR 8
175 #define COL_MI 9
176 #define COL_SU 10
177 #define COL_SG 11
178 #define COL_TW 12
179 #define COL_OW 13
180 #define COL_ST 14
181 #define COL_EX 15
182 #define COL_LC 16
183 #define COL_RC 17
184 #define COL_EC 18
185 #define COL_TC 19
186 #define COL_SP 20
187 #define COL_MA 21
188 #define COL_HI 22
189 #define COL_DU 23
191 #define NUM_COLS 24
193 /* Names of the terminal strings. */
195 static char *colnames[] = {
196 "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "or", "mi",
197 "su", "sg", "tw", "ow", "st", "ex",
198 "lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", NULL
201 /* Default values. */
203 static char *defcols[] = {
204 "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", NULL, NULL,
205 "37;41", "30;43", "30;42", "34;42", "37;44", "1;32",
206 "\033[", "m", NULL, "0", "0", "7", NULL, NULL
209 /* This describes a terminal string for a file type. */
211 typedef struct filecol *Filecol;
213 struct filecol {
214 Patprog prog; /* group pattern */
215 char *col; /* color string */
216 Filecol next; /* next one */
219 /* This describes a terminal string for a pattern. */
221 typedef struct patcol *Patcol;
223 struct patcol {
224 Patprog prog;
225 Patprog pat; /* pattern for match */
226 char *cols[MAX_POS + 1];
227 Patcol next;
230 /* This describes a terminal string for a filename extension. */
232 typedef struct extcol *Extcol;
234 struct extcol {
235 Patprog prog; /* group pattern or NULL */
236 char *ext; /* the extension */
237 char *col; /* the terminal color string */
238 Extcol next; /* the next one in the list */
241 /* This holds all terminal strings. */
243 typedef struct listcols *Listcols;
245 /* values for listcol flags */
246 enum {
247 /* ln=target: follow symlinks to determine highlighting */
248 LC_FOLLOW_SYMLINKS = 0x0001
251 struct listcols {
252 Filecol files[NUM_COLS]; /* strings for file types */
253 Patcol pats; /* strings for patterns */
254 Extcol exts; /* strings for extensions */
255 int flags; /* special settings, see above */
259 * Contains information about the colours to be used for entries.
260 * Sometimes mcolors is passed as an argument even though it's
261 * available to all the functions.
263 static struct listcols mcolors;
265 /* Combined length of LC and RC, maximum length of capability strings. */
267 static int lr_caplen, max_caplen;
269 /* This parses the value of a definition (the part after the `=').
270 * The return value is a pointer to the character after it. */
272 static char *
273 getcolval(char *s, int multi)
275 char *p, *o = s;
277 for (p = s; *s && *s != ':' && (!multi || *s != '='); p++, s++) {
278 if (*s == '\\' && s[1]) {
279 switch (*++s) {
280 case 'a': *p = '\007'; break;
281 case 'n': *p = '\n'; break;
282 case 'b': *p = '\b'; break;
283 case 't': *p = '\t'; break;
284 case 'v': *p = '\v'; break;
285 case 'f': *p = '\f'; break;
286 case 'r': *p = '\r'; break;
287 case 'e': *p = '\033'; break;
288 case '_': *p = ' '; break;
289 case '?': *p = '\177'; break;
290 default:
291 if (*s >= '0' && *s <= '7') {
292 int i = STOUC(*s);
294 if (*++s >= '0' && *s <= '7') {
295 i = (i * 8) + STOUC(*s);
296 if (*++s >= '0' && *s <= '7')
297 i = (i * 8) + STOUC(*s);
299 *p = (char) i;
300 } else
301 *p = *s;
303 } else if (*s == '^') {
304 if ((s[1] >= '@' && s[1] <= '_') ||
305 (s[1] >= 'a' && s[1] <= 'z'))
306 *p = (char) (STOUC(*s) & ~0x60);
307 else if (s[1] == '?')
308 *p = '\177';
309 else {
310 *p++ = *s;
311 *p = s[1];
313 s++;
314 } else
315 *p = *s;
317 if (p != s)
318 *p = '\0';
319 if ((s - o) > max_caplen)
320 max_caplen = s - o;
321 return s;
324 /* This parses one definition. Return value is a pointer to the
325 * character after it. */
327 static char *
328 getcoldef(char *s)
330 Patprog gprog = NULL;
332 if (*s == '(') {
333 char *p;
334 int l = 0;
336 for (p = s + 1, l = 0; *p && (*p != ')' || l); p++)
337 if (*p == '\\' && p[1])
338 p++;
339 else if (*p == '(')
340 l++;
341 else if (*p == ')')
342 l--;
344 if (*p == ')') {
345 char sav = p[1];
347 p[1] = '\0';
348 tokenize(s);
349 gprog = patcompile(s, 0, NULL);
350 p[1] =sav;
352 s = p + 1;
355 if (*s == '*') {
356 Extcol ec, eo;
357 char *n, *p;
359 /* This is for an extension. */
361 n = ++s;
362 while (*s && *s != '=')
363 s++;
364 if (!*s)
365 return s;
366 *s++ = '\0';
367 p = getcolval(s, 0);
368 ec = (Extcol) zhalloc(sizeof(*ec));
369 ec->prog = gprog;
370 ec->ext = n;
371 ec->col = s;
372 ec->next = NULL;
373 if ((eo = mcolors.exts)) {
374 while (eo->next)
375 eo = eo->next;
376 eo->next = ec;
377 } else
378 mcolors.exts = ec;
379 if (*p)
380 *p++ = '\0';
381 return p;
382 } else if (*s == '=') {
383 char *p = ++s, *t, *cols[MAX_POS];
384 int ncols = 0;
385 Patprog prog;
387 /* This is for a pattern. */
389 while (*s && *s != '=')
390 s++;
391 if (!*s)
392 return s;
393 *s++ = '\0';
394 while (1) {
395 t = getcolval(s, 1);
396 if (ncols < MAX_POS)
397 cols[ncols++] = s;
398 s = t;
399 if (*s != '=')
400 break;
401 *s++ = '\0';
403 tokenize(p);
404 if ((prog = patcompile(p, 0, NULL))) {
405 Patcol pc, po;
406 int i;
408 pc = (Patcol) zhalloc(sizeof(*pc));
409 pc->prog = gprog;
410 pc->pat = prog;
411 for (i = 0; i < ncols; i++)
412 pc->cols[i] = cols[i];
413 pc->cols[i] = NULL;
414 pc->next = NULL;
415 if ((po = mcolors.pats)) {
416 while (po->next)
417 po = po->next;
418 po->next = pc;
419 } else
420 mcolors.pats = pc;
422 if (*t)
423 *t++ = '\0';
424 return t;
425 } else {
426 char *n = s, *p, **nn;
427 int i;
429 /* This is for a file type. */
431 while (*s && *s != '=')
432 s++;
433 if (!*s)
434 return s;
435 *s++ = '\0';
436 for (i = 0, nn = colnames; *nn; i++, nn++)
437 if (!strcmp(n, *nn))
438 break;
440 * special case: highlighting link targets
442 if (i == COL_LN && strpfx("target", s) &&
443 (s[6] == ':' || !s[6])) {
444 mcolors.flags |= LC_FOLLOW_SYMLINKS;
445 p = s + 6;
446 } else {
447 p = getcolval(s, 0);
448 if (*nn) {
449 Filecol fc, fo;
451 fc = (Filecol) zhalloc(sizeof(*fc));
452 fc->prog = (i == COL_EC || i == COL_LC || i == COL_RC ?
453 NULL : gprog);
454 fc->col = s;
455 fc->next = NULL;
456 if ((fo = mcolors.files[i])) {
457 while (fo->next)
458 fo = fo->next;
459 fo->next = fc;
460 } else
461 mcolors.files[i] = fc;
463 if (*p)
464 *p++ = '\0';
466 return p;
470 static Filecol
471 filecol(char *col)
473 Filecol fc;
475 fc = (Filecol) zhalloc(sizeof(*fc));
476 fc->prog = NULL;
477 fc->col = col;
478 fc->next = NULL;
480 return fc;
484 * This initializes the given terminal color structure.
487 static void
488 getcols()
490 char *s;
491 int i, l;
493 max_caplen = lr_caplen = 0;
494 mcolors.flags = 0;
495 queue_signals();
496 if (!(s = getsparam("ZLS_COLORS")) &&
497 !(s = getsparam("ZLS_COLOURS"))) {
498 for (i = 0; i < NUM_COLS; i++)
499 mcolors.files[i] = filecol("");
500 mcolors.pats = NULL;
501 mcolors.exts = NULL;
503 if ((s = tcstr[TCSTANDOUTBEG]) && s[0]) {
504 mcolors.files[COL_MA] = filecol(s);
505 mcolors.files[COL_EC] = filecol(tcstr[TCSTANDOUTEND]);
506 } else
507 mcolors.files[COL_MA] = filecol(defcols[COL_MA]);
508 lr_caplen = 0;
509 if ((max_caplen = strlen(mcolors.files[COL_MA]->col)) <
510 (l = strlen(mcolors.files[COL_EC]->col)))
511 max_caplen = l;
512 unqueue_signals();
513 return;
515 /* Reset the global color structure. */
516 memset(&mcolors, 0, sizeof(mcolors));
517 s = dupstring(s);
518 while (*s)
519 if (*s == ':')
520 s++;
521 else
522 s = getcoldef(s);
523 unqueue_signals();
525 /* Use default values for those that aren't set explicitly. */
526 for (i = 0; i < NUM_COLS; i++) {
527 if (!mcolors.files[i] || !mcolors.files[i]->col)
528 mcolors.files[i] = filecol(defcols[i]);
529 if (mcolors.files[i] && mcolors.files[i]->col &&
530 (l = strlen(mcolors.files[i]->col)) > max_caplen)
531 max_caplen = l;
533 lr_caplen = strlen(mcolors.files[COL_LC]->col) +
534 strlen(mcolors.files[COL_RC]->col);
536 /* Default for orphan is same as link. */
537 if (!mcolors.files[COL_OR] || !mcolors.files[COL_OR]->col)
538 mcolors.files[COL_OR] = mcolors.files[COL_LN];
539 /* Default for missing files: currently not used */
540 if (!mcolors.files[COL_MI] || !mcolors.files[COL_MI]->col)
541 mcolors.files[COL_MI] = mcolors.files[COL_FI];
543 return;
546 static void
547 zlrputs(char *cap)
549 if (!*last_cap || strcmp(last_cap, cap)) {
550 VARARR(char, buf, lr_caplen + max_caplen + 1);
552 strcpy(buf, mcolors.files[COL_LC]->col);
553 strcat(buf, cap);
554 strcat(buf, mcolors.files[COL_RC]->col);
556 tputs(buf, 1, putshout);
558 strcpy(last_cap, cap);
562 static void
563 zcputs(char *group, int colour)
565 Filecol fc;
567 for (fc = mcolors.files[colour]; fc; fc = fc->next)
568 if (fc->col &&
569 (!fc->prog || !group || pattry(fc->prog, group))) {
570 zlrputs(fc->col);
572 return;
574 zlrputs("0");
577 /* Turn off colouring. */
579 static void
580 zcoff(void)
582 if (mcolors.files[COL_EC] && mcolors.files[COL_EC]->col) {
583 tputs(mcolors.files[COL_EC]->col, 1, putshout);
584 *last_cap = '\0';
585 } else
586 zcputs(NULL, COL_NO);
590 static void
591 initiscol()
593 int i;
595 zlrputs(patcols[0]);
597 curiscols[curiscol = 0] = *patcols++;
599 curisbeg = curissend = 0;
601 for (i = 0; i < nrefs; i++)
602 sendpos[i] = 0xfffffff;
603 for (; i < MAX_POS; i++)
604 begpos[i] = endpos[i] = sendpos[i] = 0xfffffff;
607 static void
608 doiscol(int pos)
610 int fi;
612 while (pos > sendpos[curissend]) {
613 curissend++;
614 if (curiscol) {
615 zcputs(NULL, COL_NO);
616 zlrputs(curiscols[--curiscol]);
619 while (((fi = (endpos[curisbeg] < begpos[curisbeg] ||
620 begpos[curisbeg] == -1)) ||
621 pos == begpos[curisbeg]) && *patcols) {
622 if (!fi) {
623 int i, j, e = endpos[curisbeg];
625 /* insert e in sendpos */
626 for (i = curissend; sendpos[i] <= e; ++i)
628 for (j = MAX_POS - 1; j > i; --j)
629 sendpos[j] = sendpos[j-1];
630 sendpos[i] = e;
632 zcputs(NULL, COL_NO);
633 zlrputs(*patcols);
634 curiscols[++curiscol] = *patcols;
636 ++patcols;
637 ++curisbeg;
641 /* Stripped-down version of printfmt(). But can do in-string colouring. */
643 static int
644 clprintfmt(char *p, int ml)
646 int cc = 0, i = 0, ask, beg;
648 initiscol();
650 for (; *p; p++) {
651 doiscol(i++);
652 cc++;
653 if (*p == '\n') {
654 if (mlbeg >= 0 && tccan(TCCLEAREOL))
655 tcout(TCCLEAREOL);
656 cc = 0;
658 if (ml == mlend - 1 && (cc % columns) == columns - 1)
659 return 0;
661 if (*p == Meta) {
662 p++;
663 putc(*p ^ 32, shout);
664 } else
665 putc(*p, shout);
666 if ((beg = !(cc % columns)))
667 ml++;
668 if (mscroll && !(cc % columns) &&
669 !--mrestlines && (ask = asklistscroll(ml)))
670 return ask;
672 if (mlbeg >= 0 && tccan(TCCLEAREOL))
673 tcout(TCCLEAREOL);
674 return 0;
678 * Local version of nicezputs() with in-string colouring
679 * and scrolling.
682 static int
683 clnicezputs(int do_colors, char *s, int ml)
685 int i = 0, col = 0, ask, oml = ml;
686 char *t;
687 ZLE_CHAR_T cc;
688 #ifdef MULTIBYTE_SUPPORT
690 * ums is the untokenized, unmetafied string (length umlen)
691 * uptr is a pointer into it
692 * sptr is the start of the nice character representation
693 * wptr is the point at which the wide character itself starts
694 * (but may be the end of the string if the character was fully
695 * prettified).
696 * ret is the return status from the conversion to a wide character
697 * umleft is the remaining length of the unmetafied string to output
698 * umlen is the full length of the unmetafied string
699 * width is the full printing width of a prettified character,
700 * including both ASCII prettification and the wide character itself.
701 * mbs is the shift state of the conversion to wide characters.
703 char *ums, *uptr, *sptr, *wptr;
704 int umleft, umlen, eol = 0;
705 size_t width;
706 mbstate_t mbs;
708 memset(&mbs, 0, sizeof mbs);
709 ums = ztrdup(s);
710 untokenize(ums);
711 uptr = unmetafy(ums, &umlen);
712 umleft = umlen;
714 if (do_colors)
715 initiscol();
717 mb_metacharinit();
718 while (umleft > 0) {
719 size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, uptr, umleft, &mbs);
721 switch (cnt) {
722 case MB_INCOMPLETE:
723 eol = 1;
724 /* FALL THROUGH */
725 case MB_INVALID:
726 /* This handles byte values that aren't valid wide-character
727 * sequences. */
728 sptr = nicechar(*uptr);
729 /* everything here is ASCII... */
730 width = strlen(sptr);
731 wptr = sptr + width;
732 cnt = 1;
733 /* Get mbs out of its undefined state. */
734 memset(&mbs, 0, sizeof mbs);
735 break;
736 case 0:
737 /* This handles a '\0' in the input (which is a real char
738 * to us, not a terminator). */
739 cnt = 1;
740 /* FALL THROUGH */
741 default:
742 sptr = wcs_nicechar(cc, &width, &wptr);
743 break;
746 umleft -= cnt;
747 uptr += cnt;
748 if (do_colors) {
750 * The code for the colo[u]ri[s/z]ation is obscure (surprised?)
751 * but if we do it for every input character, as we do in
752 * the simple case, we shouldn't go too far wrong.
754 while (cnt--)
755 doiscol(i++);
759 * Loop over characters in the output of the nice
760 * representation. This will often correspond to one input
761 * (possibly multibyte) character.
763 for (t = sptr; *t; t++) {
764 /* Input is metafied... */
765 int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
766 /* Is the screen full? */
767 if (ml == mlend - 1 && col == columns - 1) {
768 mlprinted = ml - oml;
769 return 0;
771 if (t < wptr) {
772 /* outputting ASCII, so single-width */
773 putc(nc, shout);
774 col++;
775 width--;
776 } else {
777 /* outputting a single wide character, do the lot */
778 putc(nc, shout);
779 /* don't check column until finished */
780 if (t[1])
781 continue;
782 /* now we've done the entire rest of the representation */
783 col += width;
786 * There might be problems with characters of printing width
787 * greater than one here.
789 if (col > columns) {
790 ml++;
791 if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
792 mlprinted = ml - oml;
793 return ask;
795 col -= columns;
796 if (do_colors)
797 fputs(" \010", shout);
802 free(ums);
803 #else
805 if (do_colors)
806 initiscol();
808 while ((cc = *s++)) {
809 if (do_colors)
810 doiscol(i++);
811 if (itok(cc)) {
812 if (cc <= Comma)
813 cc = ztokens[cc - Pound];
814 else
815 continue;
817 if (cc == Meta)
818 cc = *s++ ^ 32;
820 for (t = nicechar(cc); *t; t++) {
821 int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
822 if (ml == mlend - 1 && col == columns - 1) {
823 mlprinted = ml - oml;
824 return 0;
826 putc(nc, shout);
827 if (++col > columns) {
828 ml++;
829 if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
830 mlprinted = ml - oml;
831 return ask;
833 col = 0;
834 if (do_colors)
835 fputs(" \010", shout);
839 #endif
840 mlprinted = ml - oml;
841 return 0;
844 /* Get the terminal color string for the given match. */
846 static int
847 putmatchcol(char *group, char *n)
849 Patcol pc;
851 nrefs = MAX_POS - 1;
853 for (pc = mcolors.pats; pc; pc = pc->next)
854 if ((!pc->prog || !group || pattry(pc->prog, group)) &&
855 pattryrefs(pc->pat, n, -1, -1, 0, &nrefs, begpos, endpos)) {
856 if (pc->cols[1]) {
857 patcols = pc->cols;
859 return 1;
861 zlrputs(pc->cols[0]);
863 return 0;
866 zcputs(group, COL_NO);
868 return 0;
871 /* Get the terminal color string for the file with the given name and
872 * file modes. */
874 static int
875 putfilecol(char *group, char *n, mode_t m, int special)
877 int colour = -1;
878 Extcol ec;
879 Patcol pc;
881 nrefs = MAX_POS - 1;
883 for (pc = mcolors.pats; pc; pc = pc->next)
884 if ((!pc->prog || !group || pattry(pc->prog, group)) &&
885 pattryrefs(pc->pat, n, -1, -1, 0, &nrefs, begpos, endpos)) {
886 if (pc->cols[1]) {
887 patcols = pc->cols;
889 return 1;
891 zlrputs(pc->cols[0]);
893 return 0;
896 if (special != -1) {
897 colour = special;
898 } else if (S_ISDIR(m)) {
899 if (m & S_IWOTH)
900 if (m & S_ISVTX)
901 colour = COL_TW;
902 else
903 colour = COL_OW;
904 else if (m & S_ISVTX)
905 colour = COL_ST;
906 else
907 colour = COL_DI;
908 } else if (S_ISLNK(m))
909 colour = COL_LN;
910 else if (S_ISFIFO(m))
911 colour = COL_PI;
912 else if (S_ISSOCK(m))
913 colour = COL_SO;
914 else if (S_ISBLK(m))
915 colour = COL_BD;
916 else if (S_ISCHR(m))
917 colour = COL_CD;
918 else if (m & S_ISUID)
919 colour = COL_SU;
920 else if (m & S_ISGID)
921 colour = COL_SG;
922 else if (S_ISREG(m) && (m & S_IXUGO))
923 colour = COL_EX;
925 if (colour != -1) {
926 zcputs(group, colour);
927 return 0;
930 for (ec = mcolors.exts; ec; ec = ec->next)
931 if (strsfx(ec->ext, n) &&
932 (!ec->prog || !group || pattry(ec->prog, group))) {
933 zlrputs(ec->col);
935 return 0;
938 zcputs(group, COL_FI);
940 return 0;
943 static Cmgroup last_group;
945 /**/
946 static int
947 asklistscroll(int ml)
949 Thingy cmd;
950 int i, ret = 0;
952 compprintfmt(NULL, 1, 1, 1, ml, NULL);
954 fflush(shout);
955 zsetterm();
956 selectlocalmap(lskeymap);
957 if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
958 ret = 1;
959 else if (cmd == Th(z_acceptline) ||
960 cmd == Th(z_downhistory) ||
961 cmd == Th(z_downlineorhistory) ||
962 cmd == Th(z_downlineorsearch) ||
963 cmd == Th(z_vidownlineorhistory))
964 mrestlines = 1;
965 else if (cmd == Th(z_completeword) ||
966 cmd == Th(z_expandorcomplete) ||
967 cmd == Th(z_expandorcompleteprefix) ||
968 cmd == Th(z_menucomplete) ||
969 cmd == Th(z_menuexpandorcomplete) ||
970 !strcmp(cmd->nam, "menu-select") ||
971 !strcmp(cmd->nam, "complete-word") ||
972 !strcmp(cmd->nam, "expand-or-complete") ||
973 !strcmp(cmd->nam, "expand-or-complete-prefix") ||
974 !strcmp(cmd->nam, "menu-complete") ||
975 !strcmp(cmd->nam, "menu-expand-or-complete"))
976 mrestlines = lines - 1;
977 else {
978 ungetkeycmd();
979 ret = 1;
981 selectlocalmap(NULL);
982 settyinfo(&shttyinfo);
983 putc('\r', shout);
984 for (i = columns - 1; i-- > 0; )
985 putc(' ', shout);
986 putc('\r', shout);
988 return ret;
991 #define dolist(X) ((X) >= mlbeg && (X) < mlend)
992 #define dolistcl(X) ((X) >= mlbeg && (X) < mlend + 1)
993 #define dolistnl(X) ((X) >= mlbeg && (X) < mlend - 1)
995 /**/
996 static int
997 compprintnl(int ml)
999 int ask;
1001 if (mlbeg >= 0 && tccan(TCCLEAREOL))
1002 tcout(TCCLEAREOL);
1003 putc('\n', shout);
1005 if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
1006 return ask;
1008 return 0;
1011 /* This is used to print the strings (e.g. explanations). *
1012 * It returns the number of lines printed. */
1014 /**/
1015 static int
1016 compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
1018 char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12];
1019 int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat;
1021 if ((stat = !fmt)) {
1022 if (mlbeg >= 0) {
1023 if (!(fmt = mstatus)) {
1024 mlprinted = 0;
1025 return 0;
1027 cc = -1;
1028 } else
1029 fmt = mlistp;
1031 MB_METACHARINIT();
1032 for (p = fmt; *p; ) {
1033 convchar_t cchar;
1034 int len, width;
1036 len = MB_METACHARLENCONV(p, &cchar);
1037 #ifdef MULTIBYTE_SUPPORT
1038 if (cchar == WEOF) {
1039 cchar = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p);
1040 width = 1;
1042 else
1043 #endif
1044 width = WCWIDTH_WINT(cchar);
1046 if (doesc && cchar == ZWC('%')) {
1047 p += len;
1048 if (*p) {
1049 int arg = 0, is_fg;
1051 len = MB_METACHARLENCONV(p, &cchar);
1052 #ifdef MULTIBYTE_SUPPORT
1053 if (cchar == WEOF)
1054 cchar = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p);
1055 #endif
1056 p += len;
1058 if (idigit(*p))
1059 arg = zstrtol(p, &p, 10);
1061 m = 0;
1062 switch (cchar) {
1063 case ZWC('%'):
1064 if (dopr == 1)
1065 putc('%', shout);
1066 cc++;
1067 break;
1068 case ZWC('n'):
1069 if (!stat) {
1070 sprintf(nc, "%d", n);
1071 if (dopr == 1)
1072 fputs(nc, shout);
1073 /* everything here is ASCII... */
1074 cc += strlen(nc);
1076 break;
1077 case ZWC('B'):
1078 b = 1;
1079 if (dopr)
1080 tcout(TCBOLDFACEBEG);
1081 break;
1082 case ZWC('b'):
1083 b = 0; m = 1;
1084 if (dopr)
1085 tcout(TCALLATTRSOFF);
1086 break;
1087 case ZWC('S'):
1088 s = 1;
1089 if (dopr)
1090 tcout(TCSTANDOUTBEG);
1091 break;
1092 case ZWC('s'):
1093 s = 0; m = 1;
1094 if (dopr)
1095 tcout(TCSTANDOUTEND);
1096 break;
1097 case ZWC('U'):
1098 u = 1;
1099 if (dopr)
1100 tcout(TCUNDERLINEBEG);
1101 break;
1102 case ZWC('u'):
1103 u = 0; m = 1;
1104 if (dopr)
1105 tcout(TCUNDERLINEEND);
1106 break;
1107 case ZWC('F'):
1108 case ZWC('K'):
1109 is_fg = (cchar == ZWC('F'));
1110 /* colours must be ASCII */
1111 if (*p == '{') {
1112 p++;
1113 arg = match_colour((const char **)&p, is_fg, 0);
1114 if (*p == '}')
1115 p++;
1116 } else
1117 arg = match_colour(NULL, is_fg, arg);
1118 if (arg >= 0 && dopr)
1119 set_colour_attribute(arg, is_fg ? COL_SEQ_FG :
1120 COL_SEQ_BG, 0);
1121 break;
1122 case ZWC('f'):
1123 if (dopr)
1124 set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0);
1125 break;
1126 case ZWC('k'):
1127 if (dopr)
1128 set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0);
1129 break;
1130 case ZWC('{'):
1131 if (arg)
1132 cc += arg;
1133 for (; *p && (*p != '%' || p[1] != '}'); p++)
1134 if (dopr)
1135 putc(*p == Meta ? *++p ^ 32 : *p, shout);
1136 if (*p)
1137 p += 2;
1138 break;
1139 case ZWC('m'):
1140 if (stat) {
1141 sprintf(nc, "%d/%d", (n ? mlastm : mselect),
1142 listdat.nlist);
1143 m = 2;
1145 break;
1146 case ZWC('M'):
1147 if (stat) {
1148 sprintf(nbuf, "%d/%d", (n ? mlastm : mselect),
1149 listdat.nlist);
1150 sprintf(nc, "%-9s", nbuf);
1151 m = 2;
1153 break;
1154 case ZWC('l'):
1155 if (stat) {
1156 sprintf(nc, "%d/%d", ml + 1, listdat.nlines);
1157 m = 2;
1159 break;
1160 case ZWC('L'):
1161 if (stat) {
1162 sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines);
1163 sprintf(nc, "%-9s", nbuf);
1164 m = 2;
1166 break;
1167 case ZWC('p'):
1168 if (stat) {
1169 if (ml == listdat.nlines - 1)
1170 strcpy(nc, "Bottom");
1171 else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl))
1172 sprintf(nc, "%d%%",
1173 ((ml + 1) * 100) / listdat.nlines);
1174 else
1175 strcpy(nc, "Top");
1176 m = 2;
1178 break;
1179 case ZWC('P'):
1180 if (stat) {
1181 if (ml == listdat.nlines - 1)
1182 strcpy(nc, "Bottom");
1183 else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl))
1184 sprintf(nc, "%2d%% ",
1185 ((ml + 1) * 100) / listdat.nlines);
1186 else
1187 strcpy(nc, "Top ");
1188 m = 2;
1190 break;
1192 if (m == 2 && dopr == 1) {
1193 /* nc only contains ASCII text */
1194 int l = strlen(nc);
1196 if (l + cc > columns - 2)
1197 nc[l -= l + cc - (columns - 2)] = '\0';
1198 fputs(nc, shout);
1199 cc += l;
1200 } else if (dopr && m == 1) {
1201 if (b)
1202 tcout(TCBOLDFACEBEG);
1203 if (s)
1204 tcout(TCSTANDOUTBEG);
1205 if (u)
1206 tcout(TCUNDERLINEBEG);
1208 } else
1209 break;
1210 } else {
1211 cc += width;
1213 if ((cc >= columns - 2 || cchar == ZWC('\n')) && stat)
1214 dopr = 2;
1215 if (cchar == ZWC('\n')) {
1216 if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL))
1217 tcout(TCCLEAREOL);
1218 l += 1 + ((cc - 1) / columns);
1219 cc = 0;
1221 if (dopr == 1) {
1222 if (ml == mlend - 1 && (cc % columns) == columns - 1) {
1223 dopr = 0;
1224 p += len;
1225 continue;
1227 while (len--) {
1228 if (*p == Meta) {
1229 len--;
1230 p++;
1231 putc(*p++ ^ 32, shout);
1232 } else
1233 putc(*p++, shout);
1236 * TODO: the following doesn't allow for
1237 * character widths greater than 1.
1239 if ((beg = !(cc % columns)) && !stat) {
1240 ml++;
1241 fputs(" \010", shout);
1243 if (mscroll && beg && !--mrestlines && (ask = asklistscroll(ml))) {
1244 *stop = 1;
1245 if (stat && n)
1246 mfirstl = -1;
1247 mlprinted = l + (cc ? ((cc-1) / columns) : 0);
1248 return mlprinted;
1251 else
1252 p += len;
1255 if (dopr) {
1256 if (!(cc % columns))
1257 fputs(" \010", shout);
1258 if (mlbeg >= 0 && tccan(TCCLEAREOL))
1259 tcout(TCCLEAREOL);
1261 if (stat && n)
1262 mfirstl = -1;
1265 * *Not* subtracting 1 from cc at this point appears to be
1266 * correct. C.f. printfmt in zle_tricky.c.
1268 mlprinted = l + (cc / columns);
1269 return mlprinted;
1272 /* This is like zputs(), but allows scrolling. */
1274 /**/
1275 static int
1276 compzputs(char const *s, int ml)
1278 int c, col = 0, ask;
1280 while (*s) {
1281 if (*s == Meta)
1282 c = *++s ^ 32;
1283 else if(itok(*s)) {
1284 s++;
1285 continue;
1286 } else
1287 c = *s;
1288 s++;
1289 putc(c, shout);
1290 if (c == '\n' && mlbeg >= 0 && tccan(TCCLEAREOL))
1291 tcout(TCCLEAREOL);
1292 if (mscroll && (++col == columns || c == '\n')) {
1293 ml++;
1294 if (!--mrestlines && (ask = asklistscroll(ml)))
1295 return ask;
1297 col = 0;
1300 return 0;
1303 /**/
1304 static int
1305 compprintlist(int showall)
1307 static int lasttype = 0, lastbeg = 0, lastml = 0, lastinvcount = -1;
1308 static int lastn = 0, lastnl = 0, lastnlnct = -1;
1309 static Cmgroup lastg = NULL;
1310 static Cmatch *lastp = NULL;
1311 static Cexpl *lastexpl = NULL;
1313 Cmgroup g;
1314 Cmatch *p, m;
1315 Cexpl *e;
1316 int pnl = 0, cl, mc = 0, ml = 0, printed = 0, stop = 0, asked = 1;
1317 int lastused = 0;
1319 mfirstl = -1;
1320 if (mnew || lastinvcount != invcount || lastbeg != mlbeg || mlbeg < 0) {
1321 lasttype = 0;
1322 lastg = NULL;
1323 lastexpl = NULL;
1324 lastml = 0;
1325 lastnlnct = -1;
1327 cl = (listdat.nlines > lines - nlnct - mhasstat ?
1328 lines - nlnct - mhasstat : listdat.nlines) - (lastnlnct > nlnct);
1329 lastnlnct = nlnct;
1330 mrestlines = lines - 1;
1331 lastinvcount = invcount;
1333 if (cl < 2) {
1334 cl = -1;
1335 if (tccan(TCCLEAREOD))
1336 tcout(TCCLEAREOD);
1337 } else if (mlbeg >= 0 && !tccan(TCCLEAREOL) && tccan(TCCLEAREOD))
1338 tcout(TCCLEAREOD);
1340 g = ((lasttype && lastg) ? lastg : amatches);
1341 while (g) {
1342 char **pp = g->ylist;
1344 if ((e = g->expls)) {
1345 int l;
1347 if (!lastused && lasttype == 1) {
1348 e = lastexpl;
1349 ml = lastml;
1350 lastused = 1;
1352 while (*e) {
1353 if (((*e)->count || (*e)->always) &&
1354 (!listdat.onlyexpl ||
1355 (listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) {
1356 if (pnl) {
1357 if (dolistnl(ml) && compprintnl(ml))
1358 goto end;
1359 pnl = 0;
1360 ml++;
1361 if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
1362 cl = -1;
1363 if (tccan(TCCLEAREOD))
1364 tcout(TCCLEAREOD);
1367 if (mlbeg < 0 && mfirstl < 0)
1368 mfirstl = ml;
1369 l = compprintfmt((*e)->str,
1370 ((*e)->always ? -1 : (*e)->count),
1371 dolist(ml), 1, ml, &stop);
1372 if (mselect >= 0) {
1373 int mm = (mcols * ml), i;
1375 for (i = mcols; i-- > 0; ) {
1376 DPUTS(mm+i >= mgtabsize, "BUG: invalid position");
1377 mtab[mm + i] = mtmark(NULL);
1378 mgtab[mm + i] = mgmark(NULL);
1381 if (stop)
1382 goto end;
1383 if (!lasttype && ml >= mlbeg) {
1384 lasttype = 1;
1385 lastg = g;
1386 lastbeg = mlbeg;
1387 lastml = ml;
1388 lastexpl = e;
1389 lastp = NULL;
1390 lastused = 1;
1392 ml += mlprinted;
1393 if (dolistcl(ml) && cl >= 0 && (cl -= mlprinted) <= 1) {
1394 cl = -1;
1395 if (tccan(TCCLEAREOD))
1396 tcout(TCCLEAREOD);
1398 pnl = 1;
1400 e++;
1401 if (!mnew && ml > mlend)
1402 goto end;
1405 if (!listdat.onlyexpl && mlbeg < 0 && pp && *pp) {
1406 if (pnl) {
1407 if (dolistnl(ml) && compprintnl(ml))
1408 goto end;
1409 pnl = 0;
1410 ml++;
1411 if (cl >= 0 && --cl <= 1) {
1412 cl = -1;
1413 if (tccan(TCCLEAREOD))
1414 tcout(TCCLEAREOD);
1417 if (mlbeg < 0 && mfirstl < 0)
1418 mfirstl = ml;
1419 if (g->flags & CGF_LINES) {
1420 while (*pp) {
1421 if (compzputs(*pp, ml))
1422 goto end;
1423 if (*++pp && compprintnl(ml))
1424 goto end;
1426 } else {
1427 int n = g->lcount, nl, nc, i, a;
1428 char **pq;
1430 nl = nc = g->lins;
1432 while (n && nl--) {
1433 i = g->cols;
1434 mc = 0;
1435 pq = pp;
1436 while (n && i--) {
1437 if (pq - g->ylist >= g->lcount)
1438 break;
1439 if (compzputs(*pq, mscroll))
1440 goto end;
1441 if (i) {
1442 a = (g->widths ? g->widths[mc] : g->width) -
1443 strlen(*pq);
1444 while (a--)
1445 putc(' ', shout);
1447 pq += ((g->flags & CGF_ROWS) ? 1 : nc);
1448 mc++;
1449 n--;
1451 if (n) {
1452 if (compprintnl(ml))
1453 goto end;
1454 ml++;
1455 if (cl >= 0 && --cl <= 1) {
1456 cl = -1;
1457 if (tccan(TCCLEAREOD))
1458 tcout(TCCLEAREOD);
1461 pp += ((g->flags & CGF_ROWS) ? g->cols : 1);
1464 } else if (!listdat.onlyexpl &&
1465 (g->lcount || (showall && g->mcount))) {
1466 int n = g->dcount, nl, nc, i, j, wid;
1467 Cmatch *q;
1469 nl = nc = g->lins;
1471 if ((g->flags & CGF_HASDL) &&
1472 (lastused || !lasttype || lasttype == 2)) {
1473 if (!lastused && lasttype == 2) {
1474 p = lastp;
1475 ml = lastml;
1476 n = lastn;
1477 nl = lastnl;
1478 lastused = 1;
1479 pnl = 0;
1480 } else
1481 p = g->matches;
1483 for (; (m = *p); p++) {
1484 if (m->disp && (m->flags & CMF_DISPLINE) &&
1485 (showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) {
1486 if (pnl) {
1487 if (dolistnl(ml) && compprintnl(ml))
1488 goto end;
1489 pnl = 0;
1490 ml++;
1491 if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
1492 cl = -1;
1493 if (tccan(TCCLEAREOD))
1494 tcout(TCCLEAREOD);
1497 if (!lasttype && ml >= mlbeg) {
1498 lasttype = 2;
1499 lastg = g;
1500 lastbeg = mlbeg;
1501 lastml = ml;
1502 lastp = p;
1503 lastn = n;
1504 lastnl = nl;
1505 lastused = 1;
1507 if (mfirstl < 0)
1508 mfirstl = ml;
1509 if (dolist(ml))
1510 printed++;
1511 if (clprintm(g, p, 0, ml, 1, 0))
1512 goto end;
1513 ml += mlprinted;
1514 if (dolistcl(ml) && (cl -= mlprinted) <= 1) {
1515 cl = -1;
1516 if (tccan(TCCLEAREOD))
1517 tcout(TCCLEAREOD);
1519 pnl = 1;
1521 if (!mnew && ml > mlend)
1522 goto end;
1525 if (n && pnl) {
1526 if (dolistnl(ml) && compprintnl(ml))
1527 goto end;
1528 pnl = 0;
1529 ml++;
1530 if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
1531 cl = -1;
1532 if (tccan(TCCLEAREOD))
1533 tcout(TCCLEAREOD);
1536 if (!lastused && lasttype == 3) {
1537 p = lastp;
1538 n = lastn;
1539 nl = lastnl;
1540 ml = lastml;
1541 lastused = 1;
1542 } else
1543 p = skipnolist(g->matches, showall);
1545 while (n && nl--) {
1546 if (!lasttype && ml >= mlbeg) {
1547 lasttype = 3;
1548 lastg = g;
1549 lastbeg = mlbeg;
1550 lastml = ml;
1551 lastp = p;
1552 lastn = n;
1553 lastnl = nl + 1;
1554 lastused = 1;
1556 i = g->cols;
1557 mc = 0;
1558 q = p;
1559 while (n && i--) {
1560 wid = (g->widths ? g->widths[mc] : g->width);
1561 if (!(m = *q)) {
1562 if (clprintm(g, NULL, mc, ml, (!i), wid))
1563 goto end;
1564 break;
1566 if (clprintm(g, q, mc, ml, (!i), wid))
1567 goto end;
1569 if (dolist(ml))
1570 printed++;
1571 ml += mlprinted;
1572 if (dolistcl(ml) && (cl -= mlprinted) < 1) {
1573 cl = -1;
1574 if (tccan(TCCLEAREOD))
1575 tcout(TCCLEAREOD);
1577 if (mfirstl < 0)
1578 mfirstl = ml;
1580 if (--n)
1581 for (j = ((g->flags & CGF_ROWS) ? 1 : nc);
1582 j && *q; j--)
1583 q = skipnolist(q + 1, showall);
1584 mc++;
1586 while (i-- > 0) {
1587 if (clprintm(g, NULL, mc, ml, (!i),
1588 (g->widths ? g->widths[mc] : g->width)))
1589 goto end;
1590 mc++;
1592 if (n) {
1593 if (dolistnl(ml) && compprintnl(ml))
1594 goto end;
1595 ml++;
1596 if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
1597 cl = -1;
1598 if (tccan(TCCLEAREOD))
1599 tcout(TCCLEAREOD);
1601 if (nl)
1602 for (j = ((g->flags & CGF_ROWS) ? g->cols : 1);
1603 j && *p; j--)
1604 p = skipnolist(p + 1, showall);
1606 if (!mnew && ml > mlend)
1607 goto end;
1610 if (g->lcount || (showall && g->mcount))
1611 pnl = 1;
1612 g = g->next;
1614 asked = 0;
1615 end:
1616 mstatprinted = 0;
1617 lastlistlen = 0;
1618 if (nlnct <= 1)
1619 mscroll = 0;
1620 if (clearflag) {
1621 int nl;
1623 /* Move the cursor up to the prompt, if always_last_prompt *
1624 * is set and all that... */
1625 if (mlbeg >= 0) {
1626 if ((nl = listdat.nlines + nlnct) >= lines) {
1627 if (mhasstat) {
1628 putc('\n', shout);
1629 compprintfmt(NULL, 0, 1, 1, mline, NULL);
1630 mstatprinted = 1;
1632 nl = lines - 1;
1633 } else
1634 nl--;
1635 tcmultout(TCUP, TCMULTUP, nl);
1636 showinglist = -1;
1638 lastlistlen = listdat.nlines;
1639 } else if ((nl = listdat.nlines + nlnct - 1) < lines) {
1640 if (mlbeg >= 0 && tccan(TCCLEAREOL))
1641 tcout(TCCLEAREOL);
1642 tcmultout(TCUP, TCMULTUP, nl);
1643 showinglist = -1;
1645 lastlistlen = listdat.nlines;
1646 } else {
1647 clearflag = 0;
1648 if (!asked) {
1649 mrestlines = (ml + nlnct > lines);
1650 compprintnl(ml);
1653 } else if (!asked) {
1654 mrestlines = (ml + nlnct > lines);
1655 compprintnl(ml);
1657 listshown = (clearflag ? 1 : -1);
1658 mnew = 0;
1660 return printed;
1663 /**/
1664 static int
1665 clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width)
1667 Cmatch m;
1668 int len, subcols = 0, stop = 0, ret = 0;
1670 DPUTS2(mselect >= 0 && ml >= mlines,
1671 "clprintm called with ml too large (%d/%d)",
1672 ml, mlines);
1673 if (g != last_group)
1674 *last_cap = '\0';
1676 last_group = g;
1678 if (!mp) {
1679 if (dolist(ml)) {
1680 zcputs(g->name, COL_SP);
1681 len = width - 2;
1682 while (len-- > 0)
1683 putc(' ', shout);
1684 zcoff();
1686 mlprinted = 0;
1687 return 0;
1689 m = *mp;
1691 if ((m->flags & CMF_ALL) && (!m->disp || !m->disp[0]))
1692 bld_all_str(m);
1694 mlastm = m->gnum;
1695 if (m->disp && (m->flags & CMF_DISPLINE)) {
1696 if (mselect >= 0) {
1697 int mm = (mcols * ml), i;
1699 if (m->flags & CMF_DUMMY) {
1700 for (i = mcols; i-- > 0; ) {
1701 DPUTS(mm+i >= mgtabsize, "BUG: invalid position");
1702 mtab[mm + i] = mtmark(mp);
1703 mgtab[mm + i] = mgmark(g);
1705 } else {
1706 for (i = mcols; i-- > 0; ) {
1707 DPUTS(mm+i >= mgtabsize, "BUG: invalid position");
1708 mtab[mm + i] = mp;
1709 mgtab[mm + i] = g;
1713 if (!dolist(ml)) {
1714 mlprinted = printfmt(m->disp, 0, 0, 0);
1715 return 0;
1717 if (m->gnum == mselect) {
1718 int mm = (mcols * ml);
1719 DPUTS(mm >= mgtabsize, "BUG: invalid position");
1720 mline = ml;
1721 mcol = 0;
1722 mmtabp = mtab + mm;
1723 mgtabp = mgtab + mm;
1724 zcputs(g->name, COL_MA);
1725 } else if ((m->flags & CMF_NOLIST) &&
1726 mcolors.files[COL_HI] && mcolors.files[COL_HI]->col)
1727 zcputs(g->name, COL_HI);
1728 else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)) &&
1729 mcolors.files[COL_DU] && mcolors.files[COL_DU]->col)
1730 zcputs(g->name, COL_DU);
1731 else
1732 subcols = putmatchcol(g->name, m->disp);
1733 if (subcols)
1734 ret = clprintfmt(m->disp, ml);
1735 else {
1736 compprintfmt(m->disp, 0, 1, 0, ml, &stop);
1737 if (stop)
1738 ret = 1;
1740 zcoff();
1741 } else {
1742 int mx, modec;
1744 if (g->widths) {
1745 int i;
1747 for (i = mx = 0; i < mc; i++)
1748 mx += g->widths[i];
1749 } else
1750 mx = mc * g->width;
1752 if (mselect >= 0) {
1753 int mm = mcols * ml, i;
1755 if (m->flags & CMF_DUMMY) {
1756 for (i = (width ? width : mcols); i-- > 0; ) {
1757 DPUTS(mx+mm+i >= mgtabsize, "BUG: invalid position");
1758 mtab[mx + mm + i] = mtmark(mp);
1759 mgtab[mx + mm + i] = mgmark(g);
1761 } else {
1762 for (i = (width ? width : mcols); i-- > 0; ) {
1763 DPUTS(mx+mm+i >= mgtabsize, "BUG: invalid position");
1764 mtab[mx + mm + i] = mp;
1765 mgtab[mx + mm + i] = g;
1769 if (!dolist(ml)) {
1770 int nc = ZMB_nicewidth(m->disp ? m->disp : m->str);
1771 if (nc)
1772 mlprinted = (nc-1) / columns;
1773 else
1774 mlprinted = 0;
1775 return 0;
1777 if (m->gnum == mselect) {
1778 int mm = mcols * ml;
1779 DPUTS(mx+mm >= mgtabsize, "BUG: invalid position");
1781 mcol = mx;
1782 mline = ml;
1783 mmtabp = mtab + mx + mm;
1784 mgtabp = mgtab + mx + mm;
1785 zcputs(g->name, COL_MA);
1786 } else if (m->flags & CMF_NOLIST)
1787 zcputs(g->name, COL_HI);
1788 else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)))
1789 zcputs(g->name, COL_DU);
1790 else if (m->mode) {
1792 * Symlink is orphaned if we read the mode with lstat
1793 * but couldn't read one with stat. That's the
1794 * only way they can be different so the following
1795 * test should be enough.
1797 int orphan_colour = (m->mode && !m->fmode) ? COL_OR : -1;
1798 if (mcolors.flags & LC_FOLLOW_SYMLINKS) {
1799 subcols = putfilecol(g->name, m->str, m->fmode, orphan_colour);
1800 } else {
1801 subcols = putfilecol(g->name, m->str, m->mode, orphan_colour);
1804 else
1805 subcols = putmatchcol(g->name, (m->disp ? m->disp : m->str));
1807 ret = clnicezputs(subcols,
1808 (m->disp ? m->disp : m->str), ml);
1809 if (ret) {
1810 zcoff();
1811 return 1;
1813 len = ZMB_nicewidth(m->disp ? m->disp : m->str);
1814 mlprinted = len ? (len-1) / columns : 0;
1816 modec = (mcolors.flags & LC_FOLLOW_SYMLINKS) ? m->fmodec : m->modec;
1817 if ((g->flags & CGF_FILES) && modec) {
1818 if (m->gnum != mselect) {
1819 zcoff();
1820 zcputs(g->name, COL_TC);
1822 putc(modec, shout);
1823 len++;
1825 if ((len = width - len - 2) > 0) {
1826 if (m->gnum != mselect) {
1827 zcoff();
1828 zcputs(g->name, COL_SP);
1830 while (len-- > 0)
1831 putc(' ', shout);
1833 zcoff();
1834 if (!lastc) {
1835 zcputs(g->name, COL_SP);
1836 fputs(" ", shout);
1837 zcoff();
1840 return ret;
1843 static int
1844 singlecalc(int *cp, int l, int *lcp)
1846 int c = *cp, n, j, first = 1;
1847 Cmatch **p, *op, *mp = mtab[l * columns + c];
1849 for (n = 0, j = c, p = mtab + l * columns + c, op = NULL; j >= 0; j--, p--) {
1850 if (*p == mp)
1851 c = j;
1852 if (!first && *p != op)
1853 n++;
1854 op = *p;
1855 first = 0;
1857 *cp = c;
1858 *lcp = 1;
1859 for (p = mtab + l * columns + c; c < columns; c++, p++)
1860 if (*p && mp != *p)
1861 *lcp = 0;
1863 return n;
1866 static void
1867 singledraw()
1869 Cmgroup g;
1870 int mc1, mc2, ml1, ml2, md1, md2, mcc1, mcc2, lc1, lc2, t1, t2;
1872 t1 = mline - mlbeg;
1873 t2 = moline - molbeg;
1875 if (t2 < t1) {
1876 mc1 = mocol; ml1 = moline; md1 = t2;
1877 mc2 = mcol; ml2 = mline; md2 = t1;
1878 } else {
1879 mc1 = mcol; ml1 = mline; md1 = t1;
1880 mc2 = mocol; ml2 = moline; md2 = t2;
1882 mcc1 = singlecalc(&mc1, ml1, &lc1);
1883 mcc2 = singlecalc(&mc2, ml2, &lc2);
1885 if (md1)
1886 tc_downcurs(md1);
1887 if (mc1)
1888 tcmultout(TCRIGHT, TCMULTRIGHT, mc1);
1889 DPUTS(ml1 * columns + mc1 >= mgtabsize, "BUG: invalid position");
1890 g = mgtab[ml1 * columns + mc1];
1891 clprintm(g, mtab[ml1 * columns + mc1], mcc1, ml1, lc1,
1892 (g->widths ? g->widths[mcc1] : g->width));
1893 if (mlprinted)
1894 (void) tcmultout(TCUP, TCMULTUP, mlprinted);
1895 putc('\r', shout);
1897 if (md2 != md1)
1898 tc_downcurs(md2 - md1);
1899 if (mc2)
1900 tcmultout(TCRIGHT, TCMULTRIGHT, mc2);
1901 DPUTS(ml2 * columns + mc2 >= mgtabsize, "BUG: invalid position");
1902 g = mgtab[ml2 * columns + mc2];
1903 clprintm(g, mtab[ml2 * columns + mc2], mcc2, ml2, lc2,
1904 (g->widths ? g->widths[mcc2] : g->width));
1905 if (mlprinted)
1906 (void) tcmultout(TCUP, TCMULTUP, mlprinted);
1907 putc('\r', shout);
1909 if (mstatprinted) {
1910 int i = lines - md2 - nlnct;
1912 tc_downcurs(i - 1);
1913 compprintfmt(NULL, 0, 1, 1, mline, NULL);
1914 tcmultout(TCUP, TCMULTUP, lines - 1);
1915 } else
1916 tcmultout(TCUP, TCMULTUP, md2 + nlnct);
1918 showinglist = -1;
1919 listshown = 1;
1922 static int
1923 complistmatches(UNUSED(Hookdef dummy), Chdata dat)
1925 static int onlnct = -1;
1926 static int extendedglob;
1928 Cmgroup oamatches = amatches;
1930 amatches = dat->matches;
1932 noselect = 0;
1934 if ((minfo.asked == 2 && mselect < 0) || nlnct >= lines) {
1935 showinglist = 0;
1936 amatches = oamatches;
1937 return (noselect = 1);
1941 * There's a lot of memory allocation from this function
1942 * for setting up the color display which isn't needed
1943 * after the function exits, so it's worthwhile pushing
1944 * another heap. As this is called from a hook in the main
1945 * completion handler nothing temporarily allocated from here can be
1946 * useful outside.
1948 pushheap();
1949 extendedglob = opts[EXTENDEDGLOB];
1950 opts[EXTENDEDGLOB] = 1;
1952 getcols();
1954 mnew = ((calclist(mselect >= 0) || mlastcols != columns ||
1955 mlastlines != listdat.nlines) && mselect >= 0);
1957 if (!listdat.nlines || (mselect >= 0 &&
1958 !(isset(USEZLE) && !termflags &&
1959 complastprompt && *complastprompt))) {
1960 showinglist = listshown = 0;
1961 noselect = 1;
1962 amatches = oamatches;
1963 popheap();
1964 opts[EXTENDEDGLOB] = extendedglob;
1965 return 1;
1967 if (inselect || mlbeg >= 0)
1968 clearflag = 0;
1970 mscroll = 0;
1971 mlistp = NULL;
1973 queue_signals();
1974 if (mselect >= 0 || mlbeg >= 0 ||
1975 (mlistp = dupstring(getsparam("LISTPROMPT")))) {
1976 unqueue_signals();
1977 if (mlistp && !*mlistp)
1978 mlistp = "%SAt %p: Hit TAB for more, or the character to insert%s";
1979 trashzle();
1980 showinglist = listshown = 0;
1982 lastlistlen = 0;
1984 if (mlistp) {
1985 clearflag = (isset(USEZLE) && !termflags && dolastprompt);
1986 mscroll = 1;
1987 } else {
1988 clearflag = 1;
1989 minfo.asked = (listdat.nlines + nlnct <= lines);
1991 } else {
1992 unqueue_signals();
1993 mlistp = NULL;
1994 if (asklist()) {
1995 amatches = oamatches;
1996 popheap();
1997 opts[EXTENDEDGLOB] = extendedglob;
1998 return (noselect = 1);
2001 if (mlbeg >= 0) {
2002 mlend = mlbeg + lines - nlnct - mhasstat;
2003 while (mline >= mlend)
2004 mlbeg++, mlend++;
2005 } else
2006 mlend = 9999999;
2008 if (mnew) {
2009 int i;
2011 mtab_been_reallocated = 1;
2013 i = columns * listdat.nlines;
2014 free(mtab);
2015 mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **));
2016 memset(mtab, 0, i * sizeof(Cmatch **));
2017 free(mgtab);
2018 mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
2019 #ifdef DEBUG
2020 mgtabsize = i;
2021 #endif
2022 memset(mgtab, 0, i * sizeof(Cmgroup));
2023 mlastcols = mcols = columns;
2024 mlastlines = mlines = listdat.nlines;
2026 last_cap = (char *) zhalloc(max_caplen + 1);
2027 *last_cap = '\0';
2029 if (!mnew && inselect && onlnct == nlnct && mlbeg >= 0 && mlbeg == molbeg)
2030 singledraw();
2031 else if (!compprintlist(mselect >= 0) || !clearflag)
2032 noselect = 1;
2034 onlnct = nlnct;
2035 molbeg = mlbeg;
2036 mocol = mcol;
2037 moline = mline;
2039 amatches = oamatches;
2041 popheap();
2042 opts[EXTENDEDGLOB] = extendedglob;
2044 return noselect;
2047 static int
2048 adjust_mcol(int wish, Cmatch ***tabp, Cmgroup **grp)
2050 Cmatch **tab = *tabp;
2051 int p, n, c;
2053 tab -= mcol;
2055 for (p = wish; p >= 0 && (!tab[p] || mmarked(tab[p])); p--);
2056 for (n = wish; n < mcols && (!tab[n] || mmarked(tab[n])); n++);
2057 if (n == mcols)
2058 n = -1;
2060 if (p < 0) {
2061 if (n < 0)
2062 return 1;
2063 c = n;
2064 } else if (n < 0)
2065 c = p;
2066 else
2067 c = ((mcol - p) < (n - mcol) ? p : n);
2069 *tabp = tab + c;
2070 if (grp)
2071 *grp = *grp + c - mcol;
2073 mcol = c;
2075 return 0;
2078 typedef struct menustack *Menustack;
2080 struct menustack {
2081 Menustack prev;
2082 char *line;
2083 Brinfo brbeg;
2084 Brinfo brend;
2085 int nbrbeg, nbrend;
2086 int cs, acc, nmatches, mline, mlbeg, nolist;
2087 struct menuinfo info;
2088 Cmgroup amatches, pmatches, lastmatches, lastlmatches;
2090 * Status for how line looked like previously.
2092 char *origline;
2093 int origcs, origll;
2095 * Status for interactive mode. status is the line
2096 * printed above the matches saying what the interactive
2097 * completion prefix is. mode says whether we are in
2098 * interactive or some search mode.
2099 * typed.
2101 char *status;
2102 int mode;
2105 typedef struct menusearch *Menusearch;
2107 struct menusearch {
2108 Menusearch prev;
2109 char *str;
2110 int line;
2111 int col;
2112 int back;
2113 int state;
2114 Cmatch **ptr;
2117 #define MS_OK 0
2118 #define MS_FAILED 1
2119 #define MS_WRAPPED 2
2121 #define MAX_STATUS 128
2123 static char *
2124 setmstatus(char *status, char *sline, int sll, int scs,
2125 int *csp, int *llp, int *lenp)
2127 char *p, *s, *ret = NULL;
2128 int pl, sl, max;
2130 METACHECK();
2132 if (csp) {
2133 *csp = zlemetacs;
2134 *llp = zlemetall;
2135 *lenp = lastend - wb;
2137 ret = dupstring(zlemetaline);
2139 p = (char *) zhalloc(zlemetacs - wb + 1);
2140 strncpy(p, zlemetaline + wb, zlemetacs - wb);
2141 p[zlemetacs - wb] = '\0';
2142 if (lastend < zlemetacs)
2143 s = "";
2144 else {
2145 s = (char *) zhalloc(lastend - zlemetacs + 1);
2146 strncpy(s, zlemetaline + zlemetacs, lastend - zlemetacs);
2147 s[lastend - zlemetacs] = '\0';
2149 zlemetacs = 0;
2150 foredel(zlemetall, CUT_RAW);
2151 spaceinline(sll);
2152 memcpy(zlemetaline, sline, sll);
2153 zlemetacs = scs;
2154 } else {
2155 p = complastprefix;
2156 s = complastsuffix;
2158 pl = strlen(p);
2159 sl = strlen(s);
2160 max = (columns < MAX_STATUS ? columns : MAX_STATUS) - 14;
2162 if (max > 12) {
2163 int h = (max - 2) >> 1;
2165 strcpy(status, "interactive: ");
2166 if (pl > h - 3) {
2167 strcat(status, "...");
2168 strcat(status, p + pl - h - 3);
2169 } else
2170 strcat(status, p);
2172 strcat(status, "[]");
2173 if (sl > h - 3) {
2174 strncat(status, s, h - 3);
2175 strcat(status, "...");
2176 } else
2177 strcat(status, s);
2179 return ret;
2182 static Menusearch msearchstack;
2183 static char *msearchstr = NULL;
2184 static int msearchstate;
2186 static void
2187 msearchpush(Cmatch **p, int back)
2189 Menusearch s = (Menusearch) zhalloc(sizeof(struct menusearch));
2191 s->prev = msearchstack;
2192 msearchstack = s;
2193 s->str = dupstring(msearchstr);
2194 s->line = mline;
2195 s->col = mcol;
2196 s->back = back;
2197 s->state = msearchstate;
2198 s->ptr = p;
2201 static Cmatch **
2202 msearchpop(int *backp)
2204 Menusearch s = msearchstack;
2206 if (!s)
2207 return NULL;
2209 if (s->prev)
2210 msearchstack = s->prev;
2212 msearchstr = s->str;
2213 mline = s->line;
2214 mcol = s->col;
2215 msearchstate = s->state;
2217 *backp = s->back;
2219 return s->ptr;
2222 static Cmatch **
2223 msearch(Cmatch **ptr, int ins, int back, int rep, int *wrapp)
2225 #ifdef MULTIBYTE_SUPPORT
2226 /* MB_CUR_MAX may not be constant */
2227 VARARR(char, s, MB_CUR_MAX+1);
2228 #else
2229 char s[2];
2230 #endif
2231 Cmatch **p, *l = NULL, m;
2232 int x = mcol, y = mline;
2233 int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED);
2235 msearchpush(ptr, back);
2237 if (ins) {
2238 #ifdef MULTIBYTE_SUPPORT
2239 if (lastchar_wide_valid)
2241 mbstate_t mbs;
2242 int len;
2244 memset(&mbs, 0, sizeof(mbs));
2245 len = wcrtomb(s, lastchar_wide, &mbs);
2246 if (len < 0)
2247 len = 0;
2248 s[len] = '\0';
2249 } else
2250 #endif
2252 s[0] = lastchar;
2253 s[1] = '\0';
2256 msearchstr = dyncat(msearchstr, s);
2258 if (back) {
2259 ex = mcols - 1;
2260 ey = -1;
2261 } else {
2262 ex = 0;
2263 ey = listdat.nlines;
2265 p = mtab + (mline * mcols) + mcol;
2266 if (rep)
2267 l = *p;
2268 while (1) {
2269 if (!rep && mtunmark(*p) && *p != l) {
2270 l = *p;
2271 m = *mtunmark(*p);
2273 if (strstr((m->disp ? m->disp : m->str), msearchstr)) {
2274 mcol = x;
2275 mline = y;
2277 return p;
2280 rep = 0;
2282 if (back) {
2283 p--;
2284 if (--x < 0) {
2285 x = mcols - 1;
2286 y--;
2288 } else {
2289 p++;
2290 if (++x == mcols) {
2291 x = 0;
2292 y++;
2295 if (x == ex && y == ey) {
2296 if (wrap) {
2297 msearchstate = MS_FAILED | owrap;
2298 break;
2300 msearchstate |= MS_WRAPPED;
2302 if (back) {
2303 x = mcols - 1;
2304 y = listdat.nlines - 1;
2305 p = mtab + (y * mcols) + x;
2306 } else {
2307 x = y = 0;
2308 p = mtab;
2310 ex = mcol;
2311 ey = mline;
2312 wrap = 1;
2313 *wrapp = 1;
2316 return NULL;
2320 * Values to assign to mode: interactive, etc.
2322 #define MM_INTER 1
2323 #define MM_FSEARCH 2
2324 #define MM_BSEARCH 3
2326 static int
2327 domenuselect(Hookdef dummy, Chdata dat)
2329 static Chdata fdat = NULL;
2330 static char *lastsearch = NULL;
2331 Cmatch **p;
2332 Cmgroup *pg;
2333 Thingy cmd = 0;
2334 int do_last_key = 0;
2335 Menustack u = NULL;
2336 int i = 0, acc = 0, wishcol = 0, setwish = 0, oe = onlyexpl, wasnext = 0;
2337 int space, lbeg = 0, step = 1, wrap, pl = nlnct, broken = 0, first = 1;
2338 int nolist = 0, mode = 0, modecs, modell, modelen, wasmeta;
2339 char *s;
2340 char status[MAX_STATUS], *modeline = NULL;
2342 msearchstack = NULL;
2343 msearchstr = "";
2344 msearchstate = MS_OK;
2346 status[0] = '\0';
2347 queue_signals();
2348 if (fdat || (dummy && (!(s = getsparam("MENUSELECT")) ||
2349 (dat && dat->num < atoi(s))))) {
2350 if (fdat) {
2351 fdat->matches = dat->matches;
2352 fdat->num = dat->num;
2353 fdat->nmesg = dat->nmesg;
2355 unqueue_signals();
2356 return 0;
2359 * Lots of the logic here doesn't really make sense if the
2360 * line isn't metafied, but the evidence was that it only used
2361 * to be metafied locally in a couple of places.
2362 * It's horrifically difficult to work out where the line
2363 * is metafied, so I've resorted to the following.
2364 * Unfortunately we need to unmetatfy in zrefresh() when
2365 * we want to display something. Maybe this function can
2366 * be done better.
2368 if (zlemetaline != NULL)
2369 wasmeta = 1;
2370 else {
2371 wasmeta = 0;
2372 metafy_line();
2375 if ((s = getsparam("MENUSCROLL"))) {
2376 if (!(step = mathevali(s)))
2377 step = (lines - nlnct) >> 1;
2378 else if (step < 0)
2379 if ((step += lines - nlnct) < 0)
2380 step = 1;
2382 if ((s = getsparam("MENUMODE"))) {
2383 if (!strcmp(s, "interactive")) {
2384 int l = strlen(origline);
2387 * In interactive completion mode we don't insert
2388 * the completion onto the command line, instead
2389 * we show just what the user has typed and
2390 * the match so far underneath (stored in "status").
2391 * So put the command line back to how it
2392 * was before completion started.
2394 mode = MM_INTER;
2395 zlemetacs = 0;
2396 foredel(zlemetall, CUT_RAW);
2397 spaceinline(l);
2398 strncpy(zlemetaline, origline, l);
2399 zlemetacs = origcs;
2400 setmstatus(status, NULL, 0 , 0, NULL, NULL, NULL);
2401 } else if (strpfx("search", s)) {
2402 mode = (strstr(s, "back") ? MM_BSEARCH : MM_FSEARCH);
2405 if ((mstatus = dupstring(getsparam("MENUPROMPT"))) && !*mstatus)
2406 mstatus = "%SScrolling active: current selection at %p%s";
2407 unqueue_signals();
2408 mhasstat = (mstatus && *mstatus);
2409 fdat = dat;
2410 selectlocalmap(mskeymap);
2411 noselect = 1;
2412 while ((menuacc &&
2413 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) ||
2414 ((*minfo.cur)->flags & CMF_DUMMY) ||
2415 (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
2416 (!(*minfo.cur)->str || !*(*minfo.cur)->str)))
2417 do_menucmp(0);
2419 mselect = (*(minfo.cur))->gnum;
2420 mline = 0;
2421 mlines = 999999;
2422 mlbeg = 0;
2423 molbeg = -42;
2424 mtab_been_reallocated = 0;
2425 for (;;) {
2426 METACHECK();
2428 if (mline < 0 || mtab_been_reallocated) {
2429 int x, y;
2430 Cmatch **p = mtab;
2432 for (y = 0; y < mlines; y++) {
2433 for (x = mcols; x > 0; x--, p++)
2434 if (*p && !mmarked(*p) && **p && mselect == (**p)->gnum)
2435 break;
2436 if (x) {
2437 mcol = mcols - x;
2438 break;
2441 if (y < mlines)
2442 mline = y;
2444 mtab_been_reallocated = 0;
2445 DPUTS(mline < 0,
2446 "BUG: mline < 0 after re-scanning mtab in domenuselect()");
2447 while (mline < mlbeg)
2448 if ((mlbeg -= step) < 0) {
2449 mlbeg = 0;
2450 /* Crude workaround for BUG above */
2451 if (mline < 0)
2452 break;
2455 if (mlbeg && lbeg != mlbeg) {
2456 Cmatch **p = mtab + ((mlbeg - 1) * columns), **q;
2457 int c;
2459 while (mlbeg) {
2460 for (q = p, c = columns; c > 0; q++, c--)
2461 if (*q && !mmarked(*q))
2462 break;
2463 if (c)
2464 break;
2465 p -= columns;
2466 mlbeg--;
2469 if ((space = lines - pl - mhasstat))
2470 while (mline >= mlbeg + space)
2471 if ((mlbeg += step) + space > mlines)
2472 mlbeg = mlines - space;
2473 if (lbeg != mlbeg) {
2474 Cmatch **p = mtab + (mlbeg * columns), **q;
2475 int c;
2477 while (mlbeg < mlines) {
2478 for (q = p, c = columns; c > 0; q++, c--)
2479 if (*q)
2480 break;
2481 if (c)
2482 break;
2483 p += columns;
2484 mlbeg++;
2487 lbeg = mlbeg;
2488 onlyexpl = 0;
2489 showinglist = -2;
2490 if (first && !listshown && isset(LISTBEEP))
2491 zbeep();
2492 if (first) {
2494 * remember the original data that we will use when
2495 * performing interactive completion to restore the
2496 * command line when a menu completion is inserted.
2497 * this is because menu completion will insert
2498 * the next match in the loop; for interactive
2499 * completion we don't want that, we always want to
2500 * be able to type the next character.
2502 modeline = dupstring(zlemetaline);
2503 modecs = zlemetacs;
2504 modell = zlemetall;
2505 modelen = minfo.len;
2507 first = 0;
2508 if (mode == MM_INTER)
2509 statusline = status;
2510 else if (mode) {
2511 int l = sprintf(status, "%s%sisearch%s: ",
2512 ((msearchstate & MS_FAILED) ? "failed " : ""),
2513 ((msearchstate & MS_WRAPPED) ? "wrapped " : ""),
2514 (mode == MM_FSEARCH ? "" : " backward"));
2516 strncat(status, msearchstr, MAX_STATUS - l - 1);
2518 statusline = status;
2519 } else {
2520 statusline = NULL;
2522 zrefresh();
2523 statusline = NULL;
2524 inselect = 1;
2525 if (noselect) {
2526 broken = 1;
2527 break;
2529 selected = 1;
2530 if (!i) {
2531 i = mcols * mlines;
2532 while (i--)
2533 if (mtab[i])
2534 break;
2535 if (!i)
2536 break;
2537 i = 1;
2539 p = mmtabp;
2540 pg = mgtabp;
2541 minfo.cur = *p;
2542 minfo.group = *pg;
2543 if (setwish)
2544 wishcol = mcol;
2545 else if (mcol > wishcol) {
2546 while (mcol > 0 && p[-1] == minfo.cur)
2547 mcol--, p--, pg--;
2548 } else if (mcol < wishcol) {
2549 while (mcol < mcols - 1 && p[1] == minfo.cur)
2550 mcol++, p++, pg++;
2552 setwish = wasnext = 0;
2554 getk:
2556 if (!do_last_key) {
2557 cmd = getkeycmd();
2558 if (mtab_been_reallocated) {
2559 do_last_key = 1;
2560 continue;
2563 do_last_key = 0;
2565 if (!cmd || cmd == Th(z_sendbreak)) {
2566 zbeep();
2567 molbeg = -1;
2568 break;
2569 } else if (nolist && cmd != Th(z_undo) &&
2570 (!mode || (cmd != Th(z_backwarddeletechar) &&
2571 cmd != Th(z_selfinsert) &&
2572 cmd != Th(z_selfinsertunmeta)))) {
2573 ungetkeycmd();
2574 break;
2575 } else if (cmd == Th(z_acceptline)) {
2576 if (mode == MM_FSEARCH || mode == MM_BSEARCH) {
2577 mode = 0;
2578 continue;
2580 acc = 1;
2581 break;
2582 } else if (cmd == Th(z_viinsert)) {
2583 if (mode == MM_INTER)
2584 mode = 0;
2585 else {
2586 int l = strlen(origline);
2589 * Entering interactive completion mode:
2590 * same code as when we enter it on menu selection
2591 * start.
2593 mode = MM_INTER;
2594 zlemetacs = 0;
2595 foredel(zlemetall, CUT_RAW);
2596 spaceinline(l);
2597 strncpy(zlemetaline, origline, l);
2598 zlemetacs = origcs;
2599 setmstatus(status, NULL, 0, 0, NULL, NULL, NULL);
2601 continue;
2603 } else if (cmd == Th(z_acceptandinfernexthistory) ||
2604 (mode == MM_INTER && (cmd == Th(z_selfinsert) ||
2605 cmd == Th(z_selfinsertunmeta)))) {
2606 char *saveline = NULL;
2607 int savell = 0;
2608 int savecs = 0;
2609 Menustack s = (Menustack) zhalloc(sizeof(*s));
2611 s->prev = u;
2612 u = s;
2613 s->line = dupstring(zlemetaline);
2614 s->cs = zlemetacs;
2615 s->mline = mline;
2616 s->mlbeg = mlbeg;
2617 memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
2618 s->amatches = amatches;
2619 s->pmatches = pmatches;
2620 s->lastmatches = lastmatches;
2621 s->lastlmatches = lastlmatches;
2622 s->nolist = nolist;
2623 s->acc = menuacc;
2624 s->brbeg = dupbrinfo(brbeg, NULL, 1);
2625 s->brend = dupbrinfo(brend, NULL, 1);
2626 s->nbrbeg = nbrbeg;
2627 s->nbrend = nbrend;
2628 s->nmatches = nmatches;
2629 s->origline = origline;
2630 s->origcs = origcs;
2631 s->origll = origll;
2632 s->status = dupstring(status);
2634 * with just the slightest hint of a note of infuriation:
2635 * mode here is the menu mode, not the file mode, despite
2636 * the fact we're in a file dealing with file highlighting;
2637 * but that's OK, because s is a menu stack entry, despite
2638 * the fact we're in a function declaring s as char *.
2639 * anyway, in functions we really mean *mode* it's
2640 * called m, to be clear.
2642 s->mode = mode;
2643 menucmp = menuacc = hasoldlist = 0;
2644 minfo.cur = NULL;
2645 fixsuffix();
2646 handleundo();
2647 validlist = 0;
2648 amatches = pmatches = lastmatches = NULL;
2649 invalidate_list();
2650 iforcemenu = 1;
2651 comprecursive = 1;
2652 if (cmd != Th(z_acceptandinfernexthistory)) {
2653 int l = strlen(origline);
2656 * Interactive mode: we need to restore the
2657 * line, add the character, then remember how
2658 * this new line looks in order to keep
2659 * the command line as it is with just the
2660 * characters typed by the user.
2662 zlemetacs = 0;
2663 foredel(zlemetall, CUT_RAW);
2664 spaceinline(l);
2665 strncpy(zlemetaline, origline, l);
2666 zlemetacs = origcs;
2669 * Horrible quick fix:
2670 * we shouldn't need to metafy and unmetafy
2671 * quite as much. If we kept unmetafied through
2672 * here we could fix up setmstatus to use unmetafied
2673 * as well. This is the only use of setmstatus which
2674 * restores the line so that should be doable.
2676 unmetafy_line();
2677 if (cmd == Th(z_selfinsert))
2678 selfinsert(zlenoargs);
2679 else
2680 selfinsertunmeta(zlenoargs);
2681 metafy_line();
2683 saveline = (char *) zhalloc(zlemetall);
2684 memcpy(saveline, zlemetaline, zlemetall);
2685 savell = zlemetall;
2686 savecs = zlemetacs;
2687 iforcemenu = -1;
2688 } else
2689 mode = 0;
2690 /* Nested completion assumes line is unmetafied */
2691 unmetafy_line();
2692 menucomplete(zlenoargs);
2693 metafy_line();
2694 iforcemenu = 0;
2696 if (cmd != Th(z_acceptandinfernexthistory))
2697 modeline = setmstatus(status, saveline, savell, savecs,
2698 &modecs, &modell, &modelen);
2700 if (nmatches < 1 || !minfo.cur || !*(minfo.cur)) {
2701 nolist = 1;
2702 if (mode == MM_INTER) {
2703 statusline = status;
2704 } else {
2705 /* paranoia */
2706 statusline = NULL;
2708 if (nmessages) {
2709 showinglist = -2;
2710 zrefresh();
2711 } else {
2712 trashzle();
2713 zsetterm();
2714 if (tccan(TCCLEAREOD))
2715 tcout(TCCLEAREOD);
2716 fputs("no matches\r", shout);
2717 fflush(shout);
2718 tcmultout(TCUP, TCMULTUP, nlnct);
2719 showinglist = clearlist = 0;
2720 clearflag = 1;
2721 zrefresh();
2722 showinglist = clearlist = 0;
2724 statusline = NULL;
2726 goto getk;
2728 clearlist = listshown = 1;
2729 mselect = (*(minfo.cur))->gnum;
2730 setwish = wasnext = 1;
2731 mline = 0;
2732 molbeg = -42;
2733 continue;
2734 } else if (cmd == Th(z_acceptandhold) ||
2735 cmd == Th(z_acceptandmenucomplete)) {
2736 Menustack s = (Menustack) zhalloc(sizeof(*s));
2737 int ol;
2739 mode = 0;
2740 s->prev = u;
2741 u = s;
2742 s->line = dupstring(zlemetaline);
2743 s->cs = zlemetacs;
2744 s->mline = mline;
2745 s->mlbeg = mlbeg;
2746 memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
2747 s->amatches = s->pmatches =
2748 s->lastmatches = s->lastlmatches = NULL;
2749 s->nolist = nolist;
2750 s->acc = menuacc;
2751 s->brbeg = dupbrinfo(brbeg, NULL, 1);
2752 s->brend = dupbrinfo(brend, NULL, 1);
2753 s->nbrbeg = nbrbeg;
2754 s->nbrend = nbrend;
2755 s->nmatches = nmatches;
2756 s->origline = origline;
2757 s->origcs = origcs;
2758 s->origll = origll;
2759 s->status = dupstring(status);
2760 /* see above */
2761 s->mode = mode;
2762 accept_last();
2763 handleundo();
2764 comprecursive = 1;
2765 do_menucmp(0);
2766 mselect = (*(minfo.cur))->gnum;
2768 p -= mcol;
2769 mcol = 0;
2770 ol = mline;
2771 do {
2772 for (mcol = 0; mcol < mcols; mcol++, p++)
2773 if (*p == minfo.cur)
2774 break;
2775 if (mcol != mcols)
2776 break;
2777 if (++mline == mlines) {
2778 mline = 0;
2779 p -= mlines * mcols;
2781 } while (mline != ol);
2782 if (*p != minfo.cur) {
2783 noselect = clearlist = listshown = 1;
2784 onlyexpl = 0;
2785 zrefresh();
2786 break;
2788 setwish = 1;
2789 continue;
2790 } else if (cmd == Th(z_undo) ||
2791 (mode == MM_INTER && cmd == Th(z_backwarddeletechar))) {
2792 int l;
2794 if (!u)
2795 break;
2797 handleundo();
2798 zlemetacs = 0;
2799 foredel(zlemetall, CUT_RAW);
2800 spaceinline(l = strlen(u->line));
2801 strncpy(zlemetaline, u->line, l);
2802 zlemetacs = u->cs;
2803 menuacc = u->acc;
2804 memcpy(&minfo, &(u->info), sizeof(struct menuinfo));
2805 p = &(minfo.cur);
2806 mline = u->mline;
2807 mlbeg = u->mlbeg;
2808 if (u->lastmatches && lastmatches != u->lastmatches) {
2809 if (lastmatches)
2810 freematches(lastmatches, 0);
2811 amatches = u->amatches;
2812 pmatches = u->pmatches;
2813 lastmatches = u->lastmatches;
2814 lastlmatches = u->lastlmatches;
2815 nmatches = u->nmatches;
2816 hasoldlist = validlist = 1;
2818 freebrinfo(brbeg);
2819 freebrinfo(brend);
2820 brbeg = dupbrinfo(u->brbeg, &lastbrbeg, 0);
2821 brend = dupbrinfo(u->brend, &lastbrend, 0);
2822 nbrbeg = u->nbrbeg;
2823 nbrend = u->nbrend;
2824 origline = u->origline;
2825 origcs = u->origcs;
2826 origll = u->origll;
2827 strcpy(status, u->status);
2828 mode = u->mode;
2829 nolist = u->nolist;
2831 u = u->prev;
2832 clearlist = 1;
2833 setwish = 1;
2834 listdat.valid = 0;
2835 molbeg = -42;
2837 if (nolist) {
2838 if (mode == MM_INTER) {
2839 statusline = status;
2840 } else {
2841 /* paranoia */
2842 statusline = NULL;
2844 zrefresh();
2845 statusline = NULL;
2846 goto getk;
2848 if (mode)
2849 continue;
2850 } else if (cmd == Th(z_redisplay)) {
2851 redisplay(zlenoargs);
2852 molbeg = -42;
2853 continue;
2854 } else if (cmd == Th(z_clearscreen)) {
2855 clearscreen(zlenoargs);
2856 molbeg = -42;
2857 continue;
2858 } else if (cmd == Th(z_downhistory) ||
2859 cmd == Th(z_downlineorhistory) ||
2860 cmd == Th(z_downlineorsearch) ||
2861 cmd == Th(z_vidownlineorhistory)) {
2862 int omline;
2863 Cmatch **op;
2865 mode = 0;
2866 wrap = 0;
2868 down:
2870 omline = mline;
2871 op = p;
2873 do {
2874 if (mline == mlines - 1) {
2875 if (wrap & 2) {
2876 mline = omline;
2877 p = op;
2878 break;
2880 p -= mline * mcols;
2881 mline = 0;
2882 wrap |= 1;
2883 } else {
2884 mline++;
2885 p += mcols;
2887 if (adjust_mcol(wishcol, &p, NULL))
2888 continue;
2889 } while (!*p || mmarked(*p));
2891 if (wrap == 1)
2892 goto right;
2893 } else if (cmd == Th(z_uphistory) ||
2894 cmd == Th(z_uplineorhistory) ||
2895 cmd == Th(z_uplineorsearch) ||
2896 cmd == Th(z_viuplineorhistory)) {
2897 int omline;
2898 Cmatch **op;
2900 mode = 0;
2901 wrap = 0;
2905 omline = mline;
2906 op = p;
2908 do {
2909 if (!mline) {
2910 if (wrap & 2) {
2911 mline = omline;
2912 p = op;
2913 break;
2915 mline = mlines - 1;
2916 p += mline * mcols;
2917 wrap |= 1;
2918 } else {
2919 mline--;
2920 p -= mcols;
2922 if (adjust_mcol(wishcol, &p, NULL))
2923 continue;
2924 } while (!*p || mmarked(*p));
2926 if (wrap == 1) {
2927 if (mcol == wishcol)
2928 goto left;
2930 wishcol = mcol;
2932 } else if (cmd == Th(z_emacsforwardword) ||
2933 cmd == Th(z_viforwardword) ||
2934 cmd == Th(z_viforwardwordend) ||
2935 cmd == Th(z_forwardword)) {
2936 int i = lines - pl - 1, oi = i, ll = 0;
2937 Cmatch **lp = NULL;
2939 mode = 0;
2940 if (mline == mlines - 1)
2941 goto top;
2942 while (i > 0) {
2943 if (mline == mlines - 1) {
2944 if (i != oi && lp)
2945 break;
2946 goto top;
2947 } else {
2948 mline++;
2949 p += mcols;
2951 if (adjust_mcol(wishcol, &p, NULL))
2952 continue;
2953 if (*p && !mmarked(*p)) {
2954 i--;
2955 lp = p;
2956 ll = mline;
2959 p = lp;
2960 mline = ll;
2961 } else if (cmd == Th(z_emacsbackwardword) ||
2962 cmd == Th(z_vibackwardword) ||
2963 cmd == Th(z_backwardword)) {
2964 int i = lines - pl - 1, oi = i, ll = 0;
2965 Cmatch **lp = NULL;
2967 mode = 0;
2968 if (!mline)
2969 goto bottom;
2970 while (i > 0) {
2971 if (!mline) {
2972 if (i != oi && lp)
2973 break;
2974 goto bottom;
2975 } else {
2976 mline--;
2977 p -= mcols;
2979 if (adjust_mcol(wishcol, &p, NULL))
2980 continue;
2981 if (*p || !mmarked(*p)) {
2982 i--;
2983 lp = p;
2984 ll = mline;
2987 p = lp;
2988 mline = ll;
2989 } else if (cmd == Th(z_beginningofhistory)) {
2990 int ll;
2991 Cmatch **lp;
2993 mode = 0;
2995 top:
2997 ll = mline;
2998 lp = p;
2999 while (mline) {
3000 mline--;
3001 p -= mcols;
3002 if (adjust_mcol(wishcol, &p, NULL))
3003 continue;
3004 if (*p && !mmarked(*p)) {
3005 lp = p;
3006 ll = mline;
3009 mline = ll;
3010 p = lp;
3011 } else if (cmd == Th(z_endofhistory)) {
3012 int ll;
3013 Cmatch **lp;
3015 mode = 0;
3017 bottom:
3019 ll = mline;
3020 lp = p;
3021 while (mline < mlines - 1) {
3022 mline++;
3023 p += mcols;
3024 if (adjust_mcol(wishcol, &p, NULL))
3025 continue;
3026 if (*p && !mmarked(*p)) {
3027 lp = p;
3028 ll = mline;
3031 mline = ll;
3032 p = lp;
3033 } else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
3034 int omcol;
3035 Cmatch **op;
3037 mode = 0;
3038 wrap = 0;
3040 right:
3042 omcol = mcol;
3043 op = p;
3045 do {
3046 if (mcol == mcols - 1) {
3047 if (wrap & 1) {
3048 p = op;
3049 mcol = omcol;
3050 break;
3052 p -= mcol;
3053 mcol = 0;
3054 wrap |= 2;
3055 } else {
3056 mcol++;
3057 p++;
3059 } while (!*p || mmarked(*p) || (mcol != omcol && *p == *op));
3060 wishcol = mcol;
3062 if (wrap == 2)
3063 goto down;
3064 } else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) {
3065 int omcol;
3066 Cmatch **op;
3068 mode = 0;
3069 wrap = 0;
3071 left:
3073 omcol = mcol;
3074 op = p;
3076 do {
3077 if (!mcol) {
3078 if (wrap & 1) {
3079 p = op;
3080 mcol = omcol;
3081 break;
3083 mcol = mcols - 1;
3084 p += mcol;
3085 wrap |= 2;
3086 } else {
3087 mcol--;
3088 p--;
3090 } while (!*p || mmarked(*p) || (mcol != omcol && *p == *op));
3091 wishcol = mcol;
3093 if (wrap == 2) {
3094 p += mcols - 1 - mcol;
3095 wishcol = mcol = mcols - 1;
3096 adjust_mcol(wishcol, &p, NULL);
3097 goto up;
3099 } else if (cmd == Th(z_beginningofbufferorhistory) ||
3100 cmd == Th(z_beginningofline) ||
3101 cmd == Th(z_beginningoflinehist) ||
3102 cmd == Th(z_vibeginningofline)) {
3103 mode = 0;
3104 p -= mcol;
3105 mcol = 0;
3106 while (!*p || mmarked(*p)) {
3107 mcol++;
3108 p++;
3110 wishcol = 0;
3111 } else if (cmd == Th(z_endofbufferorhistory) ||
3112 cmd == Th(z_endofline) ||
3113 cmd == Th(z_endoflinehist) ||
3114 cmd == Th(z_viendofline)) {
3115 mode = 0;
3116 p += mcols - mcol - 1;
3117 mcol = mcols - 1;
3118 while (!*p || mmarked(*p)) {
3119 mcol--;
3120 p--;
3122 wishcol = mcols - 1;
3123 } else if (cmd == Th(z_viforwardblankword) ||
3124 cmd == Th(z_viforwardblankwordend)) {
3125 Cmgroup g = *pg;
3126 int ol = mline;
3128 mode = 0;
3129 do {
3130 if (mline == mlines - 1) {
3131 p -= mline * mcols;
3132 pg -= mline * mcols;
3133 mline = 0;
3134 } else {
3135 mline++;
3136 p += mcols;
3137 pg += mcols;
3139 if (adjust_mcol(wishcol, &p, &pg))
3140 continue;
3141 } while (ol != mline && (*pg == g || !*pg || mmarked(*pg)));
3142 } else if (cmd == Th(z_vibackwardblankword)) {
3143 Cmgroup g = *pg;
3144 int ol = mline;
3146 mode = 0;
3147 do {
3148 if (!mline) {
3149 mline = mlines - 1;
3150 p += mline * mcols;
3151 pg += mline * mcols;
3152 } else {
3153 mline--;
3154 p -= mcols;
3155 pg -= mcols;
3157 if (adjust_mcol(wishcol, &p, &pg))
3158 continue;
3159 } while (ol != mline && (*pg == g || !*pg || mmarked(*pg)));
3160 } else if (cmd == Th(z_completeword) ||
3161 cmd == Th(z_expandorcomplete) ||
3162 cmd == Th(z_expandorcompleteprefix) ||
3163 cmd == Th(z_menucomplete) ||
3164 cmd == Th(z_menuexpandorcomplete) ||
3165 !strcmp(cmd->nam, "menu-select") ||
3166 !strcmp(cmd->nam, "complete-word") ||
3167 !strcmp(cmd->nam, "expand-or-complete") ||
3168 !strcmp(cmd->nam, "expand-or-complete-prefix") ||
3169 !strcmp(cmd->nam, "menu-complete") ||
3170 !strcmp(cmd->nam, "menu-expand-or-complete")) {
3171 if (mode == MM_INTER) {
3173 * do_menucmp() has inserted the completion onto
3174 * the command line. In interactive mode we
3175 * don't want that, just what the user typed,
3176 * so restore the information.
3178 origline = modeline;
3179 origcs = modecs;
3180 origll = modell;
3181 zlemetacs = 0;
3182 foredel(zlemetall, CUT_RAW);
3183 spaceinline(origll);
3184 strncpy(zlemetaline, origline, origll);
3185 zlemetacs = origcs;
3186 minfo.len = modelen;
3187 } else {
3188 mode = 0;
3189 comprecursive = 1;
3190 do_menucmp(0);
3191 mselect = (*(minfo.cur))->gnum;
3192 setwish = 1;
3193 mline = -1;
3195 continue;
3196 } else if (cmd == Th(z_reversemenucomplete) ||
3197 !strcmp(cmd->nam, "reverse-menu-complete")) {
3198 mode = 0;
3199 comprecursive = 1;
3200 reversemenucomplete(zlenoargs);
3201 mselect = (*(minfo.cur))->gnum;
3202 setwish = 1;
3203 mline = -1;
3204 continue;
3205 } else if (cmd == Th(z_historyincrementalsearchforward) ||
3206 cmd == Th(z_historyincrementalsearchbackward) ||
3207 ((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
3208 (cmd == Th(z_selfinsert) ||
3209 cmd == Th(z_selfinsertunmeta)))) {
3210 Cmatch **np, **op = p;
3211 int was = (mode == MM_FSEARCH || mode == MM_BSEARCH);
3212 int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta));
3213 int back = (cmd == Th(z_historyincrementalsearchbackward));
3214 int wrap;
3216 do {
3217 if (was) {
3218 p += wishcol - mcol;
3219 mcol = wishcol;
3221 if (!ins) {
3222 if (was) {
3223 if (!*msearchstr && lastsearch) {
3224 msearchstr = dupstring(lastsearch);
3225 mode = 0;
3227 } else {
3228 msearchstr = "";
3229 msearchstack = NULL;
3232 if (cmd == Th(z_selfinsertunmeta)) {
3233 fixunmeta();
3235 wrap = 0;
3236 np = msearch(p, ins, (ins ? (mode == MM_BSEARCH) : back),
3237 (was && !ins), &wrap);
3239 if (!ins)
3240 mode = (back ? MM_BSEARCH : MM_FSEARCH);
3242 if (*msearchstr) {
3243 zsfree(lastsearch);
3244 lastsearch = ztrdup(msearchstr);
3246 if (np) {
3247 wishcol = mcol;
3248 p = np;
3250 adjust_mcol(wishcol, &p, NULL);
3252 } while ((back || cmd == Th(z_historyincrementalsearchforward)) &&
3253 np && !wrap && was && **p == **op);
3255 } else if ((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
3256 cmd == Th(z_backwarddeletechar)) {
3257 int back = 1;
3258 Cmatch **np = msearchpop(&back);
3260 mode = (back ? MM_BSEARCH : MM_FSEARCH);
3261 wishcol = mcol;
3262 if (np) {
3263 p = np;
3264 adjust_mcol(wishcol, &p, NULL);
3266 } else if (cmd == Th(z_undefinedkey)) {
3267 mode = 0;
3268 continue;
3269 } else {
3270 ungetkeycmd();
3271 if (cmd->widget && (cmd->widget->flags & WIDGET_NCOMP)) {
3272 acc = 0;
3273 broken = 2;
3274 } else
3275 acc = 1;
3276 break;
3278 do_single(**p);
3279 mselect = (**p)->gnum;
3281 if (u)
3282 for (; u; u = u->prev)
3283 if (u->lastmatches != lastmatches)
3284 freematches(u->lastmatches, 0);
3286 selectlocalmap(NULL);
3287 mselect = mlastcols = mlastlines = -1;
3288 mstatus = NULL;
3289 inselect = mhasstat = 0;
3290 if (nolist)
3291 clearlist = listshown = 1;
3292 if (acc && validlist && minfo.cur) {
3293 menucmp = lastambig = hasoldlist = 0;
3294 do_single(*(minfo.cur));
3296 if (wasnext || broken) {
3297 menucmp = 2;
3298 showinglist = ((validlist && !nolist) ? -2 : 0);
3299 minfo.asked = 0;
3300 if (!noselect) {
3301 int nos = noselect;
3303 zrefresh();
3304 noselect = nos;
3307 if (!noselect && (!dat || acc)) {
3309 * I added the following because in certain cases the zrefresh()
3310 * here was screwing up the list. Forcing it to redraw the
3311 * screen worked. The case in question (courtesy of
3312 * "Matt Wozniski" <godlygeek@gmail.com>) is in zsh-workers/24756.
3314 * *** PLEASE DON'T ASK ME WHY THIS IS NECESSARY ***
3316 mlbeg = -1;
3317 showinglist = ((validlist && !nolist) ? -2 : 0);
3318 onlyexpl = oe;
3319 if (!smatches)
3320 clearlist = listshown = 1;
3321 zrefresh();
3323 mlbeg = -1;
3324 fdat = NULL;
3326 if (!wasmeta)
3327 unmetafy_line();
3329 return (broken == 2 ? 3 :
3330 ((dat && !broken) ? (acc ? 1 : 2) : (!noselect ^ acc)));
3333 /* The widget function. */
3335 static int
3336 menuselect(char **args)
3338 int d = 0;
3340 if (!minfo.cur) {
3341 selected = 0;
3342 menucomplete(args);
3343 if ((minfo.cur && minfo.asked == 2) || selected)
3344 return 0;
3345 d = 1;
3347 if (minfo.cur && (minfo.asked == 2 || domenuselect(NULL, NULL)) && !d)
3348 menucomplete(args);
3350 return 0;
3353 static struct features module_features = {
3354 NULL, 0,
3355 NULL, 0,
3356 NULL, 0,
3357 NULL, 0,
3361 /**/
3363 setup_(UNUSED(Module m))
3365 return 0;
3368 /**/
3370 features_(Module m, char ***features)
3372 *features = featuresarray(m, &module_features);
3373 return 0;
3376 /**/
3378 enables_(Module m, int **enables)
3380 return handlefeatures(m, &module_features, enables);
3383 /**/
3385 boot_(Module m)
3387 mtab = NULL;
3388 mgtab = NULL;
3389 mselect = -1;
3390 inselect = 0;
3392 w_menuselect = addzlefunction("menu-select", menuselect,
3393 ZLE_MENUCMP|ZLE_KEEPSUFFIX|ZLE_ISCOMP);
3394 if (!w_menuselect) {
3395 zwarnnam(m->node.nam,
3396 "name clash when adding ZLE function `menu-select'");
3397 return -1;
3399 addhookfunc("comp_list_matches", (Hookfn) complistmatches);
3400 addhookfunc("menu_start", (Hookfn) domenuselect);
3401 mskeymap = newkeymap(NULL, "menuselect");
3402 linkkeymap(mskeymap, "menuselect", 1);
3403 bindkey(mskeymap, "\t", refthingy(t_completeword), NULL);
3404 bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL);
3405 bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL);
3406 bindkey(mskeymap, "\33[A", refthingy(t_uplineorhistory), NULL);
3407 bindkey(mskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
3408 bindkey(mskeymap, "\33[C", refthingy(t_forwardchar), NULL);
3409 bindkey(mskeymap, "\33[D", refthingy(t_backwardchar), NULL);
3410 bindkey(mskeymap, "\33OA", refthingy(t_uplineorhistory), NULL);
3411 bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
3412 bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL);
3413 bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL);
3414 lskeymap = newkeymap(NULL, "listscroll");
3415 linkkeymap(lskeymap, "listscroll", 1);
3416 bindkey(lskeymap, "\t", refthingy(t_completeword), NULL);
3417 bindkey(lskeymap, " ", refthingy(t_completeword), NULL);
3418 bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL);
3419 bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL);
3420 bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
3421 bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
3422 return 0;
3425 /**/
3427 cleanup_(Module m)
3429 free(mtab);
3430 free(mgtab);
3432 deletezlefunction(w_menuselect);
3433 deletehookfunc("comp_list_matches", (Hookfn) complistmatches);
3434 deletehookfunc("menu_start", (Hookfn) domenuselect);
3435 unlinkkeymap("menuselect", 1);
3436 unlinkkeymap("listscroll", 1);
3437 return setfeatureenables(m, &module_features, NULL);
3440 /**/
3442 finish_(UNUSED(Module m))
3444 return 0;