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