2 * complist.c - completion listing enhancements
4 * This file is part of zsh, the Z shell.
6 * Copyright (c) 1999 Sven Wischnowsky
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
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
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,
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
;
113 * Allow us to keep track of pointer arithmetic for mgtab; could
114 * just as well have been for mtab but wasn't.
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. */
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
];
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
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. */
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
;
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
;
225 Patprog pat
; /* pattern for match */
226 char *cols
[MAX_POS
+ 1];
230 /* This describes a terminal string for a filename extension. */
232 typedef struct extcol
*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 */
247 /* ln=target: follow symlinks to determine highlighting */
248 LC_FOLLOW_SYMLINKS
= 0x0001
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. */
273 getcolval(char *s
, int multi
)
277 for (p
= s
; *s
&& *s
!= ':' && (!multi
|| *s
!= '='); p
++, s
++) {
278 if (*s
== '\\' && s
[1]) {
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;
291 if (*s
>= '0' && *s
<= '7') {
294 if (*++s
>= '0' && *s
<= '7') {
295 i
= (i
* 8) + STOUC(*s
);
296 if (*++s
>= '0' && *s
<= '7')
297 i
= (i
* 8) + STOUC(*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] == '?')
319 if ((s
- o
) > max_caplen
)
324 /* This parses one definition. Return value is a pointer to the
325 * character after it. */
330 Patprog gprog
= NULL
;
336 for (p
= s
+ 1, l
= 0; *p
&& (*p
!= ')' || l
); p
++)
337 if (*p
== '\\' && p
[1])
349 gprog
= patcompile(s
, 0, NULL
);
359 /* This is for an extension. */
362 while (*s
&& *s
!= '=')
368 ec
= (Extcol
) zhalloc(sizeof(*ec
));
373 if ((eo
= mcolors
.exts
)) {
382 } else if (*s
== '=') {
383 char *p
= ++s
, *t
, *cols
[MAX_POS
];
387 /* This is for a pattern. */
389 while (*s
&& *s
!= '=')
404 if ((prog
= patcompile(p
, 0, NULL
))) {
408 pc
= (Patcol
) zhalloc(sizeof(*pc
));
411 for (i
= 0; i
< ncols
; i
++)
412 pc
->cols
[i
] = cols
[i
];
415 if ((po
= mcolors
.pats
)) {
426 char *n
= s
, *p
, **nn
;
429 /* This is for a file type. */
431 while (*s
&& *s
!= '=')
436 for (i
= 0, nn
= colnames
; *nn
; i
++, nn
++)
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
;
451 fc
= (Filecol
) zhalloc(sizeof(*fc
));
452 fc
->prog
= (i
== COL_EC
|| i
== COL_LC
|| i
== COL_RC
?
456 if ((fo
= mcolors
.files
[i
])) {
461 mcolors
.files
[i
] = fc
;
475 fc
= (Filecol
) zhalloc(sizeof(*fc
));
484 * This initializes the given terminal color structure.
493 max_caplen
= lr_caplen
= 0;
496 if (!(s
= getsparam("ZLS_COLORS")) &&
497 !(s
= getsparam("ZLS_COLOURS"))) {
498 for (i
= 0; i
< NUM_COLS
; i
++)
499 mcolors
.files
[i
] = filecol("");
503 if ((s
= tcstr
[TCSTANDOUTBEG
]) && s
[0]) {
504 mcolors
.files
[COL_MA
] = filecol(s
);
505 mcolors
.files
[COL_EC
] = filecol(tcstr
[TCSTANDOUTEND
]);
507 mcolors
.files
[COL_MA
] = filecol(defcols
[COL_MA
]);
509 if ((max_caplen
= strlen(mcolors
.files
[COL_MA
]->col
)) <
510 (l
= strlen(mcolors
.files
[COL_EC
]->col
)))
515 /* Reset the global color structure. */
516 memset(&mcolors
, 0, sizeof(mcolors
));
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
)
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
];
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
);
554 strcat(buf
, mcolors
.files
[COL_RC
]->col
);
556 tputs(buf
, 1, putshout
);
558 strcpy(last_cap
, cap
);
563 zcputs(char *group
, int colour
)
567 for (fc
= mcolors
.files
[colour
]; fc
; fc
= fc
->next
)
569 (!fc
->prog
|| !group
|| pattry(fc
->prog
, group
))) {
577 /* Turn off colouring. */
582 if (mcolors
.files
[COL_EC
] && mcolors
.files
[COL_EC
]->col
) {
583 tputs(mcolors
.files
[COL_EC
]->col
, 1, putshout
);
586 zcputs(NULL
, COL_NO
);
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;
612 while (pos
> sendpos
[curissend
]) {
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
) {
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];
632 zcputs(NULL
, COL_NO
);
634 curiscols
[++curiscol
] = *patcols
;
641 /* Stripped-down version of printfmt(). But can do in-string colouring. */
644 clprintfmt(char *p
, int ml
)
646 int cc
= 0, i
= 0, ask
, beg
;
654 if (mlbeg
>= 0 && tccan(TCCLEAREOL
))
658 if (ml
== mlend
- 1 && (cc
% columns
) == columns
- 1)
663 putc(*p
^ 32, shout
);
666 if ((beg
= !(cc
% columns
)))
668 if (mscroll
&& !(cc
% columns
) &&
669 !--mrestlines
&& (ask
= asklistscroll(ml
)))
672 if (mlbeg
>= 0 && tccan(TCCLEAREOL
))
678 * Local version of nicezputs() with in-string colouring
683 clnicezputs(int do_colors
, char *s
, int ml
)
685 int i
= 0, col
= 0, ask
, oml
= ml
;
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
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;
708 memset(&mbs
, 0, sizeof mbs
);
711 uptr
= unmetafy(ums
, ¨en
);
719 size_t cnt
= eol
? MB_INVALID
: mbrtowc(&cc
, uptr
, umleft
, &mbs
);
726 /* This handles byte values that aren't valid wide-character
728 sptr
= nicechar(*uptr
);
729 /* everything here is ASCII... */
730 width
= strlen(sptr
);
733 /* Get mbs out of its undefined state. */
734 memset(&mbs
, 0, sizeof mbs
);
737 /* This handles a '\0' in the input (which is a real char
738 * to us, not a terminator). */
742 sptr
= wcs_nicechar(cc
, &width
, &wptr
);
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.
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
;
772 /* outputting ASCII, so single-width */
777 /* outputting a single wide character, do the lot */
779 /* don't check column until finished */
782 /* now we've done the entire rest of the representation */
786 * There might be problems with characters of printing width
787 * greater than one here.
791 if (mscroll
&& !--mrestlines
&& (ask
= asklistscroll(ml
))) {
792 mlprinted
= ml
- oml
;
797 fputs(" \010", shout
);
808 while ((cc
= *s
++)) {
813 cc
= ztokens
[cc
- Pound
];
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
;
827 if (++col
> columns
) {
829 if (mscroll
&& !--mrestlines
&& (ask
= asklistscroll(ml
))) {
830 mlprinted
= ml
- oml
;
835 fputs(" \010", shout
);
840 mlprinted
= ml
- oml
;
844 /* Get the terminal color string for the given match. */
847 putmatchcol(char *group
, char *n
)
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
)) {
861 zlrputs(pc
->cols
[0]);
866 zcputs(group
, COL_NO
);
871 /* Get the terminal color string for the file with the given name and
875 putfilecol(char *group
, char *n
, mode_t m
, int special
)
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
)) {
891 zlrputs(pc
->cols
[0]);
898 } else if (S_ISDIR(m
)) {
904 else if (m
& S_ISVTX
)
908 } else if (S_ISLNK(m
))
910 else if (S_ISFIFO(m
))
912 else if (S_ISSOCK(m
))
918 else if (m
& S_ISUID
)
920 else if (m
& S_ISGID
)
922 else if (S_ISREG(m
) && (m
& S_IXUGO
))
926 zcputs(group
, colour
);
930 for (ec
= mcolors
.exts
; ec
; ec
= ec
->next
)
931 if (strsfx(ec
->ext
, n
) &&
932 (!ec
->prog
|| !group
|| pattry(ec
->prog
, group
))) {
938 zcputs(group
, COL_FI
);
943 static Cmgroup last_group
;
947 asklistscroll(int ml
)
952 compprintfmt(NULL
, 1, 1, 1, ml
, NULL
);
956 selectlocalmap(lskeymap
);
957 if (!(cmd
= getkeycmd()) || cmd
== Th(z_sendbreak
))
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
))
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;
981 selectlocalmap(NULL
);
982 settyinfo(&shttyinfo
);
984 for (i
= columns
- 1; i
-- > 0; )
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)
1001 if (mlbeg
>= 0 && tccan(TCCLEAREOL
))
1005 if (mscroll
&& !--mrestlines
&& (ask
= asklistscroll(ml
)))
1011 /* This is used to print the strings (e.g. explanations). *
1012 * It returns the number of lines printed. */
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
)) {
1023 if (!(fmt
= mstatus
)) {
1032 for (p
= fmt
; *p
; ) {
1036 len
= MB_METACHARLENCONV(p
, &cchar
);
1037 #ifdef MULTIBYTE_SUPPORT
1038 if (cchar
== WEOF
) {
1039 cchar
= (wchar_t)(*p
== Meta
? p
[1] ^ 32 : *p
);
1044 width
= WCWIDTH_WINT(cchar
);
1046 if (doesc
&& cchar
== ZWC('%')) {
1051 len
= MB_METACHARLENCONV(p
, &cchar
);
1052 #ifdef MULTIBYTE_SUPPORT
1054 cchar
= (wchar_t)(*p
== Meta
? p
[1] ^ 32 : *p
);
1059 arg
= zstrtol(p
, &p
, 10);
1070 sprintf(nc
, "%d", n
);
1073 /* everything here is ASCII... */
1080 tcout(TCBOLDFACEBEG
);
1085 tcout(TCALLATTRSOFF
);
1090 tcout(TCSTANDOUTBEG
);
1095 tcout(TCSTANDOUTEND
);
1100 tcout(TCUNDERLINEBEG
);
1105 tcout(TCUNDERLINEEND
);
1109 is_fg
= (cchar
== ZWC('F'));
1110 /* colours must be ASCII */
1113 arg
= match_colour((const char **)&p
, is_fg
, 0);
1117 arg
= match_colour(NULL
, is_fg
, arg
);
1118 if (arg
>= 0 && dopr
)
1119 set_colour_attribute(arg
, is_fg
? COL_SEQ_FG
:
1124 set_colour_attribute(TXTNOFGCOLOUR
, COL_SEQ_FG
, 0);
1128 set_colour_attribute(TXTNOBGCOLOUR
, COL_SEQ_BG
, 0);
1133 for (; *p
&& (*p
!= '%' || p
[1] != '}'); p
++)
1135 putc(*p
== Meta
? *++p
^ 32 : *p
, shout
);
1141 sprintf(nc
, "%d/%d", (n
? mlastm
: mselect
),
1148 sprintf(nbuf
, "%d/%d", (n
? mlastm
: mselect
),
1150 sprintf(nc
, "%-9s", nbuf
);
1156 sprintf(nc
, "%d/%d", ml
+ 1, listdat
.nlines
);
1162 sprintf(nbuf
, "%d/%d", ml
+ 1, listdat
.nlines
);
1163 sprintf(nc
, "%-9s", nbuf
);
1169 if (ml
== listdat
.nlines
- 1)
1170 strcpy(nc
, "Bottom");
1171 else if (n
? mfirstl
: (mlbeg
> 0 || ml
!= mfirstl
))
1173 ((ml
+ 1) * 100) / listdat
.nlines
);
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
);
1192 if (m
== 2 && dopr
== 1) {
1193 /* nc only contains ASCII text */
1196 if (l
+ cc
> columns
- 2)
1197 nc
[l
-= l
+ cc
- (columns
- 2)] = '\0';
1200 } else if (dopr
&& m
== 1) {
1202 tcout(TCBOLDFACEBEG
);
1204 tcout(TCSTANDOUTBEG
);
1206 tcout(TCUNDERLINEBEG
);
1213 if ((cc
>= columns
- 2 || cchar
== ZWC('\n')) && stat
)
1215 if (cchar
== ZWC('\n')) {
1216 if (dopr
== 1 && mlbeg
>= 0 && tccan(TCCLEAREOL
))
1218 l
+= 1 + ((cc
- 1) / columns
);
1222 if (ml
== mlend
- 1 && (cc
% columns
) == columns
- 1) {
1231 putc(*p
++ ^ 32, shout
);
1236 * TODO: the following doesn't allow for
1237 * character widths greater than 1.
1239 if ((beg
= !(cc
% columns
)) && !stat
) {
1241 fputs(" \010", shout
);
1243 if (mscroll
&& beg
&& !--mrestlines
&& (ask
= asklistscroll(ml
))) {
1247 mlprinted
= l
+ (cc
? ((cc
-1) / columns
) : 0);
1256 if (!(cc
% columns
))
1257 fputs(" \010", shout
);
1258 if (mlbeg
>= 0 && tccan(TCCLEAREOL
))
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
);
1272 /* This is like zputs(), but allows scrolling. */
1276 compzputs(char const *s
, int ml
)
1278 int c
, col
= 0, ask
;
1290 if (c
== '\n' && mlbeg
>= 0 && tccan(TCCLEAREOL
))
1292 if (mscroll
&& (++col
== columns
|| c
== '\n')) {
1294 if (!--mrestlines
&& (ask
= asklistscroll(ml
)))
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
;
1316 int pnl
= 0, cl
, mc
= 0, ml
= 0, printed
= 0, stop
= 0, asked
= 1;
1320 if (mnew
|| lastinvcount
!= invcount
|| lastbeg
!= mlbeg
|| mlbeg
< 0) {
1327 cl
= (listdat
.nlines
> lines
- nlnct
- mhasstat
?
1328 lines
- nlnct
- mhasstat
: listdat
.nlines
) - (lastnlnct
> nlnct
);
1330 mrestlines
= lines
- 1;
1331 lastinvcount
= invcount
;
1335 if (tccan(TCCLEAREOD
))
1337 } else if (mlbeg
>= 0 && !tccan(TCCLEAREOL
) && tccan(TCCLEAREOD
))
1340 g
= ((lasttype
&& lastg
) ? lastg
: amatches
);
1342 char **pp
= g
->ylist
;
1344 if ((e
= g
->expls
)) {
1347 if (!lastused
&& lasttype
== 1) {
1353 if (((*e
)->count
|| (*e
)->always
) &&
1354 (!listdat
.onlyexpl
||
1355 (listdat
.onlyexpl
& ((*e
)->always
> 0 ? 2 : 1)))) {
1357 if (dolistnl(ml
) && compprintnl(ml
))
1361 if (dolistcl(ml
) && cl
>= 0 && --cl
<= 1) {
1363 if (tccan(TCCLEAREOD
))
1367 if (mlbeg
< 0 && mfirstl
< 0)
1369 l
= compprintfmt((*e
)->str
,
1370 ((*e
)->always
? -1 : (*e
)->count
),
1371 dolist(ml
), 1, ml
, &stop
);
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
);
1383 if (!lasttype
&& ml
>= mlbeg
) {
1393 if (dolistcl(ml
) && cl
>= 0 && (cl
-= mlprinted
) <= 1) {
1395 if (tccan(TCCLEAREOD
))
1401 if (!mnew
&& ml
> mlend
)
1405 if (!listdat
.onlyexpl
&& mlbeg
< 0 && pp
&& *pp
) {
1407 if (dolistnl(ml
) && compprintnl(ml
))
1411 if (cl
>= 0 && --cl
<= 1) {
1413 if (tccan(TCCLEAREOD
))
1417 if (mlbeg
< 0 && mfirstl
< 0)
1419 if (g
->flags
& CGF_LINES
) {
1421 if (compzputs(*pp
, ml
))
1423 if (*++pp
&& compprintnl(ml
))
1427 int n
= g
->lcount
, nl
, nc
, i
, a
;
1437 if (pq
- g
->ylist
>= g
->lcount
)
1439 if (compzputs(*pq
, mscroll
))
1442 a
= (g
->widths
? g
->widths
[mc
] : g
->width
) -
1447 pq
+= ((g
->flags
& CGF_ROWS
) ? 1 : nc
);
1452 if (compprintnl(ml
))
1455 if (cl
>= 0 && --cl
<= 1) {
1457 if (tccan(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
;
1471 if ((g
->flags
& CGF_HASDL
) &&
1472 (lastused
|| !lasttype
|| lasttype
== 2)) {
1473 if (!lastused
&& lasttype
== 2) {
1483 for (; (m
= *p
); p
++) {
1484 if (m
->disp
&& (m
->flags
& CMF_DISPLINE
) &&
1485 (showall
|| !(m
->flags
& (CMF_HIDE
|CMF_NOLIST
)))) {
1487 if (dolistnl(ml
) && compprintnl(ml
))
1491 if (dolistcl(ml
) && cl
>= 0 && --cl
<= 1) {
1493 if (tccan(TCCLEAREOD
))
1497 if (!lasttype
&& ml
>= mlbeg
) {
1511 if (clprintm(g
, p
, 0, ml
, 1, 0))
1514 if (dolistcl(ml
) && (cl
-= mlprinted
) <= 1) {
1516 if (tccan(TCCLEAREOD
))
1521 if (!mnew
&& ml
> mlend
)
1526 if (dolistnl(ml
) && compprintnl(ml
))
1530 if (dolistcl(ml
) && cl
>= 0 && --cl
<= 1) {
1532 if (tccan(TCCLEAREOD
))
1536 if (!lastused
&& lasttype
== 3) {
1543 p
= skipnolist(g
->matches
, showall
);
1546 if (!lasttype
&& ml
>= mlbeg
) {
1560 wid
= (g
->widths
? g
->widths
[mc
] : g
->width
);
1562 if (clprintm(g
, NULL
, mc
, ml
, (!i
), wid
))
1566 if (clprintm(g
, q
, mc
, ml
, (!i
), wid
))
1572 if (dolistcl(ml
) && (cl
-= mlprinted
) < 1) {
1574 if (tccan(TCCLEAREOD
))
1581 for (j
= ((g
->flags
& CGF_ROWS
) ? 1 : nc
);
1583 q
= skipnolist(q
+ 1, showall
);
1587 if (clprintm(g
, NULL
, mc
, ml
, (!i
),
1588 (g
->widths
? g
->widths
[mc
] : g
->width
)))
1593 if (dolistnl(ml
) && compprintnl(ml
))
1596 if (dolistcl(ml
) && cl
>= 0 && --cl
<= 1) {
1598 if (tccan(TCCLEAREOD
))
1602 for (j
= ((g
->flags
& CGF_ROWS
) ? g
->cols
: 1);
1604 p
= skipnolist(p
+ 1, showall
);
1606 if (!mnew
&& ml
> mlend
)
1610 if (g
->lcount
|| (showall
&& g
->mcount
))
1623 /* Move the cursor up to the prompt, if always_last_prompt *
1624 * is set and all that... */
1626 if ((nl
= listdat
.nlines
+ nlnct
) >= lines
) {
1629 compprintfmt(NULL
, 0, 1, 1, mline
, NULL
);
1635 tcmultout(TCUP
, TCMULTUP
, nl
);
1638 lastlistlen
= listdat
.nlines
;
1639 } else if ((nl
= listdat
.nlines
+ nlnct
- 1) < lines
) {
1640 if (mlbeg
>= 0 && tccan(TCCLEAREOL
))
1642 tcmultout(TCUP
, TCMULTUP
, nl
);
1645 lastlistlen
= listdat
.nlines
;
1649 mrestlines
= (ml
+ nlnct
> lines
);
1653 } else if (!asked
) {
1654 mrestlines
= (ml
+ nlnct
> lines
);
1657 listshown
= (clearflag
? 1 : -1);
1665 clprintm(Cmgroup g
, Cmatch
*mp
, int mc
, int ml
, int lastc
, int width
)
1668 int len
, subcols
= 0, stop
= 0, ret
= 0;
1670 DPUTS2(mselect
>= 0 && ml
>= mlines
,
1671 "clprintm called with ml too large (%d/%d)",
1673 if (g
!= last_group
)
1680 zcputs(g
->name
, COL_SP
);
1691 if ((m
->flags
& CMF_ALL
) && (!m
->disp
|| !m
->disp
[0]))
1695 if (m
->disp
&& (m
->flags
& CMF_DISPLINE
)) {
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
);
1706 for (i
= mcols
; i
-- > 0; ) {
1707 DPUTS(mm
+i
>= mgtabsize
, "BUG: invalid position");
1714 mlprinted
= printfmt(m
->disp
, 0, 0, 0);
1717 if (m
->gnum
== mselect
) {
1718 int mm
= (mcols
* ml
);
1719 DPUTS(mm
>= mgtabsize
, "BUG: invalid position");
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
);
1732 subcols
= putmatchcol(g
->name
, m
->disp
);
1734 ret
= clprintfmt(m
->disp
, ml
);
1736 compprintfmt(m
->disp
, 0, 1, 0, ml
, &stop
);
1747 for (i
= mx
= 0; i
< mc
; i
++)
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
);
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
;
1770 int nc
= ZMB_nicewidth(m
->disp
? m
->disp
: m
->str
);
1772 mlprinted
= (nc
-1) / columns
;
1777 if (m
->gnum
== mselect
) {
1778 int mm
= mcols
* ml
;
1779 DPUTS(mx
+mm
>= mgtabsize
, "BUG: invalid position");
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
);
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
);
1801 subcols
= putfilecol(g
->name
, m
->str
, m
->mode
, orphan_colour
);
1805 subcols
= putmatchcol(g
->name
, (m
->disp
? m
->disp
: m
->str
));
1807 ret
= clnicezputs(subcols
,
1808 (m
->disp
? m
->disp
: m
->str
), ml
);
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
) {
1820 zcputs(g
->name
, COL_TC
);
1825 if ((len
= width
- len
- 2) > 0) {
1826 if (m
->gnum
!= mselect
) {
1828 zcputs(g
->name
, COL_SP
);
1835 zcputs(g
->name
, COL_SP
);
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
--) {
1852 if (!first
&& *p
!= op
)
1859 for (p
= mtab
+ l
* columns
+ c
; c
< columns
; c
++, p
++)
1870 int mc1
, mc2
, ml1
, ml2
, md1
, md2
, mcc1
, mcc2
, lc1
, lc2
, t1
, t2
;
1873 t2
= moline
- molbeg
;
1876 mc1
= mocol
; ml1
= moline
; md1
= t2
;
1877 mc2
= mcol
; ml2
= mline
; md2
= t1
;
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
);
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
));
1894 (void) tcmultout(TCUP
, TCMULTUP
, mlprinted
);
1898 tc_downcurs(md2
- md1
);
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
));
1906 (void) tcmultout(TCUP
, TCMULTUP
, mlprinted
);
1910 int i
= lines
- md2
- nlnct
;
1913 compprintfmt(NULL
, 0, 1, 1, mline
, NULL
);
1914 tcmultout(TCUP
, TCMULTUP
, lines
- 1);
1916 tcmultout(TCUP
, TCMULTUP
, md2
+ nlnct
);
1923 complistmatches(UNUSED(Hookdef dummy
), Chdata dat
)
1925 static int onlnct
= -1;
1926 static int extendedglob
;
1928 Cmgroup oamatches
= amatches
;
1930 amatches
= dat
->matches
;
1934 if ((minfo
.asked
== 2 && mselect
< 0) || nlnct
>= lines
) {
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
1949 extendedglob
= opts
[EXTENDEDGLOB
];
1950 opts
[EXTENDEDGLOB
] = 1;
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;
1962 amatches
= oamatches
;
1964 opts
[EXTENDEDGLOB
] = extendedglob
;
1967 if (inselect
|| mlbeg
>= 0)
1974 if (mselect
>= 0 || mlbeg
>= 0 ||
1975 (mlistp
= dupstring(getsparam("LISTPROMPT")))) {
1977 if (mlistp
&& !*mlistp
)
1978 mlistp
= "%SAt %p: Hit TAB for more, or the character to insert%s";
1980 showinglist
= listshown
= 0;
1985 clearflag
= (isset(USEZLE
) && !termflags
&& dolastprompt
);
1989 minfo
.asked
= (listdat
.nlines
+ nlnct
<= lines
);
1995 amatches
= oamatches
;
1997 opts
[EXTENDEDGLOB
] = extendedglob
;
1998 return (noselect
= 1);
2002 mlend
= mlbeg
+ lines
- nlnct
- mhasstat
;
2003 while (mline
>= mlend
)
2011 mtab_been_reallocated
= 1;
2013 i
= columns
* listdat
.nlines
;
2015 mtab
= (Cmatch
**) zalloc(i
* sizeof(Cmatch
**));
2016 memset(mtab
, 0, i
* sizeof(Cmatch
**));
2018 mgtab
= (Cmgroup
*) zalloc(i
* sizeof(Cmgroup
));
2022 memset(mgtab
, 0, i
* sizeof(Cmgroup
));
2023 mlastcols
= mcols
= columns
;
2024 mlastlines
= mlines
= listdat
.nlines
;
2026 last_cap
= (char *) zhalloc(max_caplen
+ 1);
2029 if (!mnew
&& inselect
&& onlnct
== nlnct
&& mlbeg
>= 0 && mlbeg
== molbeg
)
2031 else if (!compprintlist(mselect
>= 0) || !clearflag
)
2039 amatches
= oamatches
;
2042 opts
[EXTENDEDGLOB
] = extendedglob
;
2048 adjust_mcol(int wish
, Cmatch
***tabp
, Cmgroup
**grp
)
2050 Cmatch
**tab
= *tabp
;
2055 for (p
= wish
; p
>= 0 && (!tab
[p
] || mmarked(tab
[p
])); p
--);
2056 for (n
= wish
; n
< mcols
&& (!tab
[n
] || mmarked(tab
[n
])); n
++);
2067 c
= ((mcol
- p
) < (n
- mcol
) ? p
: n
);
2071 *grp
= *grp
+ c
- mcol
;
2078 typedef struct menustack
*Menustack
;
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.
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.
2105 typedef struct menusearch
*Menusearch
;
2119 #define MS_WRAPPED 2
2121 #define MAX_STATUS 128
2124 setmstatus(char *status
, char *sline
, int sll
, int scs
,
2125 int *csp
, int *llp
, int *lenp
)
2127 char *p
, *s
, *ret
= NULL
;
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
)
2145 s
= (char *) zhalloc(lastend
- zlemetacs
+ 1);
2146 strncpy(s
, zlemetaline
+ zlemetacs
, lastend
- zlemetacs
);
2147 s
[lastend
- zlemetacs
] = '\0';
2150 foredel(zlemetall
, CUT_RAW
);
2152 memcpy(zlemetaline
, sline
, sll
);
2160 max
= (columns
< MAX_STATUS
? columns
: MAX_STATUS
) - 14;
2163 int h
= (max
- 2) >> 1;
2165 strcpy(status
, "interactive: ");
2167 strcat(status
, "...");
2168 strcat(status
, p
+ pl
- h
- 3);
2172 strcat(status
, "[]");
2174 strncat(status
, s
, h
- 3);
2175 strcat(status
, "...");
2182 static Menusearch msearchstack
;
2183 static char *msearchstr
= NULL
;
2184 static int msearchstate
;
2187 msearchpush(Cmatch
**p
, int back
)
2189 Menusearch s
= (Menusearch
) zhalloc(sizeof(struct menusearch
));
2191 s
->prev
= msearchstack
;
2193 s
->str
= dupstring(msearchstr
);
2197 s
->state
= msearchstate
;
2202 msearchpop(int *backp
)
2204 Menusearch s
= msearchstack
;
2210 msearchstack
= s
->prev
;
2212 msearchstr
= s
->str
;
2215 msearchstate
= s
->state
;
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);
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
);
2238 #ifdef MULTIBYTE_SUPPORT
2239 if (lastchar_wide_valid
)
2244 memset(&mbs
, 0, sizeof(mbs
));
2245 len
= wcrtomb(s
, lastchar_wide
, &mbs
);
2256 msearchstr
= dyncat(msearchstr
, s
);
2263 ey
= listdat
.nlines
;
2265 p
= mtab
+ (mline
* mcols
) + mcol
;
2269 if (!rep
&& mtunmark(*p
) && *p
!= l
) {
2273 if (strstr((m
->disp
? m
->disp
: m
->str
), msearchstr
)) {
2295 if (x
== ex
&& y
== ey
) {
2297 msearchstate
= MS_FAILED
| owrap
;
2300 msearchstate
|= MS_WRAPPED
;
2304 y
= listdat
.nlines
- 1;
2305 p
= mtab
+ (y
* mcols
) + x
;
2320 * Values to assign to mode: interactive, etc.
2323 #define MM_FSEARCH 2
2324 #define MM_BSEARCH 3
2327 domenuselect(Hookdef dummy
, Chdata dat
)
2329 static Chdata fdat
= NULL
;
2330 static char *lastsearch
= NULL
;
2334 int do_last_key
= 0;
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
;
2340 char status
[MAX_STATUS
], *modeline
= NULL
;
2342 msearchstack
= NULL
;
2344 msearchstate
= MS_OK
;
2348 if (fdat
|| (dummy
&& (!(s
= getsparam("MENUSELECT")) ||
2349 (dat
&& dat
->num
< atoi(s
))))) {
2351 fdat
->matches
= dat
->matches
;
2352 fdat
->num
= dat
->num
;
2353 fdat
->nmesg
= dat
->nmesg
;
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
2368 if (zlemetaline
!= NULL
)
2375 if ((s
= getsparam("MENUSCROLL"))) {
2376 if (!(step
= mathevali(s
)))
2377 step
= (lines
- nlnct
) >> 1;
2379 if ((step
+= lines
- nlnct
) < 0)
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.
2396 foredel(zlemetall
, CUT_RAW
);
2398 strncpy(zlemetaline
, origline
, l
);
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";
2408 mhasstat
= (mstatus
&& *mstatus
);
2410 selectlocalmap(mskeymap
);
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
)))
2419 mselect
= (*(minfo
.cur
))->gnum
;
2424 mtab_been_reallocated
= 0;
2428 if (mline
< 0 || mtab_been_reallocated
) {
2432 for (y
= 0; y
< mlines
; y
++) {
2433 for (x
= mcols
; x
> 0; x
--, p
++)
2434 if (*p
&& !mmarked(*p
) && **p
&& mselect
== (**p
)->gnum
)
2444 mtab_been_reallocated
= 0;
2446 "BUG: mline < 0 after re-scanning mtab in domenuselect()");
2447 while (mline
< mlbeg
)
2448 if ((mlbeg
-= step
) < 0) {
2450 /* Crude workaround for BUG above */
2455 if (mlbeg
&& lbeg
!= mlbeg
) {
2456 Cmatch
**p
= mtab
+ ((mlbeg
- 1) * columns
), **q
;
2460 for (q
= p
, c
= columns
; c
> 0; q
++, c
--)
2461 if (*q
&& !mmarked(*q
))
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
;
2477 while (mlbeg
< mlines
) {
2478 for (q
= p
, c
= columns
; c
> 0; q
++, c
--)
2490 if (first
&& !listshown
&& isset(LISTBEEP
))
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
);
2505 modelen
= minfo
.len
;
2508 if (mode
== MM_INTER
)
2509 statusline
= status
;
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
;
2545 else if (mcol
> wishcol
) {
2546 while (mcol
> 0 && p
[-1] == minfo
.cur
)
2548 } else if (mcol
< wishcol
) {
2549 while (mcol
< mcols
- 1 && p
[1] == minfo
.cur
)
2552 setwish
= wasnext
= 0;
2558 if (mtab_been_reallocated
) {
2565 if (!cmd
|| cmd
== Th(z_sendbreak
)) {
2569 } else if (nolist
&& cmd
!= Th(z_undo
) &&
2570 (!mode
|| (cmd
!= Th(z_backwarddeletechar
) &&
2571 cmd
!= Th(z_selfinsert
) &&
2572 cmd
!= Th(z_selfinsertunmeta
)))) {
2575 } else if (cmd
== Th(z_acceptline
)) {
2576 if (mode
== MM_FSEARCH
|| mode
== MM_BSEARCH
) {
2582 } else if (cmd
== Th(z_viinsert
)) {
2583 if (mode
== MM_INTER
)
2586 int l
= strlen(origline
);
2589 * Entering interactive completion mode:
2590 * same code as when we enter it on menu selection
2595 foredel(zlemetall
, CUT_RAW
);
2597 strncpy(zlemetaline
, origline
, l
);
2599 setmstatus(status
, NULL
, 0, 0, NULL
, NULL
, NULL
);
2603 } else if (cmd
== Th(z_acceptandinfernexthistory
) ||
2604 (mode
== MM_INTER
&& (cmd
== Th(z_selfinsert
) ||
2605 cmd
== Th(z_selfinsertunmeta
)))) {
2606 char *saveline
= NULL
;
2609 Menustack s
= (Menustack
) zhalloc(sizeof(*s
));
2613 s
->line
= dupstring(zlemetaline
);
2617 memcpy(&(s
->info
), &minfo
, sizeof(struct menuinfo
));
2618 s
->amatches
= amatches
;
2619 s
->pmatches
= pmatches
;
2620 s
->lastmatches
= lastmatches
;
2621 s
->lastlmatches
= lastlmatches
;
2624 s
->brbeg
= dupbrinfo(brbeg
, NULL
, 1);
2625 s
->brend
= dupbrinfo(brend
, NULL
, 1);
2628 s
->nmatches
= nmatches
;
2629 s
->origline
= origline
;
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.
2643 menucmp
= menuacc
= hasoldlist
= 0;
2648 amatches
= pmatches
= lastmatches
= NULL
;
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.
2663 foredel(zlemetall
, CUT_RAW
);
2665 strncpy(zlemetaline
, origline
, l
);
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.
2677 if (cmd
== Th(z_selfinsert
))
2678 selfinsert(zlenoargs
);
2680 selfinsertunmeta(zlenoargs
);
2683 saveline
= (char *) zhalloc(zlemetall
);
2684 memcpy(saveline
, zlemetaline
, zlemetall
);
2690 /* Nested completion assumes line is unmetafied */
2692 menucomplete(zlenoargs
);
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
)) {
2702 if (mode
== MM_INTER
) {
2703 statusline
= status
;
2714 if (tccan(TCCLEAREOD
))
2716 fputs("no matches\r", shout
);
2718 tcmultout(TCUP
, TCMULTUP
, nlnct
);
2719 showinglist
= clearlist
= 0;
2722 showinglist
= clearlist
= 0;
2728 clearlist
= listshown
= 1;
2729 mselect
= (*(minfo
.cur
))->gnum
;
2730 setwish
= wasnext
= 1;
2734 } else if (cmd
== Th(z_acceptandhold
) ||
2735 cmd
== Th(z_acceptandmenucomplete
)) {
2736 Menustack s
= (Menustack
) zhalloc(sizeof(*s
));
2742 s
->line
= dupstring(zlemetaline
);
2746 memcpy(&(s
->info
), &minfo
, sizeof(struct menuinfo
));
2747 s
->amatches
= s
->pmatches
=
2748 s
->lastmatches
= s
->lastlmatches
= NULL
;
2751 s
->brbeg
= dupbrinfo(brbeg
, NULL
, 1);
2752 s
->brend
= dupbrinfo(brend
, NULL
, 1);
2755 s
->nmatches
= nmatches
;
2756 s
->origline
= origline
;
2759 s
->status
= dupstring(status
);
2766 mselect
= (*(minfo
.cur
))->gnum
;
2772 for (mcol
= 0; mcol
< mcols
; mcol
++, p
++)
2773 if (*p
== minfo
.cur
)
2777 if (++mline
== mlines
) {
2779 p
-= mlines
* mcols
;
2781 } while (mline
!= ol
);
2782 if (*p
!= minfo
.cur
) {
2783 noselect
= clearlist
= listshown
= 1;
2790 } else if (cmd
== Th(z_undo
) ||
2791 (mode
== MM_INTER
&& cmd
== Th(z_backwarddeletechar
))) {
2799 foredel(zlemetall
, CUT_RAW
);
2800 spaceinline(l
= strlen(u
->line
));
2801 strncpy(zlemetaline
, u
->line
, l
);
2804 memcpy(&minfo
, &(u
->info
), sizeof(struct menuinfo
));
2808 if (u
->lastmatches
&& lastmatches
!= u
->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;
2820 brbeg
= dupbrinfo(u
->brbeg
, &lastbrbeg
, 0);
2821 brend
= dupbrinfo(u
->brend
, &lastbrend
, 0);
2824 origline
= u
->origline
;
2827 strcpy(status
, u
->status
);
2838 if (mode
== MM_INTER
) {
2839 statusline
= status
;
2850 } else if (cmd
== Th(z_redisplay
)) {
2851 redisplay(zlenoargs
);
2854 } else if (cmd
== Th(z_clearscreen
)) {
2855 clearscreen(zlenoargs
);
2858 } else if (cmd
== Th(z_downhistory
) ||
2859 cmd
== Th(z_downlineorhistory
) ||
2860 cmd
== Th(z_downlineorsearch
) ||
2861 cmd
== Th(z_vidownlineorhistory
)) {
2874 if (mline
== mlines
- 1) {
2887 if (adjust_mcol(wishcol
, &p
, NULL
))
2889 } while (!*p
|| mmarked(*p
));
2893 } else if (cmd
== Th(z_uphistory
) ||
2894 cmd
== Th(z_uplineorhistory
) ||
2895 cmd
== Th(z_uplineorsearch
) ||
2896 cmd
== Th(z_viuplineorhistory
)) {
2922 if (adjust_mcol(wishcol
, &p
, NULL
))
2924 } while (!*p
|| mmarked(*p
));
2927 if (mcol
== wishcol
)
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;
2940 if (mline
== mlines
- 1)
2943 if (mline
== mlines
- 1) {
2951 if (adjust_mcol(wishcol
, &p
, NULL
))
2953 if (*p
&& !mmarked(*p
)) {
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;
2979 if (adjust_mcol(wishcol
, &p
, NULL
))
2981 if (*p
|| !mmarked(*p
)) {
2989 } else if (cmd
== Th(z_beginningofhistory
)) {
3002 if (adjust_mcol(wishcol
, &p
, NULL
))
3004 if (*p
&& !mmarked(*p
)) {
3011 } else if (cmd
== Th(z_endofhistory
)) {
3021 while (mline
< mlines
- 1) {
3024 if (adjust_mcol(wishcol
, &p
, NULL
))
3026 if (*p
&& !mmarked(*p
)) {
3033 } else if (cmd
== Th(z_forwardchar
) || cmd
== Th(z_viforwardchar
)) {
3046 if (mcol
== mcols
- 1) {
3059 } while (!*p
|| mmarked(*p
) || (mcol
!= omcol
&& *p
== *op
));
3064 } else if (cmd
== Th(z_backwardchar
) || cmd
== Th(z_vibackwardchar
)) {
3090 } while (!*p
|| mmarked(*p
) || (mcol
!= omcol
&& *p
== *op
));
3094 p
+= mcols
- 1 - mcol
;
3095 wishcol
= mcol
= mcols
- 1;
3096 adjust_mcol(wishcol
, &p
, NULL
);
3099 } else if (cmd
== Th(z_beginningofbufferorhistory
) ||
3100 cmd
== Th(z_beginningofline
) ||
3101 cmd
== Th(z_beginningoflinehist
) ||
3102 cmd
== Th(z_vibeginningofline
)) {
3106 while (!*p
|| mmarked(*p
)) {
3111 } else if (cmd
== Th(z_endofbufferorhistory
) ||
3112 cmd
== Th(z_endofline
) ||
3113 cmd
== Th(z_endoflinehist
) ||
3114 cmd
== Th(z_viendofline
)) {
3116 p
+= mcols
- mcol
- 1;
3118 while (!*p
|| mmarked(*p
)) {
3122 wishcol
= mcols
- 1;
3123 } else if (cmd
== Th(z_viforwardblankword
) ||
3124 cmd
== Th(z_viforwardblankwordend
)) {
3130 if (mline
== mlines
- 1) {
3132 pg
-= mline
* mcols
;
3139 if (adjust_mcol(wishcol
, &p
, &pg
))
3141 } while (ol
!= mline
&& (*pg
== g
|| !*pg
|| mmarked(*pg
)));
3142 } else if (cmd
== Th(z_vibackwardblankword
)) {
3151 pg
+= mline
* mcols
;
3157 if (adjust_mcol(wishcol
, &p
, &pg
))
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
;
3182 foredel(zlemetall
, CUT_RAW
);
3183 spaceinline(origll
);
3184 strncpy(zlemetaline
, origline
, origll
);
3186 minfo
.len
= modelen
;
3191 mselect
= (*(minfo
.cur
))->gnum
;
3196 } else if (cmd
== Th(z_reversemenucomplete
) ||
3197 !strcmp(cmd
->nam
, "reverse-menu-complete")) {
3200 reversemenucomplete(zlenoargs
);
3201 mselect
= (*(minfo
.cur
))->gnum
;
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
));
3218 p
+= wishcol
- mcol
;
3223 if (!*msearchstr
&& lastsearch
) {
3224 msearchstr
= dupstring(lastsearch
);
3229 msearchstack
= NULL
;
3232 if (cmd
== Th(z_selfinsertunmeta
)) {
3236 np
= msearch(p
, ins
, (ins
? (mode
== MM_BSEARCH
) : back
),
3237 (was
&& !ins
), &wrap
);
3240 mode
= (back
? MM_BSEARCH
: MM_FSEARCH
);
3244 lastsearch
= ztrdup(msearchstr
);
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
)) {
3258 Cmatch
**np
= msearchpop(&back
);
3260 mode
= (back
? MM_BSEARCH
: MM_FSEARCH
);
3264 adjust_mcol(wishcol
, &p
, NULL
);
3266 } else if (cmd
== Th(z_undefinedkey
)) {
3271 if (cmd
->widget
&& (cmd
->widget
->flags
& WIDGET_NCOMP
)) {
3279 mselect
= (**p
)->gnum
;
3282 for (; u
; u
= u
->prev
)
3283 if (u
->lastmatches
!= lastmatches
)
3284 freematches(u
->lastmatches
, 0);
3286 selectlocalmap(NULL
);
3287 mselect
= mlastcols
= mlastlines
= -1;
3289 inselect
= mhasstat
= 0;
3291 clearlist
= listshown
= 1;
3292 if (acc
&& validlist
&& minfo
.cur
) {
3293 menucmp
= lastambig
= hasoldlist
= 0;
3294 do_single(*(minfo
.cur
));
3296 if (wasnext
|| broken
) {
3298 showinglist
= ((validlist
&& !nolist
) ? -2 : 0);
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 ***
3317 showinglist
= ((validlist
&& !nolist
) ? -2 : 0);
3320 clearlist
= listshown
= 1;
3329 return (broken
== 2 ? 3 :
3330 ((dat
&& !broken
) ? (acc
? 1 : 2) : (!noselect
^ acc
)));
3333 /* The widget function. */
3336 menuselect(char **args
)
3343 if ((minfo
.cur
&& minfo
.asked
== 2) || selected
)
3347 if (minfo
.cur
&& (minfo
.asked
== 2 || domenuselect(NULL
, NULL
)) && !d
)
3353 static struct features module_features
= {
3363 setup_(UNUSED(Module m
))
3370 features_(Module m
, char ***features
)
3372 *features
= featuresarray(m
, &module_features
);
3378 enables_(Module m
, int **enables
)
3380 return handlefeatures(m
, &module_features
, enables
);
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'");
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
);
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
);
3442 finish_(UNUSED(Module m
))