mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-18 15:21:16 +02:00
Back off previous fix as this only covered some subset of problems. Remaining problems happend after reset-prompt in TRAPINT. One was in complist and is fixed by not attempting to list after an error or interrupt. The other was owing to not resetting clearflag when ZLE was re-entered.
3587 lines
84 KiB
C
3587 lines
84 KiB
C
/*
|
|
* complist.c - completion listing enhancements
|
|
*
|
|
* This file is part of zsh, the Z shell.
|
|
*
|
|
* Copyright (c) 1999 Sven Wischnowsky
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, without written agreement and without
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
* software and to distribute modified versions of this software for any
|
|
* purpose, provided that the above copyright notice and the following
|
|
* two paragraphs appear in all copies of this software.
|
|
*
|
|
* In no event shall Sven Wischnowsky or the Zsh Development Group be liable
|
|
* to any party for direct, indirect, special, incidental, or consequential
|
|
* damages arising out of the use of this software and its documentation,
|
|
* even if Sven Wischnowsky and the Zsh Development Group have been advised of
|
|
* the possibility of such damage.
|
|
*
|
|
* Sven Wischnowsky and the Zsh Development Group specifically disclaim any
|
|
* warranties, including, but not limited to, the implied warranties of
|
|
* merchantability and fitness for a particular purpose. The software
|
|
* provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
|
|
* Zsh Development Group have no obligation to provide maintenance,
|
|
* support, updates, enhancements, or modifications.
|
|
*
|
|
*/
|
|
|
|
#include "complist.mdh"
|
|
#include "complist.pro"
|
|
|
|
|
|
/* Information about the list shown. */
|
|
|
|
/*
|
|
* noselect: 1 if complistmatches indicated we shouldn't do selection;
|
|
* -1 if interactive mode needs to reset the selection list.
|
|
* Tested in domenuselect, and in complistmatches to skip redraw.
|
|
* mselect: Local copy of the index of the currently selected match.
|
|
* Initialised to the gnum entry of the current match for
|
|
* each completion.
|
|
* inselect: 1 if we already selecting matches; tested in complistmatches()
|
|
* mcol: The column for the selected completion. As we never scroll
|
|
* horizontally this applies both the screen and the logical array.
|
|
* mline: The line for the selected completion in the logical array of
|
|
* all matches, not all of which may be on screen at once.
|
|
* mcols: Local copy of columns used in sizing arrays.
|
|
* mlines: The number of lines in the logical array of all matches,
|
|
* initialised from listdat.nlines.
|
|
*/
|
|
static int noselect, mselect, inselect, mcol, mline, mcols, mlines;
|
|
/*
|
|
* selected: Used to signal between domenucomplete() and menuselect()
|
|
* that a selected entry has been found. Or something.
|
|
* mlbeg: The first line of the logical array of all matches that
|
|
* fits on screen. Setting this to -1 forces a redraw.
|
|
* mlend: The line after the last that fits on screen.
|
|
* mscroll: 1 if the scrolling prompt is shown on screen.
|
|
* mrestlines: The number of screen lines remaining to be processed.
|
|
*/
|
|
static int selected, mlbeg = -1, mlend = 9999999, mscroll, mrestlines;
|
|
/*
|
|
* mnew: 1 if a new selection menu is being displayed.
|
|
* mlastcols: stored value of mcols for use in calculating mnew.
|
|
* mlastlines: stored value of mlines for use in calculating mnew.
|
|
* mhasstat: Indicates if the status line is present (but maybe not
|
|
* yet printed).
|
|
* mfirstl: The first line of the logical array of all matches to
|
|
* be shown on screen, -1 if this has not yet been determined.
|
|
* mlastm: The index of the selected match in some circumstances; used
|
|
* if an explicit number for a match is passed to compprintfmt();
|
|
* initialised from the total number of matches. I realise this
|
|
* isn't very illuminating.
|
|
*/
|
|
static int mnew, mlastcols, mlastlines, mhasstat, mfirstl, mlastm;
|
|
/*
|
|
* mlprinted: Used to signal the number of additional lines printed
|
|
* when outputting matches (as argument passing is a bit
|
|
* screwy within the completion system).
|
|
* molbeg: The last value of mlbeg; -1 if invalid, -42 if, er, very
|
|
* invalid. Used in calculations of how much to draw.
|
|
* mocol: The last value of mcol.
|
|
* moline: The last value of mline.
|
|
* mstatprinted: Indicates that the status line has now been printed,
|
|
* c.f. mhasstat.
|
|
*/
|
|
static int mlprinted, molbeg = -2, mocol = 0, moline = 0, mstatprinted;
|
|
/*
|
|
* mstatus: The message printed when scrolling.
|
|
* mlistp: The message printed when merely listing.
|
|
*/
|
|
static char *mstatus, *mlistp;
|
|
/*
|
|
* mtab is the logical array of all matches referred to above. It
|
|
* contains mcols*mlines entries. These entries contain a pointer to
|
|
* the match structure which is in use at a particular point. Note
|
|
* that for multiple line entries lines after the first contain NULL.
|
|
*
|
|
* mmtabp is a pointer to the selected entry in mtab.
|
|
*/
|
|
static Cmatch **mtab, **mmtabp;
|
|
/*
|
|
* Used to indicate that the list has changed and needs redisplaying.
|
|
*/
|
|
static int mtab_been_reallocated;
|
|
/*
|
|
* Array and pointer for the match group in exactly the same layout
|
|
* as mtab and mmtabp.
|
|
*/
|
|
static Cmgroup *mgtab, *mgtabp;
|
|
#ifdef DEBUG
|
|
/*
|
|
* Allow us to keep track of pointer arithmetic for mgtab; could
|
|
* just as well have been for mtab but wasn't.
|
|
*/
|
|
static int mgtabsize;
|
|
#endif
|
|
|
|
/*
|
|
* Used in mtab/mgtab, for explanations.
|
|
*
|
|
* UUUUUUUUUUUUUUURRRRGHHHHHHHHHH!!!!!!!!! --- pws
|
|
*/
|
|
|
|
#define MMARK ((unsigned long) 1)
|
|
#define mmarked(v) (((unsigned long) (v)) & MMARK)
|
|
#define mtmark(v) ((Cmatch *) (((unsigned long) (v)) | MMARK))
|
|
#define mtunmark(v) ((Cmatch *) (((unsigned long) (v)) & ~MMARK))
|
|
#define mgmark(v) ((Cmgroup) (((unsigned long) (v)) | MMARK))
|
|
#define mgunmark(v) ((Cmgroup) (((unsigned long) (v)) & ~MMARK))
|
|
|
|
/* Information for in-string colours. */
|
|
|
|
/* Maximum number of in-string colours supported. */
|
|
|
|
#define MAX_POS 11
|
|
|
|
static int nrefs;
|
|
static int begpos[MAX_POS], curisbeg;
|
|
static int endpos[MAX_POS];
|
|
static int sendpos[MAX_POS], curissend; /* sorted end positions */
|
|
static char **patcols, *curiscols[MAX_POS];
|
|
static int curiscol;
|
|
|
|
/* The last color used. */
|
|
|
|
static char *last_cap;
|
|
|
|
|
|
/* We use the parameters ZLS_COLORS and ZLS_COLOURS in the same way as
|
|
* the color ls does. It's just that we don't support the `or' file
|
|
* type. */
|
|
|
|
|
|
/*
|
|
* menu-select widget: used to test if it's already loaded.
|
|
*/
|
|
static Widget w_menuselect;
|
|
/*
|
|
* Similarly for the menuselect and listscroll keymaps.
|
|
*/
|
|
static Keymap mskeymap, lskeymap;
|
|
|
|
/* Indixes into the terminal string arrays. */
|
|
|
|
#define COL_NO 0
|
|
#define COL_FI 1
|
|
#define COL_DI 2
|
|
#define COL_LN 3
|
|
#define COL_PI 4
|
|
#define COL_SO 5
|
|
#define COL_BD 6
|
|
#define COL_CD 7
|
|
#define COL_OR 8
|
|
#define COL_MI 9
|
|
#define COL_SU 10
|
|
#define COL_SG 11
|
|
#define COL_TW 12
|
|
#define COL_OW 13
|
|
#define COL_ST 14
|
|
#define COL_EX 15
|
|
#define COL_LC 16
|
|
#define COL_RC 17
|
|
#define COL_EC 18
|
|
#define COL_TC 19
|
|
#define COL_SP 20
|
|
#define COL_MA 21
|
|
#define COL_HI 22
|
|
#define COL_DU 23
|
|
#define COL_SA 24
|
|
|
|
#define NUM_COLS 25
|
|
|
|
/* Names of the terminal strings. */
|
|
|
|
static char *colnames[] = {
|
|
"no", "fi", "di", "ln", "pi", "so", "bd", "cd", "or", "mi",
|
|
"su", "sg", "tw", "ow", "st", "ex",
|
|
"lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", "sa", NULL
|
|
};
|
|
|
|
/* Default values. */
|
|
|
|
static char *defcols[] = {
|
|
"0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", NULL, NULL,
|
|
"37;41", "30;43", "30;42", "34;42", "37;44", "1;32",
|
|
"\033[", "m", NULL, "0", "0", "7", NULL, NULL, "0"
|
|
};
|
|
|
|
/* This describes a terminal string for a file type. */
|
|
|
|
typedef struct filecol *Filecol;
|
|
|
|
struct filecol {
|
|
Patprog prog; /* group pattern */
|
|
char *col; /* color string */
|
|
Filecol next; /* next one */
|
|
};
|
|
|
|
/* This describes a terminal string for a pattern. */
|
|
|
|
typedef struct patcol *Patcol;
|
|
|
|
struct patcol {
|
|
Patprog prog;
|
|
Patprog pat; /* pattern for match */
|
|
char *cols[MAX_POS + 1];
|
|
Patcol next;
|
|
};
|
|
|
|
/* This describes a terminal string for a filename extension. */
|
|
|
|
typedef struct extcol *Extcol;
|
|
|
|
struct extcol {
|
|
Patprog prog; /* group pattern or NULL */
|
|
char *ext; /* the extension */
|
|
char *col; /* the terminal color string */
|
|
Extcol next; /* the next one in the list */
|
|
};
|
|
|
|
/* This holds all terminal strings. */
|
|
|
|
typedef struct listcols *Listcols;
|
|
|
|
/* values for listcol flags */
|
|
enum {
|
|
/* ln=target: follow symlinks to determine highlighting */
|
|
LC_FOLLOW_SYMLINKS = 0x0001
|
|
};
|
|
|
|
struct listcols {
|
|
Filecol files[NUM_COLS]; /* strings for file types */
|
|
Patcol pats; /* strings for patterns */
|
|
Extcol exts; /* strings for extensions */
|
|
int flags; /* special settings, see above */
|
|
};
|
|
|
|
/*
|
|
* Contains information about the colours to be used for entries.
|
|
* Sometimes mcolors is passed as an argument even though it's
|
|
* available to all the functions.
|
|
*/
|
|
static struct listcols mcolors;
|
|
|
|
/* Combined length of LC and RC, maximum length of capability strings. */
|
|
|
|
static int lr_caplen, max_caplen;
|
|
|
|
/* This parses the value of a definition (the part after the `=').
|
|
* The return value is a pointer to the character after it. */
|
|
|
|
static char *
|
|
getcolval(char *s, int multi)
|
|
{
|
|
char *p, *o = s;
|
|
|
|
for (p = s; *s && *s != ':' && (!multi || *s != '='); p++, s++) {
|
|
if (*s == '\\' && s[1]) {
|
|
switch (*++s) {
|
|
case 'a': *p = '\007'; break;
|
|
case 'n': *p = '\n'; break;
|
|
case 'b': *p = '\b'; break;
|
|
case 't': *p = '\t'; break;
|
|
case 'v': *p = '\v'; break;
|
|
case 'f': *p = '\f'; break;
|
|
case 'r': *p = '\r'; break;
|
|
case 'e': *p = '\033'; break;
|
|
case '_': *p = ' '; break;
|
|
case '?': *p = '\177'; break;
|
|
default:
|
|
if (*s >= '0' && *s <= '7') {
|
|
int i = STOUC(*s);
|
|
|
|
if (*++s >= '0' && *s <= '7') {
|
|
i = (i * 8) + STOUC(*s);
|
|
if (*++s >= '0' && *s <= '7')
|
|
i = (i * 8) + STOUC(*s);
|
|
}
|
|
*p = (char) i;
|
|
} else
|
|
*p = *s;
|
|
}
|
|
} else if (*s == '^') {
|
|
if ((s[1] >= '@' && s[1] <= '_') ||
|
|
(s[1] >= 'a' && s[1] <= 'z'))
|
|
*p = (char) (STOUC(*s) & ~0x60);
|
|
else if (s[1] == '?')
|
|
*p = '\177';
|
|
else {
|
|
*p++ = *s;
|
|
*p = s[1];
|
|
}
|
|
s++;
|
|
} else
|
|
*p = *s;
|
|
}
|
|
if (p != s)
|
|
*p = '\0';
|
|
if ((s - o) > max_caplen)
|
|
max_caplen = s - o;
|
|
return s;
|
|
}
|
|
|
|
/* This parses one definition. Return value is a pointer to the
|
|
* character after it. */
|
|
|
|
static char *
|
|
getcoldef(char *s)
|
|
{
|
|
Patprog gprog = NULL;
|
|
|
|
if (*s == '(') {
|
|
char *p;
|
|
int l = 0;
|
|
|
|
for (p = s + 1, l = 0; *p && (*p != ')' || l); p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
else if (*p == '(')
|
|
l++;
|
|
else if (*p == ')')
|
|
l--;
|
|
|
|
if (*p == ')') {
|
|
char sav = p[1];
|
|
|
|
p[1] = '\0';
|
|
s = metafy(s, -1, META_USEHEAP);
|
|
tokenize(s);
|
|
gprog = patcompile(s, 0, NULL);
|
|
p[1] = sav;
|
|
|
|
s = p + 1;
|
|
}
|
|
}
|
|
if (*s == '*') {
|
|
Extcol ec, eo;
|
|
char *n, *p;
|
|
|
|
/* This is for an extension. */
|
|
|
|
n = ++s;
|
|
while (*s && *s != '=')
|
|
s++;
|
|
if (!*s)
|
|
return s;
|
|
*s++ = '\0';
|
|
p = getcolval(s, 0);
|
|
ec = (Extcol) zhalloc(sizeof(*ec));
|
|
ec->prog = gprog;
|
|
ec->ext = n;
|
|
ec->col = s;
|
|
ec->next = NULL;
|
|
if ((eo = mcolors.exts)) {
|
|
while (eo->next)
|
|
eo = eo->next;
|
|
eo->next = ec;
|
|
} else
|
|
mcolors.exts = ec;
|
|
if (*p)
|
|
*p++ = '\0';
|
|
return p;
|
|
} else if (*s == '=') {
|
|
char *p = ++s, *t, *cols[MAX_POS];
|
|
int ncols = 0;
|
|
int nesting = 0;
|
|
Patprog prog;
|
|
|
|
/* This is for a pattern. */
|
|
|
|
while (*s && (nesting || *s != '=')) {
|
|
switch (*s++) {
|
|
case '\\':
|
|
if (*s)
|
|
s++;
|
|
break;
|
|
case '(':
|
|
nesting++;
|
|
break;
|
|
case ')':
|
|
nesting--;
|
|
break;
|
|
}
|
|
}
|
|
if (!*s)
|
|
return s;
|
|
*s++ = '\0';
|
|
while (1) {
|
|
t = getcolval(s, 1);
|
|
if (ncols < MAX_POS)
|
|
cols[ncols++] = s;
|
|
s = t;
|
|
if (*s != '=')
|
|
break;
|
|
*s++ = '\0';
|
|
}
|
|
p = metafy(p, -1, META_USEHEAP);
|
|
tokenize(p);
|
|
if ((prog = patcompile(p, 0, NULL))) {
|
|
Patcol pc, po;
|
|
int i;
|
|
|
|
pc = (Patcol) zhalloc(sizeof(*pc));
|
|
pc->prog = gprog;
|
|
pc->pat = prog;
|
|
for (i = 0; i < ncols; i++)
|
|
pc->cols[i] = cols[i];
|
|
pc->cols[i] = NULL;
|
|
pc->next = NULL;
|
|
if ((po = mcolors.pats)) {
|
|
while (po->next)
|
|
po = po->next;
|
|
po->next = pc;
|
|
} else
|
|
mcolors.pats = pc;
|
|
}
|
|
if (*t)
|
|
*t++ = '\0';
|
|
return t;
|
|
} else {
|
|
char *n = s, *p, **nn;
|
|
int i;
|
|
|
|
/* This is for a file type. */
|
|
|
|
while (*s && *s != '=')
|
|
s++;
|
|
if (!*s)
|
|
return s;
|
|
*s++ = '\0';
|
|
for (i = 0, nn = colnames; *nn; i++, nn++)
|
|
if (!strcmp(n, *nn))
|
|
break;
|
|
/*
|
|
* special case: highlighting link targets
|
|
*/
|
|
if (i == COL_LN && strpfx("target", s) &&
|
|
(s[6] == ':' || !s[6])) {
|
|
mcolors.flags |= LC_FOLLOW_SYMLINKS;
|
|
p = s + 6;
|
|
} else {
|
|
p = getcolval(s, 0);
|
|
if (*nn) {
|
|
Filecol fc, fo;
|
|
|
|
fc = (Filecol) zhalloc(sizeof(*fc));
|
|
fc->prog = (i == COL_EC || i == COL_LC || i == COL_RC ?
|
|
NULL : gprog);
|
|
fc->col = s;
|
|
fc->next = NULL;
|
|
if ((fo = mcolors.files[i])) {
|
|
while (fo->next)
|
|
fo = fo->next;
|
|
fo->next = fc;
|
|
} else
|
|
mcolors.files[i] = fc;
|
|
}
|
|
if (*p)
|
|
*p++ = '\0';
|
|
}
|
|
return p;
|
|
}
|
|
}
|
|
|
|
static Filecol
|
|
filecol(char *col)
|
|
{
|
|
Filecol fc;
|
|
|
|
fc = (Filecol) zhalloc(sizeof(*fc));
|
|
fc->prog = NULL;
|
|
fc->col = col;
|
|
fc->next = NULL;
|
|
|
|
return fc;
|
|
}
|
|
|
|
/*
|
|
* This initializes the given terminal color structure.
|
|
*/
|
|
|
|
static void
|
|
getcols(void)
|
|
{
|
|
char *s;
|
|
int i, l;
|
|
|
|
max_caplen = lr_caplen = 0;
|
|
mcolors.flags = 0;
|
|
queue_signals();
|
|
if (!(s = getsparam_u("ZLS_COLORS")) &&
|
|
!(s = getsparam_u("ZLS_COLOURS"))) {
|
|
for (i = 0; i < NUM_COLS; i++)
|
|
mcolors.files[i] = filecol("");
|
|
mcolors.pats = NULL;
|
|
mcolors.exts = NULL;
|
|
|
|
if ((s = tcstr[TCSTANDOUTBEG]) && s[0]) {
|
|
mcolors.files[COL_MA] = filecol(s);
|
|
mcolors.files[COL_EC] = filecol(tcstr[TCSTANDOUTEND]);
|
|
} else
|
|
mcolors.files[COL_MA] = filecol(defcols[COL_MA]);
|
|
lr_caplen = 0;
|
|
if ((max_caplen = strlen(mcolors.files[COL_MA]->col)) <
|
|
(l = strlen(mcolors.files[COL_EC]->col)))
|
|
max_caplen = l;
|
|
unqueue_signals();
|
|
return;
|
|
}
|
|
/* Reset the global color structure. */
|
|
memset(&mcolors, 0, sizeof(mcolors));
|
|
s = dupstring(s);
|
|
while (*s)
|
|
if (*s == ':')
|
|
s++;
|
|
else
|
|
s = getcoldef(s);
|
|
unqueue_signals();
|
|
|
|
/* Use default values for those that aren't set explicitly. */
|
|
for (i = 0; i < NUM_COLS; i++) {
|
|
if (!mcolors.files[i] || !mcolors.files[i]->col)
|
|
mcolors.files[i] = filecol(defcols[i]);
|
|
if (mcolors.files[i] && mcolors.files[i]->col &&
|
|
(l = strlen(mcolors.files[i]->col)) > max_caplen)
|
|
max_caplen = l;
|
|
}
|
|
lr_caplen = strlen(mcolors.files[COL_LC]->col) +
|
|
strlen(mcolors.files[COL_RC]->col);
|
|
|
|
/* Default for orphan is same as link. */
|
|
if (!mcolors.files[COL_OR] || !mcolors.files[COL_OR]->col)
|
|
mcolors.files[COL_OR] = mcolors.files[COL_LN];
|
|
/* Default for missing files: currently not used */
|
|
if (!mcolors.files[COL_MI] || !mcolors.files[COL_MI]->col)
|
|
mcolors.files[COL_MI] = mcolors.files[COL_FI];
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
zlrputs(char *cap)
|
|
{
|
|
if (!*last_cap || strcmp(last_cap, cap)) {
|
|
VARARR(char, buf, lr_caplen + max_caplen + 1);
|
|
|
|
strcpy(buf, mcolors.files[COL_LC]->col);
|
|
strcat(buf, cap);
|
|
strcat(buf, mcolors.files[COL_RC]->col);
|
|
|
|
tputs(buf, 1, putshout);
|
|
|
|
strcpy(last_cap, cap);
|
|
}
|
|
}
|
|
|
|
static void
|
|
zcputs(char *group, int colour)
|
|
{
|
|
Filecol fc;
|
|
|
|
for (fc = mcolors.files[colour]; fc; fc = fc->next)
|
|
if (fc->col &&
|
|
(!fc->prog || !group || pattry(fc->prog, group))) {
|
|
zlrputs(fc->col);
|
|
|
|
return;
|
|
}
|
|
zlrputs("0");
|
|
}
|
|
|
|
/* Turn off colouring. */
|
|
|
|
static void
|
|
zcoff(void)
|
|
{
|
|
if (mcolors.files[COL_EC] && mcolors.files[COL_EC]->col) {
|
|
tputs(mcolors.files[COL_EC]->col, 1, putshout);
|
|
*last_cap = '\0';
|
|
} else
|
|
zcputs(NULL, COL_NO);
|
|
}
|
|
|
|
|
|
static void
|
|
initiscol(void)
|
|
{
|
|
int i;
|
|
|
|
zlrputs(patcols[0]);
|
|
|
|
curiscols[curiscol = 0] = *patcols++;
|
|
|
|
curisbeg = curissend = 0;
|
|
|
|
for (i = 0; i < nrefs; i++)
|
|
sendpos[i] = 0xfffffff;
|
|
for (; i < MAX_POS; i++)
|
|
begpos[i] = endpos[i] = sendpos[i] = 0xfffffff;
|
|
}
|
|
|
|
static void
|
|
doiscol(int pos)
|
|
{
|
|
int fi;
|
|
|
|
while (pos > sendpos[curissend]) {
|
|
curissend++;
|
|
if (curiscol) {
|
|
zcputs(NULL, COL_NO);
|
|
zlrputs(curiscols[--curiscol]);
|
|
}
|
|
}
|
|
while (((fi = (endpos[curisbeg] < begpos[curisbeg] ||
|
|
begpos[curisbeg] == -1)) ||
|
|
pos == begpos[curisbeg]) && *patcols) {
|
|
if (!fi) {
|
|
int i, j, e = endpos[curisbeg];
|
|
|
|
/* insert e in sendpos */
|
|
for (i = curissend; sendpos[i] <= e; ++i)
|
|
;
|
|
for (j = MAX_POS - 1; j > i; --j)
|
|
sendpos[j] = sendpos[j-1];
|
|
sendpos[i] = e;
|
|
|
|
zcputs(NULL, COL_NO);
|
|
zlrputs(*patcols);
|
|
curiscols[++curiscol] = *patcols;
|
|
}
|
|
++patcols;
|
|
++curisbeg;
|
|
}
|
|
}
|
|
|
|
/* Stripped-down version of printfmt(). But can do in-string colouring. */
|
|
|
|
static int
|
|
clprintfmt(char *p, int ml)
|
|
{
|
|
int cc = 0, i = 0, ask, beg;
|
|
|
|
initiscol();
|
|
|
|
while (*p) {
|
|
convchar_t chr;
|
|
int chrlen = MB_METACHARLENCONV(p, &chr);
|
|
doiscol(i++);
|
|
cc++;
|
|
if (*p == '\n') {
|
|
if (mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
cc = 0;
|
|
}
|
|
if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1)
|
|
return 0;
|
|
|
|
while (chrlen) {
|
|
if (*p == Meta) {
|
|
p++;
|
|
chrlen--;
|
|
putc(*p ^ 32, shout);
|
|
} else
|
|
putc(*p, shout);
|
|
chrlen--;
|
|
p++;
|
|
}
|
|
if ((beg = !(cc % zterm_columns)))
|
|
ml++;
|
|
if (mscroll && !(cc % zterm_columns) &&
|
|
!--mrestlines && (ask = asklistscroll(ml)))
|
|
return ask;
|
|
}
|
|
if (mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Local version of nicezputs() with in-string colouring
|
|
* and scrolling.
|
|
*/
|
|
|
|
static int
|
|
clnicezputs(int do_colors, char *s, int ml)
|
|
{
|
|
int i = 0, col = 0, ask, oml = ml;
|
|
char *t;
|
|
ZLE_CHAR_T cc;
|
|
#ifdef MULTIBYTE_SUPPORT
|
|
/*
|
|
* ums is the untokenized, unmetafied string (length umlen)
|
|
* uptr is a pointer into it
|
|
* sptr is the start of the nice character representation
|
|
* wptr is the point at which the wide character itself starts
|
|
* (but may be the end of the string if the character was fully
|
|
* prettified).
|
|
* ret is the return status from the conversion to a wide character
|
|
* umleft is the remaining length of the unmetafied string to output
|
|
* umlen is the full length of the unmetafied string
|
|
* width is the full printing width of a prettified character,
|
|
* including both ASCII prettification and the wide character itself.
|
|
* mbs is the shift state of the conversion to wide characters.
|
|
*/
|
|
char *ums, *uptr, *sptr, *wptr;
|
|
int umleft, umlen, eol = 0;
|
|
size_t width;
|
|
mbstate_t mbs;
|
|
|
|
memset(&mbs, 0, sizeof mbs);
|
|
ums = ztrdup(s);
|
|
untokenize(ums);
|
|
uptr = unmetafy(ums, ¨en);
|
|
umleft = umlen;
|
|
|
|
if (do_colors)
|
|
initiscol();
|
|
|
|
mb_charinit();
|
|
while (umleft > 0) {
|
|
size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, uptr, umleft, &mbs);
|
|
|
|
switch (cnt) {
|
|
case MB_INCOMPLETE:
|
|
eol = 1;
|
|
/* FALL THROUGH */
|
|
case MB_INVALID:
|
|
/* This handles byte values that aren't valid wide-character
|
|
* sequences. */
|
|
sptr = nicechar(*uptr);
|
|
/* everything here is ASCII... */
|
|
width = strlen(sptr);
|
|
wptr = sptr + width;
|
|
cnt = 1;
|
|
/* Get mbs out of its undefined state. */
|
|
memset(&mbs, 0, sizeof mbs);
|
|
break;
|
|
case 0:
|
|
/* This handles a '\0' in the input (which is a real char
|
|
* to us, not a terminator). */
|
|
cnt = 1;
|
|
/* FALL THROUGH */
|
|
default:
|
|
sptr = wcs_nicechar(cc, &width, &wptr);
|
|
break;
|
|
}
|
|
|
|
umleft -= cnt;
|
|
uptr += cnt;
|
|
if (do_colors) {
|
|
/*
|
|
* The code for the colo[u]ri[s/z]ation is obscure (surprised?)
|
|
* but if we do it for every input character, as we do in
|
|
* the simple case, we shouldn't go too far wrong.
|
|
*/
|
|
while (cnt--)
|
|
doiscol(i++);
|
|
}
|
|
|
|
/*
|
|
* Loop over characters in the output of the nice
|
|
* representation. This will often correspond to one input
|
|
* (possibly multibyte) character.
|
|
*/
|
|
for (t = sptr; *t; t++) {
|
|
/* Input is metafied... */
|
|
int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
|
|
/* Is the screen full? */
|
|
if (ml == mlend - 1 && col == zterm_columns - 1) {
|
|
mlprinted = ml - oml;
|
|
free(ums);
|
|
return 0;
|
|
}
|
|
if (t < wptr) {
|
|
/* outputting ASCII, so single-width */
|
|
putc(nc, shout);
|
|
col++;
|
|
width--;
|
|
} else {
|
|
/* outputting a single wide character, do the lot */
|
|
putc(nc, shout);
|
|
/* don't check column until finished */
|
|
if (t[1])
|
|
continue;
|
|
/* now we've done the entire rest of the representation */
|
|
col += width;
|
|
}
|
|
/*
|
|
* There might be problems with characters of printing width
|
|
* greater than one here.
|
|
*/
|
|
if (col > zterm_columns) {
|
|
ml++;
|
|
if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
|
|
mlprinted = ml - oml;
|
|
free(ums);
|
|
return ask;
|
|
}
|
|
col -= zterm_columns;
|
|
if (do_colors)
|
|
fputs(" \010", shout);
|
|
}
|
|
}
|
|
}
|
|
|
|
free(ums);
|
|
#else
|
|
|
|
if (do_colors)
|
|
initiscol();
|
|
|
|
while ((cc = *s++)) {
|
|
if (do_colors)
|
|
doiscol(i++);
|
|
if (itok(cc)) {
|
|
if (cc <= Comma)
|
|
cc = ztokens[cc - Pound];
|
|
else
|
|
continue;
|
|
}
|
|
if (cc == Meta)
|
|
cc = *s++ ^ 32;
|
|
|
|
for (t = nicechar(cc); *t; t++) {
|
|
int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t);
|
|
if (ml == mlend - 1 && col == zterm_columns - 1) {
|
|
mlprinted = ml - oml;
|
|
return 0;
|
|
}
|
|
putc(nc, shout);
|
|
if (++col > zterm_columns) {
|
|
ml++;
|
|
if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) {
|
|
mlprinted = ml - oml;
|
|
return ask;
|
|
}
|
|
col = 0;
|
|
if (do_colors)
|
|
fputs(" \010", shout);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
mlprinted = ml - oml;
|
|
return 0;
|
|
}
|
|
|
|
/* Get the terminal color string for the given match. */
|
|
|
|
static int
|
|
putmatchcol(char *group, char *n)
|
|
{
|
|
Patcol pc;
|
|
|
|
for (pc = mcolors.pats; pc; pc = pc->next) {
|
|
nrefs = MAX_POS - 1;
|
|
|
|
if ((!pc->prog || !group || pattry(pc->prog, group)) &&
|
|
pattryrefs(pc->pat, n, -1, -1, NULL, 0, &nrefs, begpos, endpos)) {
|
|
if (pc->cols[1]) {
|
|
patcols = pc->cols;
|
|
|
|
return 1;
|
|
}
|
|
zlrputs(pc->cols[0]);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
zcputs(group, COL_NO);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Get the terminal color string for the file with the given name and
|
|
* file modes. */
|
|
|
|
static int
|
|
putfilecol(char *group, char *filename, mode_t m, int special)
|
|
{
|
|
int colour = -1;
|
|
Extcol ec;
|
|
Patcol pc;
|
|
int len;
|
|
|
|
for (pc = mcolors.pats; pc; pc = pc->next) {
|
|
nrefs = MAX_POS - 1;
|
|
|
|
if ((!pc->prog || !group || pattry(pc->prog, group)) &&
|
|
pattryrefs(pc->pat, filename, -1, -1, NULL,
|
|
0, &nrefs, begpos, endpos)) {
|
|
if (pc->cols[1]) {
|
|
patcols = pc->cols;
|
|
|
|
return 1;
|
|
}
|
|
zlrputs(pc->cols[0]);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (special != -1) {
|
|
colour = special;
|
|
} else if (S_ISDIR(m)) {
|
|
if (m & S_IWOTH)
|
|
if (m & S_ISVTX)
|
|
colour = COL_TW;
|
|
else
|
|
colour = COL_OW;
|
|
else if (m & S_ISVTX)
|
|
colour = COL_ST;
|
|
else
|
|
colour = COL_DI;
|
|
} else if (S_ISLNK(m))
|
|
colour = COL_LN;
|
|
else if (S_ISFIFO(m))
|
|
colour = COL_PI;
|
|
else if (S_ISSOCK(m))
|
|
colour = COL_SO;
|
|
else if (S_ISBLK(m))
|
|
colour = COL_BD;
|
|
else if (S_ISCHR(m))
|
|
colour = COL_CD;
|
|
else if (m & S_ISUID)
|
|
colour = COL_SU;
|
|
else if (m & S_ISGID)
|
|
colour = COL_SG;
|
|
else if (S_ISREG(m) && (m & S_IXUGO))
|
|
colour = COL_EX;
|
|
|
|
if (colour != -1) {
|
|
zcputs(group, colour);
|
|
return 0;
|
|
}
|
|
|
|
for (ec = mcolors.exts; ec; ec = ec->next)
|
|
if (strsfx(ec->ext, filename) &&
|
|
(!ec->prog || !group || pattry(ec->prog, group))) {
|
|
zlrputs(ec->col);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Check for suffix alias */
|
|
len = strlen(filename);
|
|
/* shortest valid suffix format is a.b */
|
|
if (len > 2) {
|
|
char *suf = filename + len - 1;
|
|
while (suf > filename+1) {
|
|
if (suf[-1] == '.') {
|
|
if (sufaliastab->getnode(sufaliastab, suf)) {
|
|
zcputs(group, COL_SA);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
suf--;
|
|
}
|
|
}
|
|
zcputs(group, COL_FI);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static Cmgroup last_group;
|
|
|
|
/**/
|
|
static int
|
|
asklistscroll(int ml)
|
|
{
|
|
Thingy cmd;
|
|
int i, ret = 0;
|
|
|
|
compprintfmt(NULL, 1, 1, 1, ml, NULL);
|
|
|
|
fflush(shout);
|
|
zsetterm();
|
|
menuselect_bindings(); /* sanity in case deleted by user */
|
|
selectlocalmap(lskeymap);
|
|
if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
|
|
ret = 1;
|
|
else if (cmd == Th(z_acceptline) ||
|
|
cmd == Th(z_downhistory) ||
|
|
cmd == Th(z_downlineorhistory) ||
|
|
cmd == Th(z_downlineorsearch) ||
|
|
cmd == Th(z_vidownlineorhistory))
|
|
mrestlines = 1;
|
|
else if (cmd == Th(z_completeword) ||
|
|
cmd == Th(z_expandorcomplete) ||
|
|
cmd == Th(z_expandorcompleteprefix) ||
|
|
cmd == Th(z_menucomplete) ||
|
|
cmd == Th(z_menuexpandorcomplete) ||
|
|
!strcmp(cmd->nam, "menu-select") ||
|
|
!strcmp(cmd->nam, "complete-word") ||
|
|
!strcmp(cmd->nam, "expand-or-complete") ||
|
|
!strcmp(cmd->nam, "expand-or-complete-prefix") ||
|
|
!strcmp(cmd->nam, "menu-complete") ||
|
|
!strcmp(cmd->nam, "menu-expand-or-complete"))
|
|
mrestlines = zterm_lines - 1;
|
|
else if (cmd == Th(z_acceptsearch))
|
|
ret = 1;
|
|
else {
|
|
ungetkeycmd();
|
|
ret = 1;
|
|
}
|
|
selectlocalmap(NULL);
|
|
settyinfo(&shttyinfo);
|
|
putc('\r', shout);
|
|
for (i = zterm_columns - 1; i-- > 0; )
|
|
putc(' ', shout);
|
|
putc('\r', shout);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define dolist(X) ((X) >= mlbeg && (X) < mlend)
|
|
#define dolistcl(X) ((X) >= mlbeg && (X) < mlend + 1)
|
|
#define dolistnl(X) ((X) >= mlbeg && (X) < mlend - 1)
|
|
|
|
/**/
|
|
static int
|
|
compprintnl(int ml)
|
|
{
|
|
int ask;
|
|
|
|
if (mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
putc('\n', shout);
|
|
|
|
if (mscroll && !--mrestlines && (ask = asklistscroll(ml)))
|
|
return ask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This is used to print the strings (e.g. explanations). *
|
|
* It returns the number of lines printed. */
|
|
|
|
/**/
|
|
static int
|
|
compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop)
|
|
{
|
|
char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12];
|
|
int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat;
|
|
|
|
if ((stat = !fmt)) {
|
|
if (mlbeg >= 0) {
|
|
if (!(fmt = mstatus)) {
|
|
mlprinted = 0;
|
|
return 0;
|
|
}
|
|
cc = -1;
|
|
} else
|
|
fmt = mlistp;
|
|
}
|
|
MB_METACHARINIT();
|
|
for (p = fmt; *p; ) {
|
|
convchar_t cchar;
|
|
int len, width;
|
|
|
|
len = MB_METACHARLENCONV(p, &cchar);
|
|
#ifdef MULTIBYTE_SUPPORT
|
|
if (cchar == WEOF) {
|
|
cchar = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p);
|
|
width = 1;
|
|
}
|
|
else
|
|
#endif
|
|
width = WCWIDTH_WINT(cchar);
|
|
|
|
if (doesc && cchar == ZWC('%')) {
|
|
p += len;
|
|
if (*p) {
|
|
int arg = 0, is_fg;
|
|
|
|
len = MB_METACHARLENCONV(p, &cchar);
|
|
#ifdef MULTIBYTE_SUPPORT
|
|
if (cchar == WEOF)
|
|
cchar = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p);
|
|
#endif
|
|
p += len;
|
|
|
|
if (idigit(*p))
|
|
arg = zstrtol(p, &p, 10);
|
|
|
|
m = 0;
|
|
switch (cchar) {
|
|
case ZWC('%'):
|
|
if (dopr == 1)
|
|
putc('%', shout);
|
|
cc++;
|
|
break;
|
|
case ZWC('n'):
|
|
if (!stat) {
|
|
sprintf(nc, "%d", n);
|
|
if (dopr == 1)
|
|
fputs(nc, shout);
|
|
/* everything here is ASCII... */
|
|
cc += strlen(nc);
|
|
}
|
|
break;
|
|
case ZWC('B'):
|
|
b = 1;
|
|
if (dopr)
|
|
tcout(TCBOLDFACEBEG);
|
|
break;
|
|
case ZWC('b'):
|
|
b = 0; m = 1;
|
|
if (dopr)
|
|
tcout(TCALLATTRSOFF);
|
|
break;
|
|
case ZWC('S'):
|
|
s = 1;
|
|
if (dopr)
|
|
tcout(TCSTANDOUTBEG);
|
|
break;
|
|
case ZWC('s'):
|
|
s = 0; m = 1;
|
|
if (dopr)
|
|
tcout(TCSTANDOUTEND);
|
|
break;
|
|
case ZWC('U'):
|
|
u = 1;
|
|
if (dopr)
|
|
tcout(TCUNDERLINEBEG);
|
|
break;
|
|
case ZWC('u'):
|
|
u = 0; m = 1;
|
|
if (dopr)
|
|
tcout(TCUNDERLINEEND);
|
|
break;
|
|
case ZWC('F'):
|
|
case ZWC('K'):
|
|
is_fg = (cchar == ZWC('F'));
|
|
/* colours must be ASCII */
|
|
if (*p == '{') {
|
|
p++;
|
|
arg = match_colour((const char **)&p, is_fg, 0);
|
|
if (*p == '}')
|
|
p++;
|
|
} else
|
|
arg = match_colour(NULL, is_fg, arg);
|
|
if (arg >= 0 && dopr)
|
|
set_colour_attribute(arg, is_fg ? COL_SEQ_FG :
|
|
COL_SEQ_BG, 0);
|
|
break;
|
|
case ZWC('f'):
|
|
if (dopr)
|
|
set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0);
|
|
break;
|
|
case ZWC('k'):
|
|
if (dopr)
|
|
set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0);
|
|
break;
|
|
case ZWC('{'):
|
|
if (arg)
|
|
cc += arg;
|
|
for (; *p && (*p != '%' || p[1] != '}'); p++)
|
|
if (dopr)
|
|
putc(*p == Meta ? *++p ^ 32 : *p, shout);
|
|
if (*p)
|
|
p += 2;
|
|
break;
|
|
case ZWC('m'):
|
|
if (stat) {
|
|
sprintf(nc, "%d/%d", (n ? mlastm : mselect),
|
|
listdat.nlist);
|
|
m = 2;
|
|
}
|
|
break;
|
|
case ZWC('M'):
|
|
if (stat) {
|
|
sprintf(nbuf, "%d/%d", (n ? mlastm : mselect),
|
|
listdat.nlist);
|
|
sprintf(nc, "%-9s", nbuf);
|
|
m = 2;
|
|
}
|
|
break;
|
|
case ZWC('l'):
|
|
if (stat) {
|
|
sprintf(nc, "%d/%d", ml + 1, listdat.nlines);
|
|
m = 2;
|
|
}
|
|
break;
|
|
case ZWC('L'):
|
|
if (stat) {
|
|
sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines);
|
|
sprintf(nc, "%-9s", nbuf);
|
|
m = 2;
|
|
}
|
|
break;
|
|
case ZWC('p'):
|
|
if (stat) {
|
|
if (ml == listdat.nlines - 1)
|
|
strcpy(nc, "Bottom");
|
|
else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl))
|
|
sprintf(nc, "%d%%",
|
|
((ml + 1) * 100) / listdat.nlines);
|
|
else
|
|
strcpy(nc, "Top");
|
|
m = 2;
|
|
}
|
|
break;
|
|
case ZWC('P'):
|
|
if (stat) {
|
|
if (ml == listdat.nlines - 1)
|
|
strcpy(nc, "Bottom");
|
|
else if (n ? mfirstl : (mlbeg > 0 || ml != mfirstl))
|
|
sprintf(nc, "%2d%% ",
|
|
((ml + 1) * 100) / listdat.nlines);
|
|
else
|
|
strcpy(nc, "Top ");
|
|
m = 2;
|
|
}
|
|
break;
|
|
}
|
|
if (m == 2 && dopr == 1) {
|
|
/* nc only contains ASCII text */
|
|
int l = strlen(nc);
|
|
|
|
if (l + cc > zterm_columns - 2)
|
|
nc[l -= l + cc - (zterm_columns - 2)] = '\0';
|
|
fputs(nc, shout);
|
|
cc += l;
|
|
} else if (dopr && m == 1) {
|
|
if (b)
|
|
tcout(TCBOLDFACEBEG);
|
|
if (s)
|
|
tcout(TCSTANDOUTBEG);
|
|
if (u)
|
|
tcout(TCUNDERLINEBEG);
|
|
}
|
|
} else
|
|
break;
|
|
} else {
|
|
cc += width;
|
|
|
|
if ((cc >= zterm_columns - 2 || cchar == ZWC('\n')) && stat)
|
|
dopr = 2;
|
|
if (cchar == ZWC('\n')) {
|
|
if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
l += 1 + ((cc - 1) / zterm_columns);
|
|
cc = 0;
|
|
}
|
|
if (dopr == 1) {
|
|
if (ml == mlend - 1 && (cc % zterm_columns) ==
|
|
zterm_columns - 1) {
|
|
dopr = 0;
|
|
p += len;
|
|
continue;
|
|
}
|
|
while (len--) {
|
|
if (*p == Meta) {
|
|
len--;
|
|
p++;
|
|
putc(*p++ ^ 32, shout);
|
|
} else
|
|
putc(*p++, shout);
|
|
}
|
|
/*
|
|
* TODO: the following doesn't allow for
|
|
* character widths greater than 1.
|
|
*/
|
|
if ((beg = !(cc % zterm_columns)) && !stat) {
|
|
ml++;
|
|
fputs(" \010", shout);
|
|
}
|
|
if (mscroll && beg && !--mrestlines && (ask = asklistscroll(ml))) {
|
|
*stop = 1;
|
|
if (stat && n)
|
|
mfirstl = -1;
|
|
mlprinted = l + (cc ? ((cc-1) / zterm_columns) : 0);
|
|
return mlprinted;
|
|
}
|
|
}
|
|
else
|
|
p += len;
|
|
}
|
|
}
|
|
if (dopr) {
|
|
if (!(cc % zterm_columns))
|
|
fputs(" \010", shout);
|
|
if (mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
}
|
|
if (stat && n)
|
|
mfirstl = -1;
|
|
|
|
/*
|
|
* *Not* subtracting 1 from cc at this point appears to be
|
|
* correct. C.f. printfmt in zle_tricky.c.
|
|
*/
|
|
mlprinted = l + (cc / zterm_columns);
|
|
return mlprinted;
|
|
}
|
|
|
|
/* This is like zputs(), but allows scrolling. */
|
|
|
|
/**/
|
|
static int
|
|
compzputs(char const *s, int ml)
|
|
{
|
|
int c, col = 0, ask;
|
|
|
|
while (*s) {
|
|
if (*s == Meta)
|
|
c = *++s ^ 32;
|
|
else if(itok(*s)) {
|
|
s++;
|
|
continue;
|
|
} else
|
|
c = *s;
|
|
s++;
|
|
putc(c, shout);
|
|
if (c == '\n' && mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
if (mscroll && (++col == zterm_columns || c == '\n')) {
|
|
ml++;
|
|
if (!--mrestlines && (ask = asklistscroll(ml)))
|
|
return ask;
|
|
|
|
col = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
static int
|
|
compprintlist(int showall)
|
|
{
|
|
static int lasttype = 0, lastbeg = 0, lastml = 0, lastinvcount = -1;
|
|
static int lastn = 0, lastnl = 0, lastnlnct = -1;
|
|
static Cmgroup lastg = NULL;
|
|
static Cmatch *lastp = NULL;
|
|
static Cexpl *lastexpl = NULL;
|
|
|
|
Cmgroup g;
|
|
Cmatch *p, m;
|
|
Cexpl *e;
|
|
int pnl = 0, cl, mc = 0, ml = 0, printed = 0, stop = 0, asked = 1;
|
|
int lastused = 0;
|
|
|
|
mfirstl = -1;
|
|
if (mnew || lastinvcount != invcount || lastbeg != mlbeg || mlbeg < 0) {
|
|
lasttype = 0;
|
|
lastg = NULL;
|
|
lastexpl = NULL;
|
|
lastml = 0;
|
|
lastnlnct = -1;
|
|
}
|
|
cl = (listdat.nlines > zterm_lines - nlnct - mhasstat ?
|
|
zterm_lines - nlnct - mhasstat :
|
|
listdat.nlines) - (lastnlnct > nlnct);
|
|
lastnlnct = nlnct;
|
|
mrestlines = zterm_lines - 1;
|
|
lastinvcount = invcount;
|
|
|
|
if (cl < 2) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
} else if (mlbeg >= 0 && !tccan(TCCLEAREOL) && tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
|
|
g = ((lasttype && lastg) ? lastg : amatches);
|
|
while (g && !errflag) {
|
|
char **pp = g->ylist;
|
|
|
|
#ifdef ZSH_HEAP_DEBUG
|
|
if (memory_validate(g->heap_id)) {
|
|
HEAP_ERROR(g->heap_id);
|
|
}
|
|
#endif
|
|
if ((e = g->expls)) {
|
|
if (!lastused && lasttype == 1) {
|
|
e = lastexpl;
|
|
ml = lastml;
|
|
lastused = 1;
|
|
}
|
|
while (*e && !errflag) {
|
|
if (((*e)->count || (*e)->always) &&
|
|
(!listdat.onlyexpl ||
|
|
(listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) {
|
|
if (pnl) {
|
|
if (dolistnl(ml) && compprintnl(ml))
|
|
goto end;
|
|
pnl = 0;
|
|
ml++;
|
|
if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
}
|
|
if (mlbeg < 0 && mfirstl < 0)
|
|
mfirstl = ml;
|
|
(void)compprintfmt((*e)->str,
|
|
((*e)->always ? -1 : (*e)->count),
|
|
dolist(ml), 1, ml, &stop);
|
|
if (mselect >= 0) {
|
|
int mm = (mcols * ml), i;
|
|
|
|
for (i = mcols; i-- > 0; ) {
|
|
DPUTS(mm+i >= mgtabsize, "BUG: invalid position");
|
|
mtab[mm + i] = mtmark(NULL);
|
|
mgtab[mm + i] = mgmark(NULL);
|
|
}
|
|
}
|
|
if (stop)
|
|
goto end;
|
|
if (!lasttype && ml >= mlbeg) {
|
|
lasttype = 1;
|
|
lastg = g;
|
|
lastbeg = mlbeg;
|
|
lastml = ml;
|
|
lastexpl = e;
|
|
lastp = NULL;
|
|
lastused = 1;
|
|
}
|
|
ml += mlprinted;
|
|
if (dolistcl(ml) && cl >= 0 && (cl -= mlprinted) <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
pnl = 1;
|
|
}
|
|
e++;
|
|
if (!mnew && ml > mlend)
|
|
goto end;
|
|
}
|
|
}
|
|
if (!listdat.onlyexpl && mlbeg < 0 && pp && *pp) {
|
|
if (pnl) {
|
|
if (dolistnl(ml) && compprintnl(ml))
|
|
goto end;
|
|
pnl = 0;
|
|
ml++;
|
|
if (cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
}
|
|
if (mlbeg < 0 && mfirstl < 0)
|
|
mfirstl = ml;
|
|
if (g->flags & CGF_LINES) {
|
|
while (*pp) {
|
|
if (compzputs(*pp, ml))
|
|
goto end;
|
|
if (*++pp && compprintnl(ml))
|
|
goto end;
|
|
}
|
|
} else {
|
|
int n = g->lcount, nl, nc, i, a;
|
|
char **pq;
|
|
|
|
nl = nc = g->lins;
|
|
|
|
while (n && nl-- && !errflag) {
|
|
i = g->cols;
|
|
mc = 0;
|
|
pq = pp;
|
|
while (n && i-- && !errflag) {
|
|
if (pq - g->ylist >= g->lcount)
|
|
break;
|
|
if (compzputs(*pq, mscroll))
|
|
goto end;
|
|
if (i) {
|
|
a = (g->widths ? g->widths[mc] : g->width) -
|
|
strlen(*pq);
|
|
while (a--)
|
|
putc(' ', shout);
|
|
}
|
|
pq += ((g->flags & CGF_ROWS) ? 1 : nc);
|
|
mc++;
|
|
n--;
|
|
}
|
|
if (n) {
|
|
if (compprintnl(ml))
|
|
goto end;
|
|
ml++;
|
|
if (cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
}
|
|
pp += ((g->flags & CGF_ROWS) ? g->cols : 1);
|
|
}
|
|
}
|
|
} else if (!listdat.onlyexpl &&
|
|
(g->lcount || (showall && g->mcount))) {
|
|
int n = g->dcount, nl, nc, i, j, wid;
|
|
Cmatch *q;
|
|
|
|
nl = nc = g->lins;
|
|
|
|
if ((g->flags & CGF_HASDL) &&
|
|
(lastused || !lasttype || lasttype == 2)) {
|
|
if (!lastused && lasttype == 2) {
|
|
p = lastp;
|
|
ml = lastml;
|
|
n = lastn;
|
|
nl = lastnl;
|
|
lastused = 1;
|
|
pnl = 0;
|
|
} else
|
|
p = g->matches;
|
|
|
|
for (; (m = *p); p++) {
|
|
if (m->disp && (m->flags & CMF_DISPLINE) &&
|
|
(showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) {
|
|
if (pnl) {
|
|
if (dolistnl(ml) && compprintnl(ml))
|
|
goto end;
|
|
pnl = 0;
|
|
ml++;
|
|
if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
}
|
|
if (!lasttype && ml >= mlbeg) {
|
|
lasttype = 2;
|
|
lastg = g;
|
|
lastbeg = mlbeg;
|
|
lastml = ml;
|
|
lastp = p;
|
|
lastn = n;
|
|
lastnl = nl;
|
|
lastused = 1;
|
|
}
|
|
if (mfirstl < 0)
|
|
mfirstl = ml;
|
|
if (dolist(ml))
|
|
printed++;
|
|
if (clprintm(g, p, 0, ml, 1, 0))
|
|
goto end;
|
|
ml += mlprinted;
|
|
if (dolistcl(ml) && (cl -= mlprinted) <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
pnl = 1;
|
|
}
|
|
if (!mnew && ml > mlend)
|
|
goto end;
|
|
}
|
|
}
|
|
if (n && pnl) {
|
|
if (dolistnl(ml) && compprintnl(ml))
|
|
goto end;
|
|
pnl = 0;
|
|
ml++;
|
|
if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
}
|
|
if (!lastused && lasttype == 3) {
|
|
p = lastp;
|
|
n = lastn;
|
|
nl = lastnl;
|
|
ml = lastml;
|
|
lastused = 1;
|
|
} else
|
|
p = skipnolist(g->matches, showall);
|
|
|
|
while (n && nl-- && !errflag) {
|
|
if (!lasttype && ml >= mlbeg) {
|
|
lasttype = 3;
|
|
lastg = g;
|
|
lastbeg = mlbeg;
|
|
lastml = ml;
|
|
lastp = p;
|
|
lastn = n;
|
|
lastnl = nl + 1;
|
|
lastused = 1;
|
|
}
|
|
i = g->cols;
|
|
mc = 0;
|
|
q = p;
|
|
while (n && i-- && !errflag) {
|
|
wid = (g->widths ? g->widths[mc] : g->width);
|
|
if (!(m = *q)) {
|
|
if (clprintm(g, NULL, mc, ml, (!i), wid))
|
|
goto end;
|
|
break;
|
|
}
|
|
if (clprintm(g, q, mc, ml, (!i), wid))
|
|
goto end;
|
|
|
|
if (dolist(ml))
|
|
printed++;
|
|
ml += mlprinted;
|
|
if (dolistcl(ml) && (cl -= mlprinted) < 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
if (mfirstl < 0)
|
|
mfirstl = ml;
|
|
|
|
if (--n)
|
|
for (j = ((g->flags & CGF_ROWS) ? 1 : nc);
|
|
j && *q; j--)
|
|
q = skipnolist(q + 1, showall);
|
|
mc++;
|
|
}
|
|
while (i-- > 0) {
|
|
if (clprintm(g, NULL, mc, ml, (!i),
|
|
(g->widths ? g->widths[mc] : g->width)))
|
|
goto end;
|
|
mc++;
|
|
}
|
|
if (n) {
|
|
if (dolistnl(ml) && compprintnl(ml))
|
|
goto end;
|
|
ml++;
|
|
if (dolistcl(ml) && cl >= 0 && --cl <= 1) {
|
|
cl = -1;
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
}
|
|
if (nl)
|
|
for (j = ((g->flags & CGF_ROWS) ? g->cols : 1);
|
|
j && *p; j--)
|
|
p = skipnolist(p + 1, showall);
|
|
}
|
|
if (!mnew && ml > mlend)
|
|
goto end;
|
|
}
|
|
}
|
|
if (g->lcount || (showall && g->mcount))
|
|
pnl = 1;
|
|
g = g->next;
|
|
}
|
|
asked = 0;
|
|
end:
|
|
mstatprinted = 0;
|
|
lastlistlen = 0;
|
|
if (nlnct <= 1)
|
|
mscroll = 0;
|
|
if (clearflag) {
|
|
int nl;
|
|
|
|
/* Move the cursor up to the prompt, if always_last_prompt *
|
|
* is set and all that... */
|
|
if (mlbeg >= 0) {
|
|
if ((nl = listdat.nlines + nlnct) >= zterm_lines) {
|
|
if (mhasstat) {
|
|
putc('\n', shout);
|
|
compprintfmt(NULL, 0, 1, 1, mline, NULL);
|
|
mstatprinted = 1;
|
|
}
|
|
nl = zterm_lines - 1;
|
|
} else
|
|
nl--;
|
|
tcmultout(TCUP, TCMULTUP, nl);
|
|
showinglist = -1;
|
|
|
|
lastlistlen = listdat.nlines;
|
|
} else if ((nl = listdat.nlines + nlnct - 1) < zterm_lines) {
|
|
if (mlbeg >= 0 && tccan(TCCLEAREOL))
|
|
tcout(TCCLEAREOL);
|
|
tcmultout(TCUP, TCMULTUP, nl);
|
|
showinglist = -1;
|
|
|
|
lastlistlen = listdat.nlines;
|
|
} else {
|
|
clearflag = 0;
|
|
if (!asked) {
|
|
mrestlines = (ml + nlnct > zterm_lines);
|
|
compprintnl(ml);
|
|
}
|
|
}
|
|
} else if (!asked) {
|
|
mrestlines = (ml + nlnct > zterm_lines);
|
|
compprintnl(ml);
|
|
}
|
|
listshown = (clearflag ? 1 : -1);
|
|
mnew = 0;
|
|
|
|
return printed;
|
|
}
|
|
|
|
/**/
|
|
static int
|
|
clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width)
|
|
{
|
|
Cmatch m;
|
|
int len, subcols = 0, stop = 0, ret = 0;
|
|
|
|
DPUTS2(mselect >= 0 && ml >= mlines,
|
|
"clprintm called with ml too large (%d/%d)",
|
|
ml, mlines);
|
|
if (g != last_group)
|
|
*last_cap = '\0';
|
|
|
|
last_group = g;
|
|
|
|
if (!mp) {
|
|
if (dolist(ml)) {
|
|
zcputs(g->name, COL_SP);
|
|
len = width - 2;
|
|
while (len-- > 0)
|
|
putc(' ', shout);
|
|
zcoff();
|
|
}
|
|
mlprinted = 0;
|
|
return 0;
|
|
}
|
|
m = *mp;
|
|
|
|
if ((m->flags & CMF_ALL) && (!m->disp || !m->disp[0]))
|
|
bld_all_str(m);
|
|
|
|
mlastm = m->gnum;
|
|
if (m->disp && (m->flags & CMF_DISPLINE)) {
|
|
if (mselect >= 0) {
|
|
int mm = (mcols * ml), i;
|
|
|
|
if (m->flags & CMF_DUMMY) {
|
|
for (i = mcols; i-- > 0; ) {
|
|
DPUTS(mm+i >= mgtabsize, "BUG: invalid position");
|
|
mtab[mm + i] = mtmark(mp);
|
|
mgtab[mm + i] = mgmark(g);
|
|
}
|
|
} else {
|
|
for (i = mcols; i-- > 0; ) {
|
|
DPUTS(mm+i >= mgtabsize, "BUG: invalid position");
|
|
mtab[mm + i] = mp;
|
|
mgtab[mm + i] = g;
|
|
}
|
|
}
|
|
}
|
|
if (!dolist(ml)) {
|
|
mlprinted = printfmt(m->disp, 0, 0, 0);
|
|
return 0;
|
|
}
|
|
if (m->gnum == mselect) {
|
|
int mm = (mcols * ml);
|
|
DPUTS(mm >= mgtabsize, "BUG: invalid position");
|
|
mline = ml;
|
|
mcol = 0;
|
|
mmtabp = mtab + mm;
|
|
mgtabp = mgtab + mm;
|
|
zcputs(g->name, COL_MA);
|
|
} else if ((m->flags & CMF_NOLIST) &&
|
|
mcolors.files[COL_HI] && mcolors.files[COL_HI]->col)
|
|
zcputs(g->name, COL_HI);
|
|
else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)) &&
|
|
mcolors.files[COL_DU] && mcolors.files[COL_DU]->col)
|
|
zcputs(g->name, COL_DU);
|
|
else
|
|
subcols = putmatchcol(g->name, m->disp);
|
|
if (subcols)
|
|
ret = clprintfmt(m->disp, ml);
|
|
else {
|
|
compprintfmt(m->disp, 0, 1, 0, ml, &stop);
|
|
if (stop)
|
|
ret = 1;
|
|
}
|
|
zcoff();
|
|
} else {
|
|
int mx, modec;
|
|
|
|
if (g->widths) {
|
|
int i;
|
|
|
|
for (i = mx = 0; i < mc; i++)
|
|
mx += g->widths[i];
|
|
} else
|
|
mx = mc * g->width;
|
|
|
|
if (mselect >= 0) {
|
|
int mm = mcols * ml, i;
|
|
|
|
if (m->flags & CMF_DUMMY) {
|
|
for (i = (width ? width : mcols); i-- > 0; ) {
|
|
DPUTS(mx+mm+i >= mgtabsize, "BUG: invalid position");
|
|
mtab[mx + mm + i] = mtmark(mp);
|
|
mgtab[mx + mm + i] = mgmark(g);
|
|
}
|
|
} else {
|
|
for (i = (width ? width : mcols); i-- > 0; ) {
|
|
DPUTS(mx+mm+i >= mgtabsize, "BUG: invalid position");
|
|
mtab[mx + mm + i] = mp;
|
|
mgtab[mx + mm + i] = g;
|
|
}
|
|
}
|
|
}
|
|
if (!dolist(ml)) {
|
|
int nc = ZMB_nicewidth(m->disp ? m->disp : m->str);
|
|
if (nc)
|
|
mlprinted = (nc-1) / zterm_columns;
|
|
else
|
|
mlprinted = 0;
|
|
return 0;
|
|
}
|
|
if (m->gnum == mselect) {
|
|
int mm = mcols * ml;
|
|
DPUTS(mx+mm >= mgtabsize, "BUG: invalid position");
|
|
|
|
mcol = mx;
|
|
mline = ml;
|
|
mmtabp = mtab + mx + mm;
|
|
mgtabp = mgtab + mx + mm;
|
|
zcputs(g->name, COL_MA);
|
|
} else if (m->flags & CMF_NOLIST)
|
|
zcputs(g->name, COL_HI);
|
|
else if (mselect >= 0 && (m->flags & (CMF_MULT | CMF_FMULT)))
|
|
zcputs(g->name, COL_DU);
|
|
else if (m->mode) {
|
|
/*
|
|
* Symlink is orphaned if we read the mode with lstat
|
|
* but couldn't read one with stat. That's the
|
|
* only way they can be different so the following
|
|
* test should be enough.
|
|
*/
|
|
int orphan_colour = (m->mode && !m->fmode) ? COL_OR : -1;
|
|
if (mcolors.flags & LC_FOLLOW_SYMLINKS) {
|
|
subcols = putfilecol(g->name, m->str, m->fmode, orphan_colour);
|
|
} else {
|
|
subcols = putfilecol(g->name, m->str, m->mode, orphan_colour);
|
|
}
|
|
}
|
|
else
|
|
subcols = putmatchcol(g->name, (m->disp ? m->disp : m->str));
|
|
|
|
ret = clnicezputs(subcols,
|
|
(m->disp ? m->disp : m->str), ml);
|
|
if (ret) {
|
|
zcoff();
|
|
return 1;
|
|
}
|
|
len = ZMB_nicewidth(m->disp ? m->disp : m->str);
|
|
mlprinted = len ? (len-1) / zterm_columns : 0;
|
|
|
|
modec = (mcolors.flags & LC_FOLLOW_SYMLINKS) ? m->fmodec : m->modec;
|
|
if ((g->flags & CGF_FILES) && modec) {
|
|
if (m->gnum != mselect) {
|
|
zcoff();
|
|
zcputs(g->name, COL_TC);
|
|
}
|
|
putc(modec, shout);
|
|
len++;
|
|
}
|
|
if ((len = width - len - 2) > 0) {
|
|
if (m->gnum != mselect) {
|
|
zcoff();
|
|
zcputs(g->name, COL_SP);
|
|
}
|
|
while (len-- > 0)
|
|
putc(' ', shout);
|
|
}
|
|
zcoff();
|
|
if (!lastc) {
|
|
zcputs(g->name, COL_SP);
|
|
fputs(" ", shout);
|
|
zcoff();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
singlecalc(int *cp, int l, int *lcp)
|
|
{
|
|
int c = *cp, n, j, first = 1;
|
|
Cmatch **p, *op, *mp = mtab[l * zterm_columns + c];
|
|
|
|
for (n = 0, j = c, p = mtab + l * zterm_columns + c, op = NULL;
|
|
j >= 0;
|
|
j--, p--) {
|
|
if (*p == mp)
|
|
c = j;
|
|
if (!first && *p != op)
|
|
n++;
|
|
op = *p;
|
|
first = 0;
|
|
}
|
|
*cp = c;
|
|
*lcp = 1;
|
|
for (p = mtab + l * zterm_columns + c; c < zterm_columns; c++, p++)
|
|
if (*p && mp != *p)
|
|
*lcp = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
singledraw(void)
|
|
{
|
|
Cmgroup g;
|
|
int mc1, mc2, ml1, ml2, md1, md2, mcc1, mcc2, lc1, lc2, t1, t2;
|
|
|
|
t1 = mline - mlbeg;
|
|
t2 = moline - molbeg;
|
|
|
|
if (t2 < t1) {
|
|
mc1 = mocol; ml1 = moline; md1 = t2;
|
|
mc2 = mcol; ml2 = mline; md2 = t1;
|
|
} else {
|
|
mc1 = mcol; ml1 = mline; md1 = t1;
|
|
mc2 = mocol; ml2 = moline; md2 = t2;
|
|
}
|
|
mcc1 = singlecalc(&mc1, ml1, &lc1);
|
|
mcc2 = singlecalc(&mc2, ml2, &lc2);
|
|
|
|
if (md1)
|
|
tc_downcurs(md1);
|
|
if (mc1)
|
|
tcmultout(TCRIGHT, TCMULTRIGHT, mc1);
|
|
DPUTS(ml1 * zterm_columns + mc1 >= mgtabsize, "BUG: invalid position");
|
|
g = mgtab[ml1 * zterm_columns + mc1];
|
|
clprintm(g, mtab[ml1 * zterm_columns + mc1], mcc1, ml1, lc1,
|
|
(g->widths ? g->widths[mcc1] : g->width));
|
|
if (mlprinted)
|
|
(void) tcmultout(TCUP, TCMULTUP, mlprinted);
|
|
putc('\r', shout);
|
|
|
|
if (md2 != md1)
|
|
tc_downcurs(md2 - md1);
|
|
if (mc2)
|
|
tcmultout(TCRIGHT, TCMULTRIGHT, mc2);
|
|
DPUTS(ml2 * zterm_columns + mc2 >= mgtabsize, "BUG: invalid position");
|
|
g = mgtab[ml2 * zterm_columns + mc2];
|
|
clprintm(g, mtab[ml2 * zterm_columns + mc2], mcc2, ml2, lc2,
|
|
(g->widths ? g->widths[mcc2] : g->width));
|
|
if (mlprinted)
|
|
(void) tcmultout(TCUP, TCMULTUP, mlprinted);
|
|
putc('\r', shout);
|
|
|
|
if (mstatprinted) {
|
|
int i = zterm_lines - md2 - nlnct;
|
|
|
|
tc_downcurs(i - 1);
|
|
compprintfmt(NULL, 0, 1, 1, mline, NULL);
|
|
tcmultout(TCUP, TCMULTUP, zterm_lines - 1);
|
|
} else
|
|
tcmultout(TCUP, TCMULTUP, md2 + nlnct);
|
|
|
|
showinglist = -1;
|
|
listshown = 1;
|
|
}
|
|
|
|
static int
|
|
complistmatches(UNUSED(Hookdef dummy), Chdata dat)
|
|
{
|
|
static int onlnct = -1;
|
|
static int extendedglob;
|
|
|
|
Cmgroup oamatches = amatches;
|
|
|
|
amatches = dat->matches;
|
|
#ifdef ZSH_HEAP_DEBUG
|
|
if (memory_validate(amatches->heap_id)) {
|
|
HEAP_ERROR(amatches->heap_id);
|
|
}
|
|
#endif
|
|
|
|
if (noselect > 0)
|
|
noselect = 0;
|
|
|
|
if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines ||
|
|
errflag) {
|
|
showinglist = 0;
|
|
amatches = oamatches;
|
|
return (noselect = 1);
|
|
}
|
|
|
|
/*
|
|
* There's a lot of memory allocation from this function
|
|
* for setting up the color display which isn't needed
|
|
* after the function exits, so it's worthwhile pushing
|
|
* another heap. As this is called from a hook in the main
|
|
* completion handler nothing temporarily allocated from here can be
|
|
* useful outside.
|
|
*/
|
|
pushheap();
|
|
extendedglob = opts[EXTENDEDGLOB];
|
|
opts[EXTENDEDGLOB] = 1;
|
|
|
|
getcols();
|
|
|
|
mnew = ((calclist(mselect >= 0) || mlastcols != zterm_columns ||
|
|
mlastlines != listdat.nlines) && mselect >= 0);
|
|
|
|
if (!listdat.nlines || (mselect >= 0 &&
|
|
!(isset(USEZLE) && !termflags &&
|
|
complastprompt && *complastprompt))) {
|
|
showinglist = listshown = 0;
|
|
noselect = 1;
|
|
amatches = oamatches;
|
|
popheap();
|
|
opts[EXTENDEDGLOB] = extendedglob;
|
|
return 1;
|
|
}
|
|
if (inselect || mlbeg >= 0)
|
|
clearflag = 0;
|
|
|
|
mscroll = 0;
|
|
mlistp = NULL;
|
|
|
|
queue_signals();
|
|
if (mselect >= 0 || mlbeg >= 0 ||
|
|
(mlistp = dupstring(getsparam("LISTPROMPT")))) {
|
|
unqueue_signals();
|
|
if (mlistp && !*mlistp)
|
|
mlistp = "%SAt %p: Hit TAB for more, or the character to insert%s";
|
|
trashzle();
|
|
showinglist = listshown = 0;
|
|
|
|
lastlistlen = 0;
|
|
|
|
if (mlistp) {
|
|
clearflag = (isset(USEZLE) && !termflags && dolastprompt);
|
|
mscroll = 1;
|
|
} else {
|
|
clearflag = 1;
|
|
minfo.asked = (listdat.nlines + nlnct <= zterm_lines);
|
|
}
|
|
} else {
|
|
unqueue_signals();
|
|
mlistp = NULL;
|
|
if (asklist()) {
|
|
amatches = oamatches;
|
|
popheap();
|
|
opts[EXTENDEDGLOB] = extendedglob;
|
|
return (noselect = 1);
|
|
}
|
|
}
|
|
if (mlbeg >= 0) {
|
|
mlend = mlbeg + zterm_lines - nlnct - mhasstat;
|
|
while (mline >= mlend)
|
|
mlbeg++, mlend++;
|
|
} else
|
|
mlend = 9999999;
|
|
|
|
if (mnew) {
|
|
int i;
|
|
|
|
mtab_been_reallocated = 1;
|
|
|
|
i = zterm_columns * listdat.nlines;
|
|
free(mtab);
|
|
mtab = (Cmatch **) zalloc(i * sizeof(Cmatch *));
|
|
memset(mtab, 0, i * sizeof(Cmatch *));
|
|
free(mgtab);
|
|
mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
|
|
#ifdef DEBUG
|
|
mgtabsize = i;
|
|
#endif
|
|
memset(mgtab, 0, i * sizeof(Cmgroup));
|
|
mlastcols = mcols = zterm_columns;
|
|
mlastlines = mlines = listdat.nlines;
|
|
mmtabp = 0;
|
|
}
|
|
last_cap = (char *) zhalloc(max_caplen + 1);
|
|
*last_cap = '\0';
|
|
|
|
if (!mnew && inselect &&
|
|
onlnct == nlnct && mlbeg >= 0 && mlbeg == molbeg) {
|
|
if (!noselect)
|
|
singledraw();
|
|
} else if (!compprintlist(mselect >= 0) || !clearflag)
|
|
noselect = 1;
|
|
|
|
onlnct = nlnct;
|
|
molbeg = mlbeg;
|
|
mocol = mcol;
|
|
moline = mline;
|
|
|
|
amatches = oamatches;
|
|
|
|
popheap();
|
|
opts[EXTENDEDGLOB] = extendedglob;
|
|
|
|
return (noselect < 0 ? 0 : noselect);
|
|
}
|
|
|
|
static int
|
|
adjust_mcol(int wish, Cmatch ***tabp, Cmgroup **grp)
|
|
{
|
|
Cmatch **matchtab = *tabp;
|
|
int p, n, c;
|
|
|
|
matchtab -= mcol;
|
|
|
|
for (p = wish; p >= 0 && (!matchtab[p] || mmarked(matchtab[p])); p--);
|
|
for (n = wish; n < mcols && (!matchtab[n] || mmarked(matchtab[n])); n++);
|
|
if (n == mcols)
|
|
n = -1;
|
|
|
|
if (p < 0) {
|
|
if (n < 0)
|
|
return 1;
|
|
c = n;
|
|
} else if (n < 0)
|
|
c = p;
|
|
else
|
|
c = ((mcol - p) < (n - mcol) ? p : n);
|
|
|
|
*tabp = matchtab + c;
|
|
if (grp)
|
|
*grp = *grp + c - mcol;
|
|
|
|
mcol = c;
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef struct menustack *Menustack;
|
|
|
|
struct menustack {
|
|
Menustack prev;
|
|
char *line;
|
|
Brinfo brbeg;
|
|
Brinfo brend;
|
|
int nbrbeg, nbrend;
|
|
int cs, acc, nmatches, mline, mlbeg, nolist;
|
|
struct menuinfo info;
|
|
Cmgroup amatches, pmatches, lastmatches, lastlmatches;
|
|
/*
|
|
* Status for how line looked like previously.
|
|
*/
|
|
char *origline;
|
|
int origcs, origll;
|
|
/*
|
|
* Status for interactive mode. status is the line
|
|
* printed above the matches saying what the interactive
|
|
* completion prefix is. mode says whether we are in
|
|
* interactive or some search mode.
|
|
* typed.
|
|
*/
|
|
char *status;
|
|
int mode;
|
|
};
|
|
|
|
typedef struct menusearch *Menusearch;
|
|
|
|
struct menusearch {
|
|
Menusearch prev;
|
|
char *str;
|
|
int line;
|
|
int col;
|
|
int back;
|
|
int state;
|
|
Cmatch **ptr;
|
|
};
|
|
|
|
#define MS_OK 0
|
|
#define MS_FAILED 1
|
|
#define MS_WRAPPED 2
|
|
|
|
#define MAX_STATUS 128
|
|
|
|
static char *
|
|
setmstatus(char *status, char *sline, int sll, int scs,
|
|
int *csp, int *llp, int *lenp)
|
|
{
|
|
char *p, *s, *ret = NULL;
|
|
int pl, sl, max;
|
|
|
|
METACHECK();
|
|
|
|
if (csp) {
|
|
*csp = zlemetacs;
|
|
*llp = zlemetall;
|
|
*lenp = lastend - wb;
|
|
|
|
ret = dupstring(zlemetaline);
|
|
|
|
p = (char *) zhalloc(zlemetacs - wb + 1);
|
|
strncpy(p, zlemetaline + wb, zlemetacs - wb);
|
|
p[zlemetacs - wb] = '\0';
|
|
if (lastend < zlemetacs)
|
|
s = "";
|
|
else {
|
|
s = (char *) zhalloc(lastend - zlemetacs + 1);
|
|
strncpy(s, zlemetaline + zlemetacs, lastend - zlemetacs);
|
|
s[lastend - zlemetacs] = '\0';
|
|
}
|
|
zlemetacs = 0;
|
|
foredel(zlemetall, CUT_RAW);
|
|
spaceinline(sll);
|
|
memcpy(zlemetaline, sline, sll);
|
|
zlemetacs = scs;
|
|
} else {
|
|
p = complastprefix;
|
|
s = complastsuffix;
|
|
}
|
|
pl = strlen(p);
|
|
sl = strlen(s);
|
|
max = (zterm_columns < MAX_STATUS ? zterm_columns : MAX_STATUS) - 14;
|
|
|
|
if (max > 12) {
|
|
int h = (max - 2) >> 1;
|
|
|
|
strcpy(status, "interactive: ");
|
|
if (pl > h - 3) {
|
|
strcat(status, "...");
|
|
strcat(status, p + pl - h - 3);
|
|
} else
|
|
strcat(status, p);
|
|
|
|
strcat(status, "[]");
|
|
if (sl > h - 3) {
|
|
strncat(status, s, h - 3);
|
|
strcat(status, "...");
|
|
} else
|
|
strcat(status, s);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static Menusearch msearchstack;
|
|
static char *msearchstr = NULL;
|
|
static int msearchstate;
|
|
|
|
static void
|
|
msearchpush(Cmatch **p, int back)
|
|
{
|
|
Menusearch s = (Menusearch) zhalloc(sizeof(struct menusearch));
|
|
|
|
s->prev = msearchstack;
|
|
msearchstack = s;
|
|
s->str = dupstring(msearchstr);
|
|
s->line = mline;
|
|
s->col = mcol;
|
|
s->back = back;
|
|
s->state = msearchstate;
|
|
s->ptr = p;
|
|
}
|
|
|
|
static Cmatch **
|
|
msearchpop(int *backp)
|
|
{
|
|
Menusearch s = msearchstack;
|
|
|
|
if (!s)
|
|
return NULL;
|
|
|
|
if (s->prev)
|
|
msearchstack = s->prev;
|
|
|
|
msearchstr = s->str;
|
|
mline = s->line;
|
|
mcol = s->col;
|
|
msearchstate = s->state;
|
|
|
|
*backp = s->back;
|
|
|
|
return s->ptr;
|
|
}
|
|
|
|
static Cmatch **
|
|
msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp)
|
|
{
|
|
Cmatch **p, *l = NULL, m;
|
|
int x = mcol, y = mline;
|
|
int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED);
|
|
|
|
msearchpush(ptr, back);
|
|
|
|
if (ins)
|
|
msearchstr = dyncat(msearchstr, ins);
|
|
if (back) {
|
|
ex = mcols - 1;
|
|
ey = -1;
|
|
} else {
|
|
ex = 0;
|
|
ey = listdat.nlines;
|
|
}
|
|
p = mtab + (mline * mcols) + mcol;
|
|
if (rep)
|
|
l = *p;
|
|
while (1) {
|
|
if (!rep && mtunmark(*p) && *p != l) {
|
|
l = *p;
|
|
m = *mtunmark(*p);
|
|
|
|
if (strstr((m->disp ? m->disp : m->str), msearchstr)) {
|
|
mcol = x;
|
|
mline = y;
|
|
|
|
return p;
|
|
}
|
|
}
|
|
rep = 0;
|
|
|
|
if (back) {
|
|
p--;
|
|
if (--x < 0) {
|
|
x = mcols - 1;
|
|
y--;
|
|
}
|
|
} else {
|
|
p++;
|
|
if (++x == mcols) {
|
|
x = 0;
|
|
y++;
|
|
}
|
|
}
|
|
if (x == ex && y == ey) {
|
|
if (wrap) {
|
|
msearchstate = MS_FAILED | owrap;
|
|
break;
|
|
}
|
|
msearchstate |= MS_WRAPPED;
|
|
|
|
if (back) {
|
|
x = mcols - 1;
|
|
y = listdat.nlines - 1;
|
|
p = mtab + (y * mcols) + x;
|
|
} else {
|
|
x = y = 0;
|
|
p = mtab;
|
|
}
|
|
ex = mcol;
|
|
ey = mline;
|
|
wrap = 1;
|
|
*wrapp = 1;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Values to assign to mode: interactive, etc.
|
|
*/
|
|
#define MM_INTER 1
|
|
#define MM_FSEARCH 2
|
|
#define MM_BSEARCH 3
|
|
|
|
static int
|
|
domenuselect(Hookdef dummy, Chdata dat)
|
|
{
|
|
static Chdata fdat = NULL;
|
|
static char *lastsearch = NULL;
|
|
Cmatch **p;
|
|
Cmgroup *pg;
|
|
Thingy cmd = 0;
|
|
int do_last_key = 0;
|
|
Menustack u = NULL;
|
|
int i = 0, acc = 0, wishcol = 0, setwish = 0, oe = onlyexpl, wasnext = 0;
|
|
int space, lbeg = 0, step = 1, wrap, pl = nlnct, broken = 0, first = 1;
|
|
int nolist = 0, mode = 0, modecs, modell, modelen, wasmeta;
|
|
char *s;
|
|
char status[MAX_STATUS], *modeline = NULL;
|
|
|
|
msearchstack = NULL;
|
|
msearchstr = "";
|
|
msearchstate = MS_OK;
|
|
|
|
status[0] = '\0';
|
|
queue_signals();
|
|
if (fdat || (dummy && (!(s = getsparam("MENUSELECT")) ||
|
|
(dat && dat->num < atoi(s))))) {
|
|
if (fdat) {
|
|
fdat->matches = dat->matches;
|
|
fdat->num = dat->num;
|
|
fdat->nmesg = dat->nmesg;
|
|
}
|
|
unqueue_signals();
|
|
return 0;
|
|
}
|
|
/*
|
|
* Lots of the logic here doesn't really make sense if the
|
|
* line isn't metafied, but the evidence was that it only used
|
|
* to be metafied locally in a couple of places.
|
|
* It's horrifically difficult to work out where the line
|
|
* is metafied, so I've resorted to the following.
|
|
* Unfortunately we need to unmetafy in zrefresh() when
|
|
* we want to display something. Maybe this function can
|
|
* be done better.
|
|
*/
|
|
if (zlemetaline != NULL)
|
|
wasmeta = 1;
|
|
else {
|
|
wasmeta = 0;
|
|
metafy_line();
|
|
}
|
|
|
|
if ((s = getsparam("MENUSCROLL"))) {
|
|
if (!(step = mathevali(s)))
|
|
step = (zterm_lines - nlnct) >> 1;
|
|
else if (step < 0)
|
|
if ((step += zterm_lines - nlnct) < 0)
|
|
step = 1;
|
|
}
|
|
if ((s = getsparam("MENUMODE"))) {
|
|
if (!strcmp(s, "interactive")) {
|
|
int l = strlen(origline);
|
|
|
|
/*
|
|
* In interactive completion mode we don't insert
|
|
* the completion onto the command line, instead
|
|
* we show just what the user has typed and
|
|
* the match so far underneath (stored in "status").
|
|
* So put the command line back to how it
|
|
* was before completion started.
|
|
*/
|
|
mode = MM_INTER;
|
|
zlemetacs = 0;
|
|
foredel(zlemetall, CUT_RAW);
|
|
spaceinline(l);
|
|
strncpy(zlemetaline, origline, l);
|
|
zlemetacs = origcs;
|
|
setmstatus(status, NULL, 0 , 0, NULL, NULL, NULL);
|
|
} else if (strpfx("search", s)) {
|
|
mode = (strstr(s, "back") ? MM_BSEARCH : MM_FSEARCH);
|
|
}
|
|
}
|
|
if ((mstatus = dupstring(getsparam("MENUPROMPT"))) && !*mstatus)
|
|
mstatus = "%SScrolling active: current selection at %p%s";
|
|
unqueue_signals();
|
|
mhasstat = (mstatus && *mstatus);
|
|
fdat = dat;
|
|
menuselect_bindings(); /* sanity in case deleted by user */
|
|
selectlocalmap(mskeymap);
|
|
noselect = 1;
|
|
while ((menuacc &&
|
|
!hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) ||
|
|
((*minfo.cur)->flags & CMF_DUMMY) ||
|
|
(((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) &&
|
|
(!(*minfo.cur)->str || !*(*minfo.cur)->str)))
|
|
do_menucmp(0);
|
|
|
|
mselect = (*(minfo.cur))->gnum;
|
|
mline = 0;
|
|
mlines = 999999;
|
|
mlbeg = 0;
|
|
molbeg = -42;
|
|
mtab_been_reallocated = 0;
|
|
for (;;) {
|
|
METACHECK();
|
|
|
|
if (mline < 0 || mtab_been_reallocated) {
|
|
int x, y;
|
|
Cmatch **p = mtab;
|
|
|
|
for (y = 0; y < mlines; y++) {
|
|
for (x = mcols; x > 0; x--, p++)
|
|
if (*p && !mmarked(*p) && **p && mselect == (**p)->gnum)
|
|
break;
|
|
if (x) {
|
|
mcol = mcols - x;
|
|
break;
|
|
}
|
|
}
|
|
if (y < mlines)
|
|
mline = y;
|
|
}
|
|
mtab_been_reallocated = 0;
|
|
DPUTS(mline < 0,
|
|
"BUG: mline < 0 after re-scanning mtab in domenuselect()");
|
|
while (mline < mlbeg)
|
|
if ((mlbeg -= step) < 0) {
|
|
mlbeg = 0;
|
|
/* Crude workaround for BUG above */
|
|
if (mline < 0)
|
|
break;
|
|
}
|
|
|
|
if (mlbeg && lbeg != mlbeg) {
|
|
Cmatch **p = mtab + ((mlbeg - 1) * zterm_columns), **q;
|
|
int c;
|
|
|
|
while (mlbeg) {
|
|
for (q = p, c = zterm_columns; c > 0; q++, c--)
|
|
if (*q && !mmarked(*q))
|
|
break;
|
|
if (c)
|
|
break;
|
|
p -= zterm_columns;
|
|
mlbeg--;
|
|
}
|
|
}
|
|
if ((space = zterm_lines - pl - mhasstat) > 0)
|
|
while (mline >= mlbeg + space)
|
|
if ((mlbeg += step) + space > mlines)
|
|
mlbeg = mlines - space;
|
|
if (lbeg != mlbeg) {
|
|
Cmatch **p = mtab + (mlbeg * zterm_columns), **q;
|
|
int c;
|
|
|
|
while (mlbeg < mlines) {
|
|
for (q = p, c = zterm_columns; c > 0; q++, c--)
|
|
if (*q)
|
|
break;
|
|
if (c)
|
|
break;
|
|
p += zterm_columns;
|
|
mlbeg++;
|
|
}
|
|
}
|
|
lbeg = mlbeg;
|
|
onlyexpl = 0;
|
|
showinglist = -2;
|
|
if (first && !listshown && isset(LISTBEEP))
|
|
zbeep();
|
|
if (first) {
|
|
/*
|
|
* remember the original data that we will use when
|
|
* performing interactive completion to restore the
|
|
* command line when a menu completion is inserted.
|
|
* this is because menu completion will insert
|
|
* the next match in the loop; for interactive
|
|
* completion we don't want that, we always want to
|
|
* be able to type the next character.
|
|
*/
|
|
modeline = dupstring(zlemetaline);
|
|
modecs = zlemetacs;
|
|
modell = zlemetall;
|
|
modelen = minfo.len;
|
|
}
|
|
first = 0;
|
|
if (mode == MM_INTER)
|
|
statusline = status;
|
|
else if (mode) {
|
|
int l = sprintf(status, "%s%sisearch%s: ",
|
|
((msearchstate & MS_FAILED) ? "failed " : ""),
|
|
((msearchstate & MS_WRAPPED) ? "wrapped " : ""),
|
|
(mode == MM_FSEARCH ? "" : " backward"));
|
|
|
|
strncat(status, msearchstr, MAX_STATUS - l - 1);
|
|
|
|
statusline = status;
|
|
} else {
|
|
statusline = NULL;
|
|
}
|
|
if (noselect < 0) {
|
|
showinglist = clearlist = 0;
|
|
clearflag = 1;
|
|
}
|
|
zrefresh();
|
|
statusline = NULL;
|
|
inselect = 1;
|
|
selected = 1;
|
|
if (noselect) {
|
|
if (noselect < 0) {
|
|
/* no selection until after processing keystroke */
|
|
noselect = 0;
|
|
goto getk;
|
|
}
|
|
broken = 1;
|
|
break;
|
|
}
|
|
if (!i) {
|
|
i = mcols * mlines;
|
|
while (i--)
|
|
if (mtab[i])
|
|
break;
|
|
if (!i)
|
|
break;
|
|
i = 1;
|
|
}
|
|
p = mmtabp;
|
|
pg = mgtabp;
|
|
if (!p) /* selected match not in display, find line */
|
|
continue;
|
|
minfo.cur = *p;
|
|
minfo.group = *pg;
|
|
if (setwish)
|
|
wishcol = mcol;
|
|
else if (mcol > wishcol) {
|
|
while (mcol > 0 && p[-1] == minfo.cur)
|
|
mcol--, p--, pg--;
|
|
} else if (mcol < wishcol) {
|
|
while (mcol < mcols - 1 && p[1] == minfo.cur)
|
|
mcol++, p++, pg++;
|
|
}
|
|
setwish = wasnext = 0;
|
|
|
|
getk:
|
|
|
|
if (!do_last_key) {
|
|
zmult = 1;
|
|
cmd = getkeycmd();
|
|
/*
|
|
* On interrupt, we'll exit due to cmd being empty.
|
|
* Don't propagate the interrupt any further, which
|
|
* can screw up redrawing.
|
|
*/
|
|
errflag &= ~ERRFLAG_INT;
|
|
if (mtab_been_reallocated) {
|
|
do_last_key = 1;
|
|
continue;
|
|
}
|
|
}
|
|
do_last_key = 0;
|
|
|
|
if (!cmd || cmd == Th(z_sendbreak)) {
|
|
zbeep();
|
|
molbeg = -1;
|
|
break;
|
|
} else if (nolist && cmd != Th(z_undo) &&
|
|
(!mode || (cmd != Th(z_backwarddeletechar) &&
|
|
cmd != Th(z_selfinsert) &&
|
|
cmd != Th(z_selfinsertunmeta)))) {
|
|
ungetkeycmd();
|
|
break;
|
|
} else if (cmd == Th(z_acceptline) || cmd == Th(z_acceptsearch)) {
|
|
if (mode == MM_FSEARCH || mode == MM_BSEARCH) {
|
|
mode = 0;
|
|
continue;
|
|
}
|
|
acc = 1;
|
|
break;
|
|
} else if (cmd == Th(z_viinsert)) {
|
|
if (mode == MM_INTER)
|
|
mode = 0;
|
|
else {
|
|
int l = strlen(origline);
|
|
|
|
/*
|
|
* Entering interactive completion mode:
|
|
* same code as when we enter it on menu selection
|
|
* start.
|
|
*/
|
|
mode = MM_INTER;
|
|
zlemetacs = 0;
|
|
foredel(zlemetall, CUT_RAW);
|
|
spaceinline(l);
|
|
strncpy(zlemetaline, origline, l);
|
|
zlemetacs = origcs;
|
|
setmstatus(status, NULL, 0, 0, NULL, NULL, NULL);
|
|
|
|
continue;
|
|
}
|
|
} else if (cmd == Th(z_acceptandinfernexthistory) ||
|
|
(mode == MM_INTER && (cmd == Th(z_selfinsert) ||
|
|
cmd == Th(z_selfinsertunmeta)))) {
|
|
char *saveline = NULL;
|
|
int savell = 0;
|
|
int savecs = 0;
|
|
Menustack s = (Menustack) zhalloc(sizeof(*s));
|
|
|
|
s->prev = u;
|
|
u = s;
|
|
s->line = dupstring(zlemetaline);
|
|
s->cs = zlemetacs;
|
|
s->mline = mline;
|
|
s->mlbeg = mlbeg;
|
|
memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
|
|
s->amatches = amatches;
|
|
#ifdef ZSH_HEAP_DEBUG
|
|
if (memory_validate(amatches->heap_id)) {
|
|
HEAP_ERROR(amatches->heap_id);
|
|
}
|
|
#endif
|
|
s->pmatches = pmatches;
|
|
s->lastmatches = lastmatches;
|
|
s->lastlmatches = lastlmatches;
|
|
s->nolist = nolist;
|
|
s->acc = menuacc;
|
|
s->brbeg = dupbrinfo(brbeg, NULL, 1);
|
|
s->brend = dupbrinfo(brend, NULL, 1);
|
|
s->nbrbeg = nbrbeg;
|
|
s->nbrend = nbrend;
|
|
s->nmatches = nmatches;
|
|
s->origline = dupstring(origline);
|
|
s->origcs = origcs;
|
|
s->origll = origll;
|
|
s->status = dupstring(status);
|
|
/*
|
|
* with just the slightest hint of a note of infuriation:
|
|
* mode here is the menu mode, not the file mode, despite
|
|
* the fact we're in a file dealing with file highlighting;
|
|
* but that's OK, because s is a menu stack entry, despite
|
|
* the fact we're in a function declaring s as char *.
|
|
* anyway, in functions we really mean *mode* it's
|
|
* called m, to be clear.
|
|
*/
|
|
s->mode = mode;
|
|
menucmp = menuacc = hasoldlist = 0;
|
|
minfo.cur = NULL;
|
|
fixsuffix();
|
|
handleundo();
|
|
validlist = 0;
|
|
amatches = pmatches = lastmatches = NULL;
|
|
invalidate_list();
|
|
iforcemenu = 1;
|
|
comprecursive = 1;
|
|
if (cmd != Th(z_acceptandinfernexthistory)) {
|
|
int l = strlen(origline);
|
|
|
|
/*
|
|
* Interactive mode: we need to restore the
|
|
* line, add the character, then remember how
|
|
* this new line looks in order to keep
|
|
* the command line as it is with just the
|
|
* characters typed by the user.
|
|
*/
|
|
zlemetacs = 0;
|
|
foredel(zlemetall, CUT_RAW);
|
|
spaceinline(l);
|
|
strncpy(zlemetaline, origline, l);
|
|
zlemetacs = origcs;
|
|
|
|
/*
|
|
* Horrible quick fix:
|
|
* we shouldn't need to metafy and unmetafy
|
|
* quite as much. If we kept unmetafied through
|
|
* here we could fix up setmstatus to use unmetafied
|
|
* as well. This is the only use of setmstatus which
|
|
* restores the line so that should be doable.
|
|
*/
|
|
unmetafy_line();
|
|
if (cmd == Th(z_selfinsert))
|
|
selfinsert(zlenoargs);
|
|
else
|
|
selfinsertunmeta(zlenoargs);
|
|
metafy_line();
|
|
minfo.len++;
|
|
minfo.end++;
|
|
|
|
saveline = (char *) zhalloc(zlemetall);
|
|
memcpy(saveline, zlemetaline, zlemetall);
|
|
savell = zlemetall;
|
|
savecs = zlemetacs;
|
|
iforcemenu = -1;
|
|
} else
|
|
mode = 0;
|
|
/* Nested completion assumes line is unmetafied */
|
|
unmetafy_line();
|
|
menucomplete(zlenoargs);
|
|
metafy_line();
|
|
iforcemenu = 0;
|
|
|
|
if (cmd != Th(z_acceptandinfernexthistory))
|
|
modeline = setmstatus(status, saveline, savell, savecs,
|
|
&modecs, &modell, &modelen);
|
|
|
|
if (nmatches < 1 || !minfo.cur || !*(minfo.cur)) {
|
|
nolist = 1;
|
|
if (mode == MM_INTER) {
|
|
statusline = status;
|
|
} else {
|
|
/* paranoia */
|
|
statusline = NULL;
|
|
}
|
|
if (nmessages) {
|
|
showinglist = -2;
|
|
zrefresh();
|
|
noselect = -1;
|
|
} else {
|
|
trashzle();
|
|
zsetterm();
|
|
if (tccan(TCCLEAREOD))
|
|
tcout(TCCLEAREOD);
|
|
fputs("no matches\r", shout);
|
|
fflush(shout);
|
|
tcmultout(TCUP, TCMULTUP, nlnct);
|
|
showinglist = clearlist = 0;
|
|
clearflag = 1;
|
|
zrefresh();
|
|
showinglist = clearlist = 0;
|
|
}
|
|
statusline = NULL;
|
|
|
|
goto getk;
|
|
}
|
|
clearlist = listshown = 1;
|
|
mselect = (*(minfo.cur))->gnum;
|
|
setwish = wasnext = 1;
|
|
mline = 0;
|
|
molbeg = -42;
|
|
continue;
|
|
} else if (cmd == Th(z_acceptandhold) ||
|
|
cmd == Th(z_acceptandmenucomplete)) {
|
|
Menustack s = (Menustack) zhalloc(sizeof(*s));
|
|
int ol;
|
|
|
|
if (mode == MM_INTER)
|
|
do_single(*minfo.cur);
|
|
mode = 0;
|
|
s->prev = u;
|
|
u = s;
|
|
s->line = dupstring(zlemetaline);
|
|
s->cs = zlemetacs;
|
|
s->mline = mline;
|
|
s->mlbeg = mlbeg;
|
|
memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
|
|
s->amatches = s->pmatches =
|
|
s->lastmatches = s->lastlmatches = NULL;
|
|
s->nolist = nolist;
|
|
s->acc = menuacc;
|
|
s->brbeg = dupbrinfo(brbeg, NULL, 1);
|
|
s->brend = dupbrinfo(brend, NULL, 1);
|
|
s->nbrbeg = nbrbeg;
|
|
s->nbrend = nbrend;
|
|
s->nmatches = nmatches;
|
|
s->origline = dupstring(origline);
|
|
s->origcs = origcs;
|
|
s->origll = origll;
|
|
s->status = dupstring(status);
|
|
/* see above */
|
|
s->mode = mode;
|
|
accept_last();
|
|
handleundo();
|
|
comprecursive = 1;
|
|
do_menucmp(0);
|
|
mselect = (*(minfo.cur))->gnum;
|
|
|
|
p -= mcol;
|
|
mcol = 0;
|
|
ol = mline;
|
|
do {
|
|
for (mcol = 0; mcol < mcols; mcol++, p++)
|
|
if (*p == minfo.cur)
|
|
break;
|
|
if (mcol != mcols)
|
|
break;
|
|
if (++mline == mlines) {
|
|
mline = 0;
|
|
p -= mlines * mcols;
|
|
}
|
|
} while (mline != ol);
|
|
if (*p != minfo.cur) {
|
|
noselect = clearlist = listshown = 1;
|
|
onlyexpl = 0;
|
|
zrefresh();
|
|
break;
|
|
}
|
|
setwish = 1;
|
|
continue;
|
|
} else if (cmd == Th(z_undo) ||
|
|
(mode == MM_INTER && cmd == Th(z_backwarddeletechar))) {
|
|
int l;
|
|
|
|
if (!u)
|
|
break;
|
|
|
|
handleundo();
|
|
zlemetacs = 0;
|
|
foredel(zlemetall, CUT_RAW);
|
|
spaceinline(l = strlen(u->line));
|
|
strncpy(zlemetaline, u->line, l);
|
|
zlemetacs = u->cs;
|
|
menuacc = u->acc;
|
|
memcpy(&minfo, &(u->info), sizeof(struct menuinfo));
|
|
p = &(minfo.cur);
|
|
mline = u->mline;
|
|
mlbeg = u->mlbeg;
|
|
if (u->lastmatches && lastmatches != u->lastmatches) {
|
|
if (lastmatches)
|
|
freematches(lastmatches, 0);
|
|
amatches = u->amatches;
|
|
#ifdef ZSH_HEAP_DEBUG
|
|
if (memory_validate(amatches->heap_id)) {
|
|
HEAP_ERROR(amatches->heap_id);
|
|
}
|
|
#endif
|
|
pmatches = u->pmatches;
|
|
lastmatches = u->lastmatches;
|
|
lastlmatches = u->lastlmatches;
|
|
nmatches = u->nmatches;
|
|
hasoldlist = validlist = 1;
|
|
}
|
|
freebrinfo(brbeg);
|
|
freebrinfo(brend);
|
|
brbeg = dupbrinfo(u->brbeg, &lastbrbeg, 0);
|
|
brend = dupbrinfo(u->brend, &lastbrend, 0);
|
|
nbrbeg = u->nbrbeg;
|
|
nbrend = u->nbrend;
|
|
zsfree(origline);
|
|
origline = ztrdup(u->origline);
|
|
origcs = u->origcs;
|
|
origll = u->origll;
|
|
strcpy(status, u->status);
|
|
mode = u->mode;
|
|
nolist = u->nolist;
|
|
|
|
u = u->prev;
|
|
clearlist = 1;
|
|
setwish = 1;
|
|
listdat.valid = 0;
|
|
molbeg = -42;
|
|
|
|
if (nolist) {
|
|
if (mode == MM_INTER) {
|
|
statusline = status;
|
|
} else {
|
|
/* paranoia */
|
|
statusline = NULL;
|
|
}
|
|
zrefresh();
|
|
statusline = NULL;
|
|
goto getk;
|
|
}
|
|
if (mode)
|
|
continue;
|
|
} else if (cmd == Th(z_redisplay)) {
|
|
redisplay(zlenoargs);
|
|
molbeg = -42;
|
|
continue;
|
|
} else if (cmd == Th(z_clearscreen)) {
|
|
clearscreen(zlenoargs);
|
|
molbeg = -42;
|
|
continue;
|
|
} else if (cmd == Th(z_downhistory) ||
|
|
cmd == Th(z_downlineorhistory) ||
|
|
cmd == Th(z_downlineorsearch) ||
|
|
cmd == Th(z_vidownlineorhistory)) {
|
|
int omline;
|
|
Cmatch **op;
|
|
|
|
mode = 0;
|
|
wrap = 0;
|
|
|
|
down:
|
|
|
|
omline = mline;
|
|
op = p;
|
|
|
|
do {
|
|
if (mline == mlines - 1) {
|
|
if (wrap & 2) {
|
|
mline = omline;
|
|
p = op;
|
|
break;
|
|
}
|
|
p -= mline * mcols;
|
|
mline = 0;
|
|
wrap |= 1;
|
|
} else {
|
|
mline++;
|
|
p += mcols;
|
|
}
|
|
if (adjust_mcol(wishcol, &p, NULL))
|
|
continue;
|
|
} while (!*p || mmarked(*p));
|
|
|
|
if (wrap == 1)
|
|
goto right;
|
|
} else if (cmd == Th(z_uphistory) ||
|
|
cmd == Th(z_uplineorhistory) ||
|
|
cmd == Th(z_uplineorsearch) ||
|
|
cmd == Th(z_viuplineorhistory)) {
|
|
int omline;
|
|
Cmatch **op;
|
|
|
|
mode = 0;
|
|
wrap = 0;
|
|
|
|
up:
|
|
|
|
omline = mline;
|
|
op = p;
|
|
|
|
do {
|
|
if (!mline) {
|
|
if (wrap & 2) {
|
|
mline = omline;
|
|
p = op;
|
|
break;
|
|
}
|
|
mline = mlines - 1;
|
|
p += mline * mcols;
|
|
wrap |= 1;
|
|
} else {
|
|
mline--;
|
|
p -= mcols;
|
|
}
|
|
if (adjust_mcol(wishcol, &p, NULL))
|
|
continue;
|
|
} while (!*p || mmarked(*p));
|
|
|
|
if (wrap == 1) {
|
|
if (mcol == wishcol)
|
|
goto left;
|
|
|
|
wishcol = mcol;
|
|
}
|
|
} else if (cmd == Th(z_emacsforwardword) ||
|
|
cmd == Th(z_viforwardword) ||
|
|
cmd == Th(z_viforwardwordend) ||
|
|
cmd == Th(z_forwardword)) {
|
|
int i = zterm_lines - pl - 1, oi = i, ll = 0;
|
|
Cmatch **lp = NULL;
|
|
|
|
mode = 0;
|
|
if (mline == mlines - 1)
|
|
goto top;
|
|
while (i > 0) {
|
|
if (mline == mlines - 1) {
|
|
if (i != oi && lp)
|
|
break;
|
|
goto top;
|
|
} else {
|
|
mline++;
|
|
p += mcols;
|
|
}
|
|
if (adjust_mcol(wishcol, &p, NULL))
|
|
continue;
|
|
if (*p && !mmarked(*p)) {
|
|
i--;
|
|
lp = p;
|
|
ll = mline;
|
|
}
|
|
}
|
|
p = lp;
|
|
mline = ll;
|
|
} else if (cmd == Th(z_emacsbackwardword) ||
|
|
cmd == Th(z_vibackwardword) ||
|
|
cmd == Th(z_backwardword)) {
|
|
int i = zterm_lines - pl - 1, oi = i, ll = 0;
|
|
Cmatch **lp = NULL;
|
|
|
|
mode = 0;
|
|
if (!mline)
|
|
goto bottom;
|
|
while (i > 0) {
|
|
if (!mline) {
|
|
if (i != oi && lp)
|
|
break;
|
|
goto bottom;
|
|
} else {
|
|
mline--;
|
|
p -= mcols;
|
|
}
|
|
if (adjust_mcol(wishcol, &p, NULL))
|
|
continue;
|
|
if (*p || !mmarked(*p)) {
|
|
i--;
|
|
lp = p;
|
|
ll = mline;
|
|
}
|
|
}
|
|
p = lp;
|
|
mline = ll;
|
|
} else if (cmd == Th(z_beginningofhistory)) {
|
|
int ll;
|
|
Cmatch **lp;
|
|
|
|
mode = 0;
|
|
|
|
top:
|
|
|
|
ll = mline;
|
|
lp = p;
|
|
while (mline) {
|
|
mline--;
|
|
p -= mcols;
|
|
if (adjust_mcol(wishcol, &p, NULL))
|
|
continue;
|
|
if (*p && !mmarked(*p)) {
|
|
lp = p;
|
|
ll = mline;
|
|
}
|
|
}
|
|
mline = ll;
|
|
p = lp;
|
|
} else if (cmd == Th(z_endofhistory)) {
|
|
int ll;
|
|
Cmatch **lp;
|
|
|
|
mode = 0;
|
|
|
|
bottom:
|
|
|
|
ll = mline;
|
|
lp = p;
|
|
while (mline < mlines - 1) {
|
|
mline++;
|
|
p += mcols;
|
|
if (adjust_mcol(wishcol, &p, NULL))
|
|
continue;
|
|
if (*p && !mmarked(*p)) {
|
|
lp = p;
|
|
ll = mline;
|
|
}
|
|
}
|
|
mline = ll;
|
|
p = lp;
|
|
} else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
|
|
int omcol;
|
|
Cmatch **op;
|
|
|
|
mode = 0;
|
|
wrap = 0;
|
|
|
|
right:
|
|
|
|
omcol = mcol;
|
|
op = p;
|
|
|
|
do {
|
|
if (mcol == mcols - 1) {
|
|
if (wrap & 1) {
|
|
p = op;
|
|
mcol = omcol;
|
|
break;
|
|
}
|
|
p -= mcol;
|
|
mcol = 0;
|
|
wrap |= 2;
|
|
} else {
|
|
mcol++;
|
|
p++;
|
|
}
|
|
} while (!*p || mmarked(*p) || (mcol != omcol && *p == *op));
|
|
wishcol = mcol;
|
|
|
|
if (wrap == 2)
|
|
goto down;
|
|
} else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) {
|
|
int omcol;
|
|
Cmatch **op;
|
|
|
|
mode = 0;
|
|
wrap = 0;
|
|
|
|
left:
|
|
|
|
omcol = mcol;
|
|
op = p;
|
|
|
|
do {
|
|
if (!mcol) {
|
|
if (wrap & 1) {
|
|
p = op;
|
|
mcol = omcol;
|
|
break;
|
|
}
|
|
mcol = mcols - 1;
|
|
p += mcol;
|
|
wrap |= 2;
|
|
} else {
|
|
mcol--;
|
|
p--;
|
|
}
|
|
} while (!*p || mmarked(*p) || (mcol != omcol && *p == *op));
|
|
wishcol = mcol;
|
|
|
|
if (wrap == 2) {
|
|
p += mcols - 1 - mcol;
|
|
wishcol = mcol = mcols - 1;
|
|
adjust_mcol(wishcol, &p, NULL);
|
|
goto up;
|
|
}
|
|
} else if (cmd == Th(z_beginningofbufferorhistory) ||
|
|
cmd == Th(z_beginningofline) ||
|
|
cmd == Th(z_beginningoflinehist) ||
|
|
cmd == Th(z_vibeginningofline)) {
|
|
mode = 0;
|
|
p -= mcol;
|
|
mcol = 0;
|
|
while (!*p || mmarked(*p)) {
|
|
mcol++;
|
|
p++;
|
|
}
|
|
wishcol = 0;
|
|
} else if (cmd == Th(z_endofbufferorhistory) ||
|
|
cmd == Th(z_endofline) ||
|
|
cmd == Th(z_endoflinehist) ||
|
|
cmd == Th(z_viendofline)) {
|
|
mode = 0;
|
|
p += mcols - mcol - 1;
|
|
mcol = mcols - 1;
|
|
while (!*p || mmarked(*p)) {
|
|
mcol--;
|
|
p--;
|
|
}
|
|
wishcol = mcols - 1;
|
|
} else if (cmd == Th(z_viforwardblankword) ||
|
|
cmd == Th(z_viforwardblankwordend)) {
|
|
Cmgroup g = *pg;
|
|
int ol = mline;
|
|
|
|
mode = 0;
|
|
do {
|
|
if (mline == mlines - 1) {
|
|
p -= mline * mcols;
|
|
pg -= mline * mcols;
|
|
mline = 0;
|
|
} else {
|
|
mline++;
|
|
p += mcols;
|
|
pg += mcols;
|
|
}
|
|
if (adjust_mcol(wishcol, &p, &pg))
|
|
continue;
|
|
} while (ol != mline && (*pg == g || !*pg || mmarked(*pg)));
|
|
} else if (cmd == Th(z_vibackwardblankword)) {
|
|
Cmgroup g = *pg;
|
|
int ol = mline;
|
|
|
|
mode = 0;
|
|
do {
|
|
if (!mline) {
|
|
mline = mlines - 1;
|
|
p += mline * mcols;
|
|
pg += mline * mcols;
|
|
} else {
|
|
mline--;
|
|
p -= mcols;
|
|
pg -= mcols;
|
|
}
|
|
if (adjust_mcol(wishcol, &p, &pg))
|
|
continue;
|
|
} while (ol != mline && (*pg == g || !*pg || mmarked(*pg)));
|
|
} else if (cmd == Th(z_completeword) ||
|
|
cmd == Th(z_expandorcomplete) ||
|
|
cmd == Th(z_expandorcompleteprefix) ||
|
|
cmd == Th(z_menucomplete) ||
|
|
cmd == Th(z_menuexpandorcomplete) ||
|
|
!strcmp(cmd->nam, "menu-select") ||
|
|
!strcmp(cmd->nam, "complete-word") ||
|
|
!strcmp(cmd->nam, "expand-or-complete") ||
|
|
!strcmp(cmd->nam, "expand-or-complete-prefix") ||
|
|
!strcmp(cmd->nam, "menu-complete") ||
|
|
!strcmp(cmd->nam, "menu-expand-or-complete")) {
|
|
if (mode == MM_INTER) {
|
|
/*
|
|
* do_menucmp() has inserted the completion onto
|
|
* the command line. In interactive mode we
|
|
* don't want that, just what the user typed,
|
|
* so restore the information.
|
|
*/
|
|
zsfree(origline);
|
|
origline = ztrdup(modeline);
|
|
origcs = modecs;
|
|
origll = modell;
|
|
zlemetacs = 0;
|
|
foredel(zlemetall, CUT_RAW);
|
|
spaceinline(origll);
|
|
strncpy(zlemetaline, origline, origll);
|
|
zlemetacs = origcs;
|
|
minfo.len = modelen;
|
|
} else {
|
|
mode = 0;
|
|
comprecursive = 1;
|
|
do_menucmp(0);
|
|
mselect = (*(minfo.cur))->gnum;
|
|
setwish = 1;
|
|
mline = -1;
|
|
}
|
|
continue;
|
|
} else if (cmd == Th(z_reversemenucomplete) ||
|
|
!strcmp(cmd->nam, "reverse-menu-complete")) {
|
|
mode = 0;
|
|
comprecursive = 1;
|
|
unmetafy_line();
|
|
reversemenucomplete(zlenoargs);
|
|
metafy_line();
|
|
mselect = (*(minfo.cur))->gnum;
|
|
setwish = 1;
|
|
mline = -1;
|
|
continue;
|
|
} else if (cmd == Th(z_historyincrementalsearchforward) ||
|
|
cmd == Th(z_historyincrementalsearchbackward) ||
|
|
((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
|
|
(cmd == Th(z_selfinsert) ||
|
|
cmd == Th(z_selfinsertunmeta) ||
|
|
cmd == Th(z_bracketedpaste)))) {
|
|
Cmatch **np, **op = p;
|
|
int was = (mode == MM_FSEARCH || mode == MM_BSEARCH);
|
|
int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta) ||
|
|
cmd == Th(z_bracketedpaste));
|
|
int back = (cmd == Th(z_historyincrementalsearchbackward));
|
|
int wrap;
|
|
|
|
do {
|
|
char *toins = NULL;
|
|
#ifdef MULTIBYTE_SUPPORT
|
|
/* MB_CUR_MAX may not be constant */
|
|
VARARR(char, insert, MB_CUR_MAX+1);
|
|
#else
|
|
char insert[2];
|
|
#endif
|
|
if (was) {
|
|
p += wishcol - mcol;
|
|
mcol = wishcol;
|
|
}
|
|
if (!ins) {
|
|
if (was) {
|
|
if (!*msearchstr && lastsearch &&
|
|
back == (mode == MM_BSEARCH)) {
|
|
msearchstr = dupstring(lastsearch);
|
|
mode = 0;
|
|
}
|
|
} else {
|
|
msearchstr = "";
|
|
msearchstack = NULL;
|
|
msearchstate = MS_OK;
|
|
}
|
|
} else {
|
|
if (cmd == Th(z_selfinsertunmeta)) {
|
|
fixunmeta();
|
|
}
|
|
if (cmd == Th(z_bracketedpaste)) {
|
|
toins = bracketedstring();
|
|
} else {
|
|
toins = insert;
|
|
#ifdef MULTIBYTE_SUPPORT
|
|
if (lastchar_wide_valid)
|
|
{
|
|
mbstate_t mbs;
|
|
int len;
|
|
|
|
memset(&mbs, 0, sizeof(mbs));
|
|
len = wcrtomb(toins, lastchar_wide, &mbs);
|
|
if (len < 0)
|
|
len = 0;
|
|
insert[len] = '\0';
|
|
} else
|
|
#endif
|
|
{
|
|
insert[0] = lastchar;
|
|
insert[1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
wrap = 0;
|
|
np = msearch(p, toins, (ins ? (mode == MM_BSEARCH) : back),
|
|
(was && !ins), &wrap);
|
|
|
|
if (!ins)
|
|
mode = (back ? MM_BSEARCH : MM_FSEARCH);
|
|
else if (cmd == Th(z_bracketedpaste))
|
|
free(toins);
|
|
|
|
if (*msearchstr) {
|
|
zsfree(lastsearch);
|
|
lastsearch = ztrdup(msearchstr);
|
|
}
|
|
if (np) {
|
|
wishcol = mcol;
|
|
p = np;
|
|
}
|
|
adjust_mcol(wishcol, &p, NULL);
|
|
|
|
} while ((back || cmd == Th(z_historyincrementalsearchforward)) &&
|
|
np && !wrap && was && **p == **op);
|
|
|
|
} else if ((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
|
|
cmd == Th(z_backwarddeletechar)) {
|
|
int back = 1;
|
|
Cmatch **np = msearchpop(&back);
|
|
|
|
mode = (back ? MM_BSEARCH : MM_FSEARCH);
|
|
wishcol = mcol;
|
|
if (np) {
|
|
p = np;
|
|
adjust_mcol(wishcol, &p, NULL);
|
|
}
|
|
} else if (cmd == Th(z_undefinedkey)) {
|
|
mode = 0;
|
|
continue;
|
|
} else {
|
|
ungetkeycmd();
|
|
if (cmd->widget && (cmd->widget->flags & WIDGET_NCOMP)) {
|
|
acc = 0;
|
|
broken = 2;
|
|
} else
|
|
acc = 1;
|
|
break;
|
|
}
|
|
do_single(**p);
|
|
mselect = (**p)->gnum;
|
|
}
|
|
if (u)
|
|
for (; u; u = u->prev)
|
|
if (u->lastmatches != lastmatches)
|
|
freematches(u->lastmatches, 0);
|
|
|
|
selectlocalmap(NULL);
|
|
mselect = mlastcols = mlastlines = -1;
|
|
mstatus = NULL;
|
|
inselect = mhasstat = 0;
|
|
if (nolist)
|
|
clearlist = listshown = 1;
|
|
if (acc && validlist && minfo.cur) {
|
|
menucmp = lastambig = hasoldlist = 0;
|
|
do_single(*(minfo.cur));
|
|
}
|
|
if (wasnext || broken) {
|
|
menucmp = 1;
|
|
showinglist = ((validlist && !nolist) ? -2 : 0);
|
|
minfo.asked = 0;
|
|
if (!noselect) {
|
|
int nos = noselect;
|
|
|
|
zrefresh();
|
|
noselect = nos;
|
|
}
|
|
}
|
|
if (!noselect && (!dat || acc)) {
|
|
/*
|
|
* I added the following because in certain cases the zrefresh()
|
|
* here was screwing up the list. Forcing it to redraw the
|
|
* screen worked. The case in question (courtesy of
|
|
* "Matt Wozniski" <godlygeek@gmail.com>) is in zsh-workers/24756.
|
|
*
|
|
* *** PLEASE DON'T ASK ME WHY THIS IS NECESSARY ***
|
|
*/
|
|
mlbeg = -1;
|
|
showinglist = ((validlist && !nolist) ? -2 : 0);
|
|
onlyexpl = oe;
|
|
if (acc && listshown) {
|
|
/*
|
|
* Clear the list without spending sixteen weeks of
|
|
* redrawing it in slightly different states first.
|
|
* The following seems to work. I'm not sure what
|
|
* the difference is between listshown and showinglist,
|
|
* but listshown looks like the traditional thing to
|
|
* check for in this file at least.
|
|
*
|
|
* showinglist has a normally undocumented value of 1,
|
|
* and an extra-specially undocumented value of -2, which
|
|
* seems to be a force---it appears we need to kick it out
|
|
* of that state, though it worries me that in some places
|
|
* the code actually forces it back into that state.
|
|
*/
|
|
clearlist = listshown = showinglist = 1;
|
|
} else if (!smatches)
|
|
clearlist = listshown = 1;
|
|
zrefresh();
|
|
}
|
|
mlbeg = -1;
|
|
fdat = NULL;
|
|
|
|
if (!wasmeta)
|
|
unmetafy_line();
|
|
|
|
return (broken == 2 ? 3 :
|
|
((dat && !broken) ? (acc ? 1 : 2) : (!noselect ^ acc)));
|
|
}
|
|
|
|
/* The widget function. */
|
|
|
|
static int
|
|
menuselect(char **args)
|
|
{
|
|
int d = 0;
|
|
|
|
if (!minfo.cur) {
|
|
selected = 0;
|
|
menucomplete(args);
|
|
if ((minfo.cur && minfo.asked == 2) || selected)
|
|
return 0;
|
|
d = 1;
|
|
}
|
|
if (minfo.cur && (minfo.asked == 2 || domenuselect(NULL, NULL)) && !d)
|
|
menucomplete(args);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct features module_features = {
|
|
NULL, 0,
|
|
NULL, 0,
|
|
NULL, 0,
|
|
NULL, 0,
|
|
0
|
|
};
|
|
|
|
/**/
|
|
int
|
|
setup_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
features_(Module m, char ***features)
|
|
{
|
|
*features = featuresarray(m, &module_features);
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
enables_(Module m, int **enables)
|
|
{
|
|
return handlefeatures(m, &module_features, enables);
|
|
}
|
|
|
|
/**/
|
|
static void
|
|
menuselect_bindings(void)
|
|
{
|
|
if (!(mskeymap = openkeymap("menuselect"))) {
|
|
mskeymap = newkeymap(NULL, "menuselect");
|
|
linkkeymap(mskeymap, "menuselect", 1);
|
|
bindkey(mskeymap, "\t", refthingy(t_completeword), NULL);
|
|
bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL);
|
|
bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL);
|
|
bindkey(mskeymap, "\33[A", refthingy(t_uplineorhistory), NULL);
|
|
bindkey(mskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
|
|
bindkey(mskeymap, "\33[C", refthingy(t_forwardchar), NULL);
|
|
bindkey(mskeymap, "\33[D", refthingy(t_backwardchar), NULL);
|
|
bindkey(mskeymap, "\33OA", refthingy(t_uplineorhistory), NULL);
|
|
bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
|
|
bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL);
|
|
bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL);
|
|
}
|
|
if (!(lskeymap = openkeymap("listscroll"))) {
|
|
lskeymap = newkeymap(NULL, "listscroll");
|
|
linkkeymap(lskeymap, "listscroll", 1);
|
|
bindkey(lskeymap, "\t", refthingy(t_completeword), NULL);
|
|
bindkey(lskeymap, " ", refthingy(t_completeword), NULL);
|
|
bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL);
|
|
bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL);
|
|
bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL);
|
|
bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL);
|
|
}
|
|
}
|
|
|
|
/**/
|
|
int
|
|
boot_(Module m)
|
|
{
|
|
mtab = NULL;
|
|
mgtab = NULL;
|
|
mselect = -1;
|
|
inselect = 0;
|
|
|
|
w_menuselect = addzlefunction("menu-select", menuselect,
|
|
ZLE_MENUCMP|ZLE_KEEPSUFFIX|ZLE_ISCOMP);
|
|
if (!w_menuselect) {
|
|
zwarnnam(m->node.nam,
|
|
"name clash when adding ZLE function `menu-select'");
|
|
return -1;
|
|
}
|
|
addhookfunc("comp_list_matches", (Hookfn) complistmatches);
|
|
addhookfunc("menu_start", (Hookfn) domenuselect);
|
|
menuselect_bindings();
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
cleanup_(Module m)
|
|
{
|
|
free(mtab);
|
|
free(mgtab);
|
|
|
|
deletezlefunction(w_menuselect);
|
|
deletehookfunc("comp_list_matches", (Hookfn) complistmatches);
|
|
deletehookfunc("menu_start", (Hookfn) domenuselect);
|
|
unlinkkeymap("menuselect", 1);
|
|
unlinkkeymap("listscroll", 1);
|
|
return setfeatureenables(m, &module_features, NULL);
|
|
}
|
|
|
|
/**/
|
|
int
|
|
finish_(UNUSED(Module m))
|
|
{
|
|
return 0;
|
|
}
|