1/* 2 * zle_hist.c - history editing 3 * 4 * This file is part of zsh, the Z shell. 5 * 6 * Copyright (c) 1992-1997 Paul Falstad 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of 19 * the possibility of such damage. 20 * 21 * Paul Falstad 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 Paul Falstad and the 25 * Zsh Development Group have no obligation to provide maintenance, 26 * support, updates, enhancements, or modifications. 27 * 28 */ 29 30#include "zle.mdh" 31#include "zle_hist.pro" 32 33/* Column position of vi ideal cursor. -1 if it is unknown -- most * 34 * movements and changes do this. */ 35 36/**/ 37int lastcol; 38 39/* current history line number */ 40 41/**/ 42int histline; 43 44/* Previous search string use in an incremental search */ 45 46/**/ 47char *previous_search = NULL; 48 49/**/ 50int previous_search_len; 51 52/* Previous aborted search string use in an incremental search */ 53 54/**/ 55char *previous_aborted_search = NULL; 56 57/* Local keymap in isearch mode */ 58 59/**/ 60Keymap isearch_keymap; 61 62/*** History text manipulation utilities ***/ 63 64/* 65 * Text for the line: anything previously modified within zle since 66 * the last time the line editor was started, else what was originally 67 * put in the history. 68 */ 69#define GETZLETEXT(ent) ((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam) 70 71/**/ 72void 73remember_edits(void) 74{ 75 Histent ent = quietgethist(histline); 76 if (ent) { 77 char *line = 78 zlemetaline ? zlemetaline : 79 zlelineasstring(zleline, zlell, 0, NULL, NULL, 0); 80 if (!ent->zle_text || strcmp(line, ent->zle_text) != 0) { 81 if (ent->zle_text) 82 free(ent->zle_text); 83 ent->zle_text = zlemetaline ? ztrdup(line) : line; 84 } else if (!zlemetaline) 85 free(line); 86 } 87} 88 89/**/ 90void 91forget_edits(void) 92{ 93 Histent he; 94 95 for (he = hist_ring; he; he = up_histent(he)) { 96 if (he->zle_text) { 97 free(he->zle_text); 98 he->zle_text = NULL; 99 } 100 } 101} 102 103 104/*** Search utilities ***/ 105 106 107/* 108 * Return zero if the ZLE string histp length histl and the ZLE string 109 * inputp length inputl are the same. Return -1 if inputp is a prefix 110 * of histp. Return 1 if inputp is the lowercase version of histp. 111 * Return 2 if inputp is the lowercase prefix of histp and return 3 112 * otherwise. 113 */ 114 115static int 116zlinecmp(const char *histp, const char *inputp) 117{ 118 const char *hptr = histp, *iptr = inputp; 119#ifdef MULTIBYTE_SUPPORT 120 mbstate_t hstate, istate; 121#endif 122 123 while (*iptr && *hptr == *iptr) { 124 hptr++; 125 iptr++; 126 } 127 128 if (!*iptr) { 129 if (!*hptr) { 130 /* strings are the same */ 131 return 0; 132 } else { 133 /* inputp is a prefix */ 134 return -1; 135 } 136 } 137 138#ifdef MULTIBYTE_SUPPORT 139 memset(&hstate, 0, sizeof(hstate)); 140 memset(&istate, 0, sizeof(istate)); 141#endif 142 143 /* look for lower case versions */ 144 while (*histp && *inputp) { 145#ifdef MULTIBYTE_SUPPORT 146 wint_t hwc, iwc; 147 int hlen, ilen; 148 149 hlen = mb_metacharlenconv_r(histp, &hwc, &hstate); 150 ilen = mb_metacharlenconv_r(inputp, &iwc, &istate); 151 152 if (hwc == WEOF || iwc == WEOF) { 153 /* can't convert, compare input characters */ 154 if (ilen != hlen || memcmp(histp, inputp, hlen) != 0) 155 return 3; 156 } else if (towlower(hwc) != iwc) 157 return 3; 158 159 histp += hlen; 160 inputp += ilen; 161#else 162 if (tulower(*histp++) != *inputp++) 163 return 3; 164#endif 165 } 166 if (!*inputp) { 167 /* one string finished, if it's the input... */ 168 if (!*histp) 169 return 1; /* ...same, else */ 170 else 171 return 2; /* ...prefix */ 172 } 173 /* Different */ 174 return 3; 175} 176 177 178/* 179 * Search for needle in haystack. Haystack and needle are metafied strings. 180 * Start the search at position pos in haystack. 181 * Search forward if dir > 0, otherwise search backward. 182 * sens is used to test against the return value of linecmp. 183 * 184 * Return the pointer to the location in haystack found, else NULL. 185 * 186 * We assume we'll only find needle at some sensible position in a multibyte 187 * string, so we don't bother calculating multibyte character lengths for 188 * incrementing and decrementing the search position. 189 */ 190 191static char * 192zlinefind(char *haystack, int pos, char *needle, int dir, int sens) 193{ 194 char *s = haystack + pos; 195 196 if (dir > 0) { 197 while (*s) { 198 if (zlinecmp(s, needle) < sens) 199 return s; 200 s++; 201 } 202 } else { 203 for (;;) { 204 if (zlinecmp(s, needle) < sens) 205 return s; 206 if (s == haystack) 207 break; 208 s--; 209 } 210 } 211 212 return NULL; 213} 214 215 216/*** Widgets ***/ 217 218 219/**/ 220int 221uphistory(UNUSED(char **args)) 222{ 223 int nodups = isset(HISTIGNOREDUPS); 224 if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP)) 225 return 1; 226 return 0; 227} 228 229/**/ 230static int 231upline(void) 232{ 233 int n = zmult; 234 235 if (n < 0) { 236 zmult = -zmult; 237 n = -downline(); 238 zmult = -zmult; 239 return n; 240 } 241 if (lastcol == -1) 242 lastcol = zlecs - findbol(); 243 zlecs = findbol(); 244 while (n) { 245 if (!zlecs) 246 break; 247 zlecs--; 248 zlecs = findbol(); 249 n--; 250 } 251 if (!n) { 252 int x = findeol(); 253 254 if ((zlecs += lastcol) >= x) { 255 zlecs = x; 256 if (zlecs > findbol() && invicmdmode()) 257 DECCS(); 258 } 259#ifdef MULTIBYTE_SUPPORT 260 else 261 CCRIGHT(); 262#endif 263 264 } 265 return n; 266} 267 268/**/ 269int 270uplineorhistory(char **args) 271{ 272 int ocs = zlecs; 273 int n = upline(); 274 if (n) { 275 int m = zmult, ret; 276 277 zlecs = ocs; 278 if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) 279 return 1; 280 zmult = n; 281 ret = uphistory(args); 282 zmult = m; 283 return ret; 284 } 285 return 0; 286} 287 288/**/ 289int 290viuplineorhistory(char **args) 291{ 292 int col = lastcol; 293 uplineorhistory(args); 294 lastcol = col; 295 return vifirstnonblank(args); 296} 297 298/**/ 299int 300uplineorsearch(char **args) 301{ 302 int ocs = zlecs; 303 int n = upline(); 304 if (n) { 305 int m = zmult, ret; 306 307 zlecs = ocs; 308 if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) 309 return 1; 310 zmult = n; 311 ret = historysearchbackward(args); 312 zmult = m; 313 return ret; 314 } 315 return 0; 316} 317 318/**/ 319static int 320downline(void) 321{ 322 int n = zmult; 323 324 if (n < 0) { 325 zmult = -zmult; 326 n = -upline(); 327 zmult = -zmult; 328 return n; 329 } 330 if (lastcol == -1) 331 lastcol = zlecs - findbol(); 332 while (n) { 333 int x = findeol(); 334 335 if (x == zlell) 336 break; 337 zlecs = x + 1; 338 n--; 339 } 340 if (!n) { 341 int x = findeol(); 342 343 if ((zlecs += lastcol) >= x) { 344 zlecs = x; 345 if (zlecs > findbol() && invicmdmode()) 346 DECCS(); 347 } 348#ifdef MULTIBYTE_SUPPORT 349 else 350 CCRIGHT(); 351#endif 352 } 353 return n; 354} 355 356/**/ 357int 358downlineorhistory(char **args) 359{ 360 int ocs = zlecs; 361 int n = downline(); 362 if (n) { 363 int m = zmult, ret; 364 365 zlecs = ocs; 366 if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) 367 return 1; 368 zmult = n; 369 ret = downhistory(args); 370 zmult = m; 371 return ret; 372 } 373 return 0; 374} 375 376/**/ 377int 378vidownlineorhistory(char **args) 379{ 380 int col = lastcol; 381 downlineorhistory(args); 382 lastcol = col; 383 return vifirstnonblank(zlenoargs); 384} 385 386/**/ 387int 388downlineorsearch(char **args) 389{ 390 int ocs = zlecs; 391 int n = downline(); 392 if (n) { 393 int m = zmult, ret; 394 395 zlecs = ocs; 396 if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) 397 return 1; 398 zmult = n; 399 ret = historysearchforward(args); 400 zmult = m; 401 return ret; 402 } 403 return 0; 404} 405 406/**/ 407int 408acceptlineanddownhistory(UNUSED(char **args)) 409{ 410 Histent he = quietgethist(histline); 411 412 if (he && (he = movehistent(he, 1, HIST_FOREIGN))) { 413 zpushnode(bufstack, ztrdup(he->node.nam)); 414 stackhist = he->histnum; 415 } 416 done = 1; 417 return 0; 418} 419 420/**/ 421int 422downhistory(UNUSED(char **args)) 423{ 424 int nodups = isset(HISTIGNOREDUPS); 425 if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP)) 426 return 1; 427 return 0; 428} 429 430/* 431 * Values remembered for history searches to enable repetition. 432 * srch_hl remembers the old value of histline, to see if it's changed 433 * since the last search. 434 * srch_cs remembers the old value of zlecs for the same purpose (it is 435 * not use for any other purpose, i.e. does not need to be a valid 436 * index into anything). 437 * srch_str is the metafied search string, as extracted from the start 438 * of zleline. 439 */ 440static int histpos, srch_hl, srch_cs = -1; 441static char *srch_str; 442 443/**/ 444int 445historysearchbackward(char **args) 446{ 447 Histent he; 448 int n = zmult; 449 char *str; 450 char *zt; 451 452 if (zmult < 0) { 453 int ret; 454 zmult = -n; 455 ret = historysearchforward(args); 456 zmult = n; 457 return ret; 458 } 459 if (*args) { 460 str = *args; 461 } else { 462 char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0); 463 if (histline == curhist || histline != srch_hl || zlecs != srch_cs || 464 mark != 0 || strncmp(srch_str, line, histpos) != 0) { 465 free(srch_str); 466 for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]); 467 histpos++) 468 ; 469 if (histpos < zlell) 470 histpos++; 471 /* ensure we're not on a combining character */ 472 CCRIGHTPOS(histpos); 473 /* histpos from now on is an index into the metafied string */ 474 srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0); 475 } 476 free(line); 477 str = srch_str; 478 } 479 if (!(he = quietgethist(histline))) 480 return 1; 481 482 metafy_line(); 483 while ((he = movehistent(he, -1, hist_skip_flags))) { 484 if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP) 485 continue; 486 zt = GETZLETEXT(he); 487 if (zlinecmp(zt, str) < 0 && 488 (*args || strcmp(zt, zlemetaline) != 0)) { 489 if (--n <= 0) { 490 unmetafy_line(); 491 zle_setline(he); 492 srch_hl = histline; 493 srch_cs = zlecs; 494 return 0; 495 } 496 } 497 } 498 unmetafy_line(); 499 return 1; 500} 501 502/**/ 503int 504historysearchforward(char **args) 505{ 506 Histent he; 507 int n = zmult; 508 char *str; 509 char *zt; 510 511 if (zmult < 0) { 512 int ret; 513 zmult = -n; 514 ret = historysearchbackward(args); 515 zmult = n; 516 return ret; 517 } 518 if (*args) { 519 str = *args; 520 } else { 521 char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 0); 522 if (histline == curhist || histline != srch_hl || zlecs != srch_cs || 523 mark != 0 || strncmp(srch_str, line, histpos) != 0) { 524 free(srch_str); 525 for (histpos = 0; histpos < zlell && !ZC_iblank(zleline[histpos]); 526 histpos++) 527 ; 528 if (histpos < zlell) 529 histpos++; 530 CCRIGHT(); 531 srch_str = zlelineasstring(zleline, histpos, 0, NULL, NULL, 0); 532 } 533 free(line); 534 str = srch_str; 535 } 536 if (!(he = quietgethist(histline))) 537 return 1; 538 539 metafy_line(); 540 while ((he = movehistent(he, 1, hist_skip_flags))) { 541 if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP) 542 continue; 543 zt = GETZLETEXT(he); 544 if (zlinecmp(zt, str) < (he->histnum == curhist) && 545 (*args || strcmp(zt, zlemetaline) != 0)) { 546 if (--n <= 0) { 547 unmetafy_line(); 548 zle_setline(he); 549 srch_hl = histline; 550 srch_cs = zlecs; 551 return 0; 552 } 553 } 554 } 555 unmetafy_line(); 556 return 1; 557} 558 559/**/ 560int 561beginningofbufferorhistory(char **args) 562{ 563 if (findbol()) 564 zlecs = 0; 565 else 566 return beginningofhistory(args); 567 return 0; 568} 569 570/**/ 571int 572beginningofhistory(UNUSED(char **args)) 573{ 574 if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP)) 575 return 1; 576 return 0; 577} 578 579/**/ 580int 581endofbufferorhistory(char **args) 582{ 583 if (findeol() != zlell) 584 zlecs = zlell; 585 else 586 return endofhistory(args); 587 return 0; 588} 589 590/**/ 591int 592endofhistory(UNUSED(char **args)) 593{ 594 zle_goto_hist(curhist, 0, 0); 595 return 0; 596} 597 598/**/ 599int 600insertlastword(char **args) 601{ 602 int n, nwords, histstep = -1, wordpos = 0, deleteword = 0, len; 603 char *s, *t; 604 Histent he = NULL; 605 LinkList l = NULL; 606 LinkNode node; 607 ZLE_STRING_T zs; 608 609 static char *lastinsert; 610 static int lasthist, lastpos, lastlen; 611 int evhist; 612 613 /* 614 * If we have at least one argument, the first is the history 615 * step. The default is -1 (go back). Repeated calls take 616 * a step in this direction. A value of 0 is allowed and doesn't 617 * move the line. 618 * 619 * If we have two arguments, the second is the position of 620 * the word to extract, 1..N. The default is to use the 621 * numeric argument, or the last word if that is not set. 622 * 623 * If we have three arguments, we reset the history pointer to 624 * the current history event before applying the history step. 625 */ 626 if (*args) 627 { 628 histstep = (int)zstrtol(*args, NULL, 10); 629 if (*++args) 630 { 631 wordpos = (int)zstrtol(*args, NULL, 10); 632 if (*++args) 633 lasthist = curhist; 634 } 635 } 636 637 fixsuffix(); 638 metafy_line(); 639 if (lastinsert && lastlen && 640 lastpos <= zlemetacs && 641 lastlen == zlemetacs - lastpos && 642 memcmp(lastinsert, &zlemetaline[lastpos], lastlen) == 0) 643 deleteword = 1; 644 else 645 lasthist = curhist; 646 evhist = histstep ? addhistnum(lasthist, histstep, HIST_FOREIGN) : 647 lasthist; 648 649 if (evhist == curhist) { 650 /* 651 * The line we are currently editing. If we are going to 652 * replace an existing word, delete the old one now to avoid 653 * confusion. 654 */ 655 if (deleteword) { 656 int pos = zlemetacs; 657 zlemetacs = lastpos; 658 foredel(pos - zlemetacs, CUT_RAW); 659 /* 660 * Mark that this has been deleted. 661 * For consistency with history lines, we really ought to 662 * insert it back if the current command later fails. But 663 * - we can't be bothered 664 * - the problem that this can screw up going to other 665 * lines in the history because we don't update 666 * the history line isn't really relevant 667 * - you can see what you're copying, dammit, so you 668 * shouldn't make errors. 669 * Of course, I could have implemented it in the time 670 * it took to say why I haven't. 671 */ 672 deleteword = 0; 673 } 674 /* 675 * Can only happen fail if the line is empty, I hope. 676 * In that case, we don't need to worry about restoring 677 * a deleted word, because that can only have come 678 * from a non-empty line. I think. 679 */ 680 if (!(l = bufferwords(NULL, NULL, NULL, 0))) { 681 unmetafy_line(); 682 return 1; 683 } 684 nwords = countlinknodes(l); 685 } else { 686 /* Some stored line. */ 687 if (!(he = quietgethist(evhist)) || !he->nwords) { 688 unmetafy_line(); 689 return 1; 690 } 691 nwords = he->nwords; 692 } 693 if (wordpos) { 694 n = (wordpos > 0) ? wordpos : nwords + wordpos + 1; 695 } else if (zmult > 0) { 696 n = nwords - (zmult - 1); 697 } else { 698 n = 1 - zmult; 699 } 700 if (n < 1 || n > nwords) { 701 /* 702 * We can't put in the requested word, but we did find the 703 * history entry, so we remember the position in the history 704 * list. This avoids getting stuck on a history line with 705 * fewer words than expected. The cursor location cs 706 * has not changed, and lastinsert is still valid. 707 */ 708 lasthist = evhist; 709 unmetafy_line(); 710 return 1; 711 } 712 /* 713 * Only remove the old word from the command line if we have 714 * successfully found a new one to insert. 715 */ 716 if (deleteword > 0) { 717 int pos = zlemetacs; 718 zlemetacs = lastpos; 719 foredel(pos - zlemetacs, CUT_RAW); 720 } 721 if (lastinsert) { 722 zfree(lastinsert, lastlen); 723 lastinsert = NULL; 724 } 725 if (l) { 726 for (node = firstnode(l); --n; incnode(node)) 727 ; 728 s = (char *)getdata(node); 729 t = s + strlen(s); 730 } else { 731 s = he->node.nam + he->words[2*n-2]; 732 t = he->node.nam + he->words[2*n-1]; 733 } 734 735 lasthist = evhist; 736 lastpos = zlemetacs; 737 /* ignore trailing whitespace */ 738 lastlen = t - s; 739 lastinsert = zalloc(t - s); 740 memcpy(lastinsert, s, lastlen); 741 n = zmult; 742 zmult = 1; 743 744 unmetafy_line(); 745 746 zs = stringaszleline(dupstrpfx(s, t - s), 0, &len, NULL, NULL); 747 doinsert(zs, len); 748 free(zs); 749 zmult = n; 750 return 0; 751} 752 753/**/ 754void 755zle_setline(Histent he) 756{ 757 int remetafy; 758 if (zlemetaline) { 759 unmetafy_line(); 760 remetafy = 1; 761 } else 762 remetafy = 0; 763 remember_edits(); 764 mkundoent(); 765 histline = he->histnum; 766 767 setline(GETZLETEXT(he), ZSL_COPY|ZSL_TOEND); 768 zlecallhook("zle-history-line-set", NULL); 769 setlastline(); 770 clearlist = 1; 771 if (remetafy) 772 metafy_line(); 773} 774 775/**/ 776int 777setlocalhistory(UNUSED(char **args)) 778{ 779 if (zmod.flags & MOD_MULT) { 780 hist_skip_flags = zmult? HIST_FOREIGN : 0; 781 } else { 782 hist_skip_flags ^= HIST_FOREIGN; 783 } 784 return 0; 785} 786 787/**/ 788int 789zle_goto_hist(int ev, int n, int skipdups) 790{ 791 Histent he = quietgethist(ev); 792 char *line = zlelineasstring(zleline, zlell, 0, NULL, NULL, 1); 793 794 if (!he || !(he = movehistent(he, n, hist_skip_flags))) 795 return 1; 796 if (skipdups && n) { 797 n = n < 0? -1 : 1; 798 while (he) { 799 int ret; 800 801 ret = zlinecmp(GETZLETEXT(he), line); 802 if (ret) 803 break; 804 he = movehistent(he, n, hist_skip_flags); 805 } 806 } 807 if (!he) 808 return 0; 809 zle_setline(he); 810 return 1; 811} 812 813/**/ 814int 815pushline(UNUSED(char **args)) 816{ 817 int n = zmult; 818 819 if (n < 0) 820 return 1; 821 zpushnode(bufstack, zlelineasstring(zleline, zlell, 0, NULL, NULL, 0)); 822 while (--n) 823 zpushnode(bufstack, ztrdup("")); 824 stackcs = zlecs; 825 *zleline = ZWC('\0'); 826 zlell = zlecs = 0; 827 clearlist = 1; 828 return 0; 829} 830 831/**/ 832int 833pushlineoredit(char **args) 834{ 835 int ics, ret; 836 ZLE_STRING_T s; 837 char *hline = hgetline(); 838 839 if (zmult < 0) 840 return 1; 841 if (hline && *hline) { 842 ZLE_STRING_T zhline = stringaszleline(hline, 0, &ics, NULL, NULL); 843 844 sizeline(ics + zlell + 1); 845 /* careful of overlapping copy */ 846 for (s = zleline + zlell; --s >= zleline; s[ics] = *s) 847 ; 848 ZS_memcpy(zleline, zhline, ics); 849 zlell += ics; 850 zlecs += ics; 851 free(zhline); 852 } 853 ret = pushline(args); 854 if (!isfirstln) 855 errflag = done = 1; 856 clearlist = 1; 857 return ret; 858} 859 860/**/ 861int 862pushinput(char **args) 863{ 864 int i, ret; 865 866 if (zmult < 0) 867 return 1; 868 zmult += i = !isfirstln; 869 ret = pushlineoredit(args); 870 zmult -= i; 871 return ret; 872} 873 874/* Renamed to avoid clash with library function */ 875/**/ 876int 877zgetline(UNUSED(char **args)) 878{ 879 char *s = getlinknode(bufstack); 880 881 if (!s) { 882 return 1; 883 } else { 884 int cc; 885 ZLE_STRING_T lineadd = stringaszleline(s, 0, &cc, NULL, NULL); 886 887 spaceinline(cc); 888 ZS_memcpy(zleline + zlecs, lineadd, cc); 889 zlecs += cc; 890 free(s); 891 free(lineadd); 892 clearlist = 1; 893 } 894 return 0; 895} 896 897/**/ 898int 899historyincrementalsearchbackward(char **args) 900{ 901 return doisearch(args, -1, 0); 902} 903 904/**/ 905int 906historyincrementalsearchforward(char **args) 907{ 908 return doisearch(args, 1, 0); 909} 910 911/**/ 912int 913historyincrementalpatternsearchbackward(char **args) 914{ 915 return doisearch(args, -1, 1); 916} 917 918/**/ 919int 920historyincrementalpatternsearchforward(char **args) 921{ 922 return doisearch(args, 1, 1); 923} 924 925static struct isrch_spot { 926 int hl; /* This spot's histline */ 927 int pat_hl; /* histline where pattern search started */ 928 unsigned short pos; /* The search position in our metafied str */ 929 unsigned short pat_pos; /* pos where pattern search started */ 930 unsigned short end_pos; /* The position of the end of the matched str */ 931 unsigned short cs; /* The visible search position to the user */ 932 unsigned short len; /* The search string's length */ 933 unsigned short flags; /* This spot's flags */ 934#define ISS_FORWARD 1 935#define ISS_NOMATCH_SHIFT 1 936} *isrch_spots; 937 938static int max_spot = 0; 939 940/**/ 941void 942free_isrch_spots(void) 943{ 944 zfree(isrch_spots, max_spot * sizeof(*isrch_spots)); 945 max_spot = 0; 946 isrch_spots = NULL; 947} 948 949/**/ 950static void 951set_isrch_spot(int num, int hl, int pos, int pat_hl, int pat_pos, 952 int end_pos, int cs, int len, int dir, int nomatch) 953{ 954 if (num >= max_spot) { 955 if (!isrch_spots) { 956 isrch_spots = (struct isrch_spot*) 957 zalloc((max_spot = 64) * sizeof *isrch_spots); 958 } else { 959 isrch_spots = (struct isrch_spot*)realloc((char*)isrch_spots, 960 (max_spot += 64) * sizeof *isrch_spots); 961 } 962 } 963 964 isrch_spots[num].hl = hl; 965 isrch_spots[num].pos = (unsigned short)pos; 966 isrch_spots[num].pat_hl = pat_hl; 967 isrch_spots[num].pat_pos = (unsigned short)pat_pos; 968 isrch_spots[num].end_pos = (unsigned short)end_pos; 969 isrch_spots[num].cs = (unsigned short)cs; 970 isrch_spots[num].len = (unsigned short)len; 971 isrch_spots[num].flags = (dir > 0? ISS_FORWARD : 0) 972 + (nomatch << ISS_NOMATCH_SHIFT); 973} 974 975/**/ 976static void 977get_isrch_spot(int num, int *hlp, int *posp, int *pat_hlp, int *pat_posp, 978 int *end_posp, int *csp, int *lenp, int *dirp, int *nomatch) 979{ 980 *hlp = isrch_spots[num].hl; 981 *posp = (int)isrch_spots[num].pos; 982 *pat_hlp = isrch_spots[num].pat_hl; 983 *pat_posp = (int)isrch_spots[num].pat_pos; 984 *end_posp = (int)isrch_spots[num].end_pos; 985 *csp = (int)isrch_spots[num].cs; 986 *lenp = (int)isrch_spots[num].len; 987 *dirp = (isrch_spots[num].flags & ISS_FORWARD)? 1 : -1; 988 *nomatch = (int)(isrch_spots[num].flags >> ISS_NOMATCH_SHIFT); 989} 990 991/* 992 * In pattern search mode, look through the list for a match at, or 993 * before or after the given position, according to the direction. 994 * Return new position or -1. 995 * 996 * Note this handles curpos out of range correctly, i.e. curpos < 0 997 * never matches when searching backwards and curpos > length of string 998 * never matches when searching forwards. 999 */ 1000static int 1001isearch_newpos(LinkList matchlist, int curpos, int dir, 1002 int *endmatchpos) 1003{ 1004 LinkNode node; 1005 1006 if (dir < 0) { 1007 for (node = lastnode(matchlist); 1008 node != (LinkNode)matchlist; decnode(node)) { 1009 Repldata rdata = (Repldata)getdata(node); 1010 if (rdata->b <= curpos) { 1011 *endmatchpos = rdata->e; 1012 return rdata->b; 1013 } 1014 } 1015 } else { 1016 for (node = firstnode(matchlist); 1017 node; incnode(node)) { 1018 Repldata rdata = (Repldata)getdata(node); 1019 if (rdata->b >= curpos) { 1020 *endmatchpos = rdata->e; 1021 return rdata->b; 1022 } 1023 } 1024 } 1025 1026 return -1; 1027} 1028 1029/* 1030 * Save an isearch buffer from sbuf to sbuf+sbptr 1031 * into the string *search with length *searchlen. 1032 * searchlen may be NULL; the string is a NULL-terminated metafied string. 1033 */ 1034static void 1035save_isearch_buffer(char *sbuf, int sbptr, 1036 char **search, int *searchlen) 1037{ 1038 if (*search) 1039 free(*search); 1040 *search = zalloc(sbptr+1); 1041 memcpy(*search, sbuf, sbptr); 1042 if (searchlen) 1043 *searchlen = sbptr; 1044 (*search)[sbptr] = '\0'; 1045} 1046 1047#define ISEARCH_PROMPT "XXXXXXX XXX-i-search: " 1048#define FAILING_TEXT "failing" 1049#define INVALID_TEXT "invalid" 1050#define BAD_TEXT_LEN 7 1051#define NORM_PROMPT_POS (BAD_TEXT_LEN+1) 1052#define FIRST_SEARCH_CHAR (NORM_PROMPT_POS + 14) 1053 1054/**/ 1055int isearch_active, isearch_startpos, isearch_endpos; 1056 1057/**/ 1058static int 1059doisearch(char **args, int dir, int pattern) 1060{ 1061 /* The full search buffer, including space for all prompts */ 1062 char *ibuf = zhalloc(80); 1063 /* 1064 * The part of the search buffer with the search string. 1065 * This is a normal metafied string. 1066 */ 1067 char *sbuf = ibuf + FIRST_SEARCH_CHAR; 1068 /* The previous line shown to the user */ 1069 char *last_line = NULL; 1070 /* Text of the history line being examined */ 1071 char *zt; 1072 /* 1073 * sbptr: index into sbuf. 1074 * top_spot: stack index into the "isrch_spot" stack. 1075 * sibuf: allocation size for ibuf 1076 */ 1077 int sbptr = 0, top_spot = 0, sibuf = 80; 1078 /* 1079 * nomatch = 1: failing isearch 1080 * nomatch = 2: invalid pattern 1081 * skip_line: finished with current line, skip to next 1082 * skip_pos: keep current line but try before/after current position. 1083 */ 1084 int nomatch = 0, skip_line = 0, skip_pos = 0; 1085 /* 1086 * odir: original search direction 1087 * sens: limit for zlinecmp to allow (3) or disallow (1) lower case 1088 * matching upper case. 1089 */ 1090 int odir = dir, sens = zmult == 1 ? 3 : 1; 1091 /* 1092 * hl: the number of the history line we are looking at 1093 * pos: the character position into it. On backward matches the 1094 * cursor will be set to this; on forward matches to the end 1095 * of the matched string 1096 */ 1097 int hl = histline, pos; 1098 /* 1099 * The value of hl and pos at which the last pattern match 1100 * search started. We need to record these because there's 1101 * a pathology with pattern matching. Here's an example. Suppose 1102 * the history consists of: 1103 * echo '*OH NO*' 1104 * echo '\n' 1105 * echo "*WHAT?*" 1106 * <...backward pattern search starts here...> 1107 * The user types "\". As there's nothing after it it's treated 1108 * literally (and I certainly don't want to change that). This 1109 * goes to the second line. Then the user types "*". This 1110 * ought to match the "*" in the line immediately before where the 1111 * search started. However, unless we return to that line for the 1112 * new search it will instead carry on to the first line. This is 1113 * different from straight string matching where we never have 1114 * to backtrack. 1115 * 1116 * I think these need resetting to the current hl and pos when 1117 * we start a new search or repeat a search. It seems to work, 1118 * anyway. 1119 * 1120 * We could optimize this more, but I don't think there's a lot 1121 * of point. (Translation: it's difficult.) 1122 */ 1123 int pat_hl = hl, pat_pos; 1124 /* 1125 * This is the flag that we need to revert the positions to 1126 * the above for the next pattern search. 1127 */ 1128 int revert_patpos = 0; 1129 /* 1130 * Another nasty feature related to the above. When 1131 * we revert the position, we might advance the search to 1132 * the same line again. When we do this the test for ignoring 1133 * duplicates may trigger. This flag indicates that in this 1134 * case it's OK. 1135 */ 1136 int dup_ok = 0; 1137 /* 1138 * End position of the match. 1139 * When forward matching, this is the position for the cursor. 1140 * When backward matching, the cursor position is pos. 1141 */ 1142 int end_pos = 0; 1143 /* 1144 * savekeys records the unget buffer, so that if we have arguments 1145 * they don't pollute the input. 1146 * feep indicates we should feep. This is a well-known word 1147 * meaning "to indicate an error in the zsh line editor". 1148 */ 1149 int savekeys = -1, feep = 0; 1150 /* Flag that we are at an old position, no need to search again */ 1151 int nosearch = 0; 1152 /* Command read as input: we don't read characters directly. */ 1153 Thingy cmd; 1154 /* Save the keymap if necessary */ 1155 char *okeymap; 1156 /* The current history entry, corresponding to hl */ 1157 Histent he; 1158 /* When pattern matching, the compiled pattern */ 1159 Patprog patprog = NULL; 1160 /* When pattern matching, the list of match positions */ 1161 LinkList matchlist = NULL; 1162 /* 1163 * When we exit isearching this may be a zle command to 1164 * execute. We save it and execute it after unmetafying the 1165 * command line. 1166 */ 1167 ZleIntFunc exitfn = (ZleIntFunc)0; 1168 /* 1169 * Flag that the search was aborted. 1170 */ 1171 int aborted = 0; 1172 1173 if (!(he = quietgethist(hl))) 1174 return 1; 1175 1176 selectlocalmap(isearch_keymap); 1177 1178 clearlist = 1; 1179 1180 if (*args) { 1181 int len; 1182 char *arg; 1183 savekeys = kungetct; 1184 arg = getkeystring(*args, &len, GETKEYS_BINDKEY, NULL); 1185 ungetbytes(arg, len); 1186 } 1187 1188 strcpy(ibuf, ISEARCH_PROMPT); 1189 /* careful with fwd/bck: we don't want the NULL copied */ 1190 memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3); 1191 okeymap = ztrdup(curkeymapname); 1192 selectkeymap("main", 1); 1193 1194 metafy_line(); 1195 remember_edits(); 1196 zt = GETZLETEXT(he); 1197 pat_pos = pos = zlemetacs; 1198 for (;;) { 1199 /* Remember the current values in case search fails (doesn't push). */ 1200 set_isrch_spot(top_spot, hl, pos, pat_hl, pat_pos, end_pos, 1201 zlemetacs, sbptr, dir, nomatch); 1202 if (sbptr == 1 && sbuf[0] == '^') { 1203 zlemetacs = 0; 1204 nomatch = 0; 1205 statusline = ibuf + NORM_PROMPT_POS; 1206 } else if (sbptr > 0) { 1207 /* The matched text, used as flag that we matched */ 1208 char *t = NULL; 1209 last_line = zt; 1210 1211 sbuf[sbptr] = '\0'; 1212 if (pattern && !patprog && !nosearch) { 1213 /* avoid too much heap use, can get heavy round here... */ 1214 char *patbuf = ztrdup(sbuf); 1215 char *patstring; 1216 /* 1217 * Use static pattern buffer since we don't need 1218 * to maintain it and won't call other pattern functions 1219 * meanwhile. 1220 * Use PAT_NOANCH because we don't need the match 1221 * anchored to the end, even if it is at the start. 1222 */ 1223 int patflags = PAT_STATIC|PAT_NOANCH; 1224 if (sbuf[0] == '^') { 1225 /* 1226 * We'll handle the anchor later when 1227 * we call into the globbing code. 1228 */ 1229 patstring = patbuf + 1; 1230 } else { 1231 /* Scanning for multiple matches per line */ 1232 patflags |= PAT_SCAN; 1233 patstring = patbuf; 1234 } 1235 if (sens == 3) 1236 patflags |= PAT_LCMATCHUC; 1237 tokenize(patstring); 1238 remnulargs(patstring); 1239 patprog = patcompile(patstring, patflags, NULL); 1240 free(patbuf); 1241 if (matchlist) { 1242 freematchlist(matchlist); 1243 matchlist = NULL; 1244 } 1245 if (patprog) { 1246 revert_patpos = 1; 1247 skip_pos = 0; 1248 } else { 1249 if (nomatch != 2) { 1250 handlefeep(zlenoargs); 1251 nomatch = 2; 1252 } 1253 /* indicate "invalid" in status line */ 1254 memcpy(ibuf, INVALID_TEXT, BAD_TEXT_LEN); 1255 statusline = ibuf; 1256 } 1257 } 1258 /* 1259 * skip search if pattern compilation failed, or 1260 * if we back somewhere we already searched. 1261 */ 1262 while ((!pattern || patprog) && !nosearch) { 1263 if (patprog) { 1264 if (revert_patpos) { 1265 /* 1266 * Search from where the previous 1267 * search started; see note above. 1268 * This is down here within the loop because of 1269 * the "nosearch" optimisation. 1270 */ 1271 revert_patpos = 0; 1272 dup_ok = 1; 1273 he = quietgethist(hl = pat_hl); 1274 zt = GETZLETEXT(he); 1275 pos = pat_pos; 1276 } 1277 /* 1278 * We are pattern matching against the current 1279 * line. If anchored at the start, this is 1280 * easy; a single test suffices. 1281 * 1282 * Otherwise, our strategy is to retrieve a linked 1283 * list of all matches within the current line and 1284 * scan through it as appropriate. This isn't 1285 * actually significantly more efficient, but 1286 * it is algorithmically easier since we just 1287 * need a single one-off line-matching interface 1288 * to the pattern code. We use a variant of 1289 * the code used for replacing within parameters 1290 * which for historical reasons is in glob.c rather 1291 * than pattern.c. 1292 * 1293 * The code for deciding whether to skip something 1294 * is a bit icky but that sort of code always is. 1295 */ 1296 if (!skip_line) { 1297 if (sbuf[0] == '^') { 1298 /* 1299 * skip_pos applies to the whole line in 1300 * this mode. 1301 */ 1302 if (!skip_pos && 1303 pattryrefs(patprog, zt, -1, -1, 0, NULL, NULL, 1304 &end_pos)) 1305 t = zt; 1306 } else { 1307 if (!matchlist && !skip_pos) { 1308 if (!getmatchlist(zt, patprog, &matchlist) || 1309 !firstnode(matchlist)) { 1310 if (matchlist) { 1311 freematchlist(matchlist); 1312 matchlist = NULL; 1313 } 1314 } 1315 } 1316 if (matchlist) { 1317 int newpos; 1318 if (!skip_pos) { 1319 /* OK to match at current pos */ 1320 newpos = pos; 1321 } else { 1322 if (dir < 0) 1323 newpos = pos - 1; 1324 else 1325 newpos = pos + 1; 1326 } 1327 newpos = isearch_newpos(matchlist, newpos, 1328 dir, &end_pos); 1329 /* need a new list next time if off the end */ 1330 if (newpos < 0) { 1331 freematchlist(matchlist); 1332 matchlist = NULL; 1333 } else { 1334 t = zt + newpos; 1335 } 1336 } 1337 } 1338 } 1339 skip_pos = 0; 1340 } else { 1341 /* 1342 * If instructed, move past a match position: 1343 * backwards if searching backwards (skipping 1344 * the line if we're at the start), forwards 1345 * if searching forwards (skipping a line if we're 1346 * at the end). 1347 */ 1348 if (skip_pos) { 1349 if (dir < 0) { 1350 if (pos == 0) 1351 skip_line = 1; 1352 else 1353 pos = backwardmetafiedchar(zlemetaline, 1354 zlemetaline + pos, 1355 NULL) - zlemetaline; 1356 } else if (sbuf[0] != '^') { 1357 if (pos >= (int)strlen(zt) - 1) 1358 skip_line = 1; 1359 else 1360 pos += 1; 1361 } else 1362 skip_line = 1; 1363 skip_pos = 0; 1364 } 1365 /* 1366 * First search for a(nother) match within the 1367 * current line, unless we've been told to skip it. 1368 */ 1369 if (!skip_line) { 1370 if (sbuf[0] == '^') { 1371 if (zlinecmp(zt, sbuf + 1) < sens) 1372 t = zt; 1373 } else 1374 t = zlinefind(zt, pos, sbuf, dir, sens); 1375 if (t) 1376 end_pos = (t - zt) + sbptr - (sbuf[0] == '^'); 1377 } 1378 } 1379 if (t) { 1380 pos = t - zt; 1381 break; 1382 } 1383 /* 1384 * If not found within that line, move through 1385 * the history to try again. 1386 */ 1387 if (!(zlereadflags & ZLRF_HISTORY) 1388 || !(he = movehistent(he, dir, hist_skip_flags))) { 1389 if (sbptr == (int)isrch_spots[top_spot-1].len 1390 && (isrch_spots[top_spot-1].flags >> ISS_NOMATCH_SHIFT)) 1391 top_spot--; 1392 get_isrch_spot(top_spot, &hl, &pos, &pat_hl, &pat_pos, 1393 &end_pos, &zlemetacs, &sbptr, &dir, 1394 &nomatch); 1395 if (nomatch != 1) { 1396 feep = 1; 1397 nomatch = 1; 1398 } 1399 he = quietgethist(hl); 1400 zt = GETZLETEXT(he); 1401 skip_line = 0; 1402 /* indicate "failing" in status line */ 1403 memcpy(ibuf, nomatch == 2 ? INVALID_TEXT :FAILING_TEXT, 1404 BAD_TEXT_LEN); 1405 statusline = ibuf; 1406 break; 1407 } 1408 hl = he->histnum; 1409 zt = GETZLETEXT(he); 1410 pos = (dir == 1) ? 0 : strlen(zt); 1411 if (dup_ok) 1412 skip_line = 0; 1413 else 1414 skip_line = isset(HISTFINDNODUPS) 1415 ? !!(he->node.flags & HIST_DUP) 1416 : !strcmp(zt, last_line); 1417 } 1418 dup_ok = 0; 1419 /* 1420 * If we matched above (t set), set the new line. 1421 * If we didn't, but are here because we are on a previous 1422 * match (nosearch set and nomatch not, set the line again). 1423 */ 1424 if (t || (nosearch && !nomatch)) { 1425 zle_setline(he); 1426 if (dir == 1) 1427 zlemetacs = end_pos; 1428 else 1429 zlemetacs = pos; 1430 statusline = ibuf + NORM_PROMPT_POS; 1431 nomatch = 0; 1432 } 1433 } else { 1434 top_spot = 0; 1435 nomatch = 0; 1436 statusline = ibuf + NORM_PROMPT_POS; 1437 } 1438 nosearch = 0; 1439 if (feep) { 1440 handlefeep(zlenoargs); 1441 feep = 0; 1442 } 1443 sbuf[sbptr] = '_'; 1444 sbuf[sbptr+1] = '\0'; 1445 if (!nomatch && sbptr && (sbptr > 1 || sbuf[0] != '^')) { 1446#ifdef MULTIBYTE_SUPPORT 1447 int charpos = 0, charcount = 0, ret; 1448 wint_t wc; 1449 mbstate_t mbs; 1450 1451 /* 1452 * Count unmetafied character positions for the 1453 * start and end of the match for the benefit of 1454 * highlighting. 1455 */ 1456 memset(&mbs, 0, sizeof(mbs)); 1457 while (charpos < end_pos) { 1458 ret = mb_metacharlenconv_r(zlemetaline + charpos, &wc, &mbs); 1459 if (ret <= 0) /* Unrecognised, treat as single char */ 1460 ret = 1; 1461 if (charpos <= pos && pos < charpos + ret) 1462 isearch_startpos = charcount; 1463 charcount++; 1464 charpos += ret; 1465 } 1466 isearch_endpos = charcount; 1467#else 1468 isearch_startpos = ztrsub(zlemetaline + pos, zlemetaline); 1469 isearch_endpos = ztrsub(zlemetaline + end_pos, 1470 zlemetaline); 1471#endif 1472 isearch_active = 1; 1473 } else 1474 isearch_active = 0; 1475 ref: 1476 zlecallhook("zle-isearch-update", NULL); 1477 zrefresh(); 1478 if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { 1479 int i; 1480 aborted = 1; 1481 save_isearch_buffer(sbuf, sbptr, 1482 &previous_aborted_search, NULL); 1483 get_isrch_spot(0, &hl, &pos, &pat_hl, &pat_pos, &end_pos, 1484 &i, &sbptr, &dir, &nomatch); 1485 he = quietgethist(hl); 1486 zle_setline(he); 1487 zt = GETZLETEXT(he); 1488 zlemetacs = i; 1489 break; 1490 } 1491 if(cmd == Th(z_clearscreen)) { 1492 clearscreen(zlenoargs); 1493 goto ref; 1494 } else if(cmd == Th(z_redisplay)) { 1495 redisplay(zlenoargs); 1496 goto ref; 1497 } else if(cmd == Th(z_vicmdmode)) { 1498 if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0)) 1499 feep = 1; 1500 goto ref; 1501 } else if (cmd == Th(z_vibackwarddeletechar) || 1502 cmd == Th(z_backwarddeletechar) || 1503 cmd == Th(z_vibackwardkillword) || 1504 cmd == Th(z_backwardkillword) || 1505 cmd == Th(z_backwarddeleteword)) { 1506 int only_one = (cmd == Th(z_vibackwarddeletechar) || 1507 cmd == Th(z_backwarddeletechar)); 1508 int old_sbptr = sbptr; 1509 if (top_spot) { 1510 for (;;) { 1511 get_isrch_spot(--top_spot, &hl, &pos, &pat_hl, 1512 &pat_pos, &end_pos, &zlemetacs, 1513 &sbptr, &dir, &nomatch); 1514 if (only_one || !top_spot || old_sbptr != sbptr) 1515 break; 1516 } 1517 patprog = NULL; 1518 nosearch = 1; 1519 skip_pos = 0; 1520 } else 1521 feep = 1; 1522 if (nomatch) { 1523 memcpy(ibuf, nomatch == 2 ? INVALID_TEXT : FAILING_TEXT, 1524 BAD_TEXT_LEN); 1525 statusline = ibuf; 1526 skip_pos = 1; 1527 } 1528 he = quietgethist(hl); 1529 zt = GETZLETEXT(he); 1530 /* 1531 * Set the line for the cases where we won't go past 1532 * the usual line-setting logic: if we're not on a match, 1533 * or if we don't have enough to search for. 1534 */ 1535 if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) { 1536 int i = zlemetacs; 1537 zle_setline(he); 1538 zlemetacs = i; 1539 } 1540 memcpy(ibuf + NORM_PROMPT_POS, 1541 (dir == 1) ? "fwd" : "bck", 3); 1542 continue; 1543 } else if(cmd == Th(z_acceptandhold)) { 1544 exitfn = acceptandhold; 1545 break; 1546 } else if(cmd == Th(z_acceptandinfernexthistory)) { 1547 exitfn = acceptandinfernexthistory; 1548 break; 1549 } else if(cmd == Th(z_acceptlineanddownhistory)) { 1550 exitfn = acceptlineanddownhistory; 1551 break; 1552 } else if(cmd == Th(z_acceptline)) { 1553 exitfn = acceptline; 1554 break; 1555 } else if(cmd == Th(z_historyincrementalsearchbackward) || 1556 cmd == Th(z_historyincrementalpatternsearchbackward)) { 1557 pat_hl = hl; 1558 pat_pos = pos; 1559 set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, 1560 zlemetacs, sbptr, dir, nomatch); 1561 if (dir != -1) 1562 dir = -1; 1563 else 1564 skip_pos = 1; 1565 goto rpt; 1566 } else if(cmd == Th(z_historyincrementalsearchforward) || 1567 cmd == Th(z_historyincrementalpatternsearchforward)) { 1568 pat_hl = hl; 1569 pat_pos = pos; 1570 set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, 1571 zlemetacs, sbptr, dir, nomatch); 1572 if (dir != 1) 1573 dir = 1; 1574 else 1575 skip_pos = 1; 1576 goto rpt; 1577 } else if(cmd == Th(z_virevrepeatsearch)) { 1578 pat_hl = hl; 1579 pat_pos = pos; 1580 set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, 1581 zlemetacs, sbptr, dir, nomatch); 1582 dir = -odir; 1583 skip_pos = 1; 1584 goto rpt; 1585 } else if(cmd == Th(z_virepeatsearch)) { 1586 pat_hl = hl; 1587 pat_pos = pos; 1588 set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, 1589 zlemetacs, sbptr, dir, nomatch); 1590 dir = odir; 1591 skip_pos = 1; 1592 rpt: 1593 if (!sbptr && previous_search_len) { 1594 if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) { 1595 ibuf = hrealloc((char *)ibuf, sibuf, 1596 (sibuf + previous_search_len)); 1597 sbuf = ibuf + FIRST_SEARCH_CHAR; 1598 sibuf += previous_search_len; 1599 } 1600 memcpy(sbuf, previous_search, sbptr = previous_search_len); 1601 } 1602 memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3); 1603 continue; 1604 } else if(cmd == Th(z_viquotedinsert) || 1605 cmd == Th(z_quotedinsert)) { 1606 if(cmd == Th(z_viquotedinsert)) { 1607 sbuf[sbptr] = '^'; 1608 sbuf[sbptr+1] = '\0'; 1609 zrefresh(); 1610 } 1611 if (getfullchar(0) == ZLEEOF) 1612 feep = 1; 1613 else 1614 goto ins; 1615 } else if (cmd == Th(z_acceptsearch)) { 1616 break; 1617 } else { 1618 if(cmd == Th(z_selfinsertunmeta)) { 1619 fixunmeta(); 1620 } else if (cmd == Th(z_magicspace)) { 1621 fixmagicspace(); 1622 } else if (cmd == Th(z_selfinsert)) { 1623#ifdef MULTIBYTE_SUPPORT 1624 if (!lastchar_wide_valid) 1625 if (getrestchar(lastchar) == WEOF) { 1626 handlefeep(zlenoargs); 1627 continue; 1628 } 1629#else 1630 ; 1631#endif 1632 } else { 1633 ungetkeycmd(); 1634 if (cmd == Th(z_sendbreak)) { 1635 aborted = 1; 1636 save_isearch_buffer(sbuf, sbptr, 1637 &previous_aborted_search, NULL); 1638 sbptr = 0; 1639 } 1640 break; 1641 } 1642 ins: 1643 if (sbptr == PATH_MAX) { 1644 feep = 1; 1645 continue; 1646 } 1647 set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, 1648 zlemetacs, sbptr, dir, nomatch); 1649 if (sbptr >= sibuf - FIRST_SEARCH_CHAR - 2 1650#ifdef MULTIBYTE_SUPPORT 1651 - 2 * (int)MB_CUR_MAX 1652#endif 1653 ) { 1654 ibuf = hrealloc(ibuf, sibuf, sibuf * 2); 1655 sbuf = ibuf + FIRST_SEARCH_CHAR; 1656 sibuf *= 2; 1657 } 1658 /* 1659 * We've supposedly arranged above that lastchar_wide is 1660 * always valid at this point. 1661 */ 1662 sbptr += zlecharasstring(LASTFULLCHAR, sbuf + sbptr); 1663 patprog = NULL; 1664 } 1665 if (feep) 1666 handlefeep(zlenoargs); 1667 feep = 0; 1668 } 1669 if (sbptr) { 1670 save_isearch_buffer(sbuf, sbptr, 1671 &previous_search, &previous_search_len); 1672 } 1673 statusline = NULL; 1674 unmetafy_line(); 1675 zlecallhook("zle-isearch-exit", NULL); 1676 if (exitfn) 1677 exitfn(zlenoargs); 1678 selectkeymap(okeymap, 1); 1679 zsfree(okeymap); 1680 if (matchlist) 1681 freematchlist(matchlist); 1682 isearch_active = 0; 1683 /* 1684 * Don't allow unused characters provided as a string to the 1685 * widget to overflow and be used as separated commands. 1686 */ 1687 if (savekeys >= 0 && kungetct > savekeys) 1688 kungetct = savekeys; 1689 1690 selectlocalmap(NULL); 1691 1692 return aborted ? 3 : nomatch; 1693} 1694 1695static Histent 1696infernexthist(Histent he, UNUSED(char **args)) 1697{ 1698 metafy_line(); 1699 for (he = movehistent(he, -2, HIST_FOREIGN); 1700 he; he = movehistent(he, -1, HIST_FOREIGN)) { 1701 if (!zlinecmp(GETZLETEXT(he), zlemetaline)) { 1702 unmetafy_line(); 1703 return movehistent(he, 1, HIST_FOREIGN); 1704 } 1705 } 1706 unmetafy_line(); 1707 return NULL; 1708} 1709 1710/**/ 1711int 1712acceptandinfernexthistory(char **args) 1713{ 1714 Histent he; 1715 1716 if (!(he = infernexthist(hist_ring, args))) 1717 return 1; 1718 zpushnode(bufstack, ztrdup(he->node.nam)); 1719 done = 1; 1720 stackhist = he->histnum; 1721 return 0; 1722} 1723 1724/**/ 1725int 1726infernexthistory(char **args) 1727{ 1728 Histent he = quietgethist(histline); 1729 1730 if (!he || !(he = infernexthist(he, args))) 1731 return 1; 1732 zle_setline(he); 1733 return 0; 1734} 1735 1736/**/ 1737int 1738vifetchhistory(UNUSED(char **args)) 1739{ 1740 if (zmult < 0) 1741 return 1; 1742 if (histline == curhist) { 1743 if (!(zmod.flags & MOD_MULT)) { 1744 zlecs = zlell; 1745 zlecs = findbol(); 1746 return 0; 1747 } 1748 } 1749 if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) && 1750 isset(HISTBEEP)) { 1751 return 1; 1752 } 1753 return 0; 1754} 1755 1756/* the last vi search */ 1757 1758static char *visrchstr, *vipenultsrchstr; 1759static int visrchsense; 1760 1761/**/ 1762static int 1763getvisrchstr(void) 1764{ 1765 char *sbuf = zhalloc(80); 1766 int sptr = 1, ret = 0, ssbuf = 80, feep = 0; 1767 Thingy cmd; 1768 char *okeymap = ztrdup(curkeymapname); 1769 1770 if (vipenultsrchstr) { 1771 zsfree(vipenultsrchstr); 1772 vipenultsrchstr = NULL; 1773 } 1774 1775 if (visrchstr) { 1776 vipenultsrchstr = visrchstr; 1777 visrchstr = NULL; 1778 } 1779 clearlist = 1; 1780 statusline = sbuf; 1781 sbuf[0] = (visrchsense == -1) ? '?' : '/'; 1782 selectkeymap("main", 1); 1783 while (sptr) { 1784 sbuf[sptr] = '_'; 1785 sbuf[sptr+1] = '\0'; 1786 zrefresh(); 1787 if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { 1788 ret = 0; 1789 break; 1790 } 1791 if(cmd == Th(z_magicspace)) { 1792 fixmagicspace(); 1793 cmd = Th(z_selfinsert); 1794 } 1795 if(cmd == Th(z_redisplay)) { 1796 redisplay(zlenoargs); 1797 } else if(cmd == Th(z_clearscreen)) { 1798 clearscreen(zlenoargs); 1799 } else if(cmd == Th(z_acceptline) || 1800 cmd == Th(z_vicmdmode)) { 1801 sbuf[sptr] = ZWC('\0'); 1802 visrchstr = ztrdup(sbuf+1); 1803 if (!strlen(visrchstr)) { 1804 zsfree(visrchstr); 1805 visrchstr = ztrdup(vipenultsrchstr); 1806 } 1807 ret = 1; 1808 sptr = 0; 1809 } else if(cmd == Th(z_backwarddeletechar) || 1810 cmd == Th(z_vibackwarddeletechar)) { 1811 sptr = backwardmetafiedchar(sbuf+1, sbuf+sptr, NULL) - sbuf; 1812 } else if(cmd == Th(z_backwardkillword) || 1813 cmd == Th(z_vibackwardkillword)) { 1814 convchar_t cc; 1815 char *newpos; 1816 while (sptr != 1) { 1817 newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc); 1818 if (!ZC_iblank(cc)) 1819 break; 1820 sptr = newpos - sbuf; 1821 } 1822 if (sptr > 1) { 1823 newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc); 1824 if (ZC_iident(cc)) { 1825 for (;;) { 1826 sptr = newpos - sbuf; 1827 if (sptr == 1) 1828 break; 1829 newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc); 1830 if (!ZC_iident(cc)) 1831 break; 1832 } 1833 } else { 1834 for (;;) { 1835 sptr = newpos - sbuf; 1836 if (sptr == 1) 1837 break; 1838 newpos = backwardmetafiedchar(sbuf+1, sbuf+sptr, &cc); 1839 if (ZC_iident(cc) || ZC_iblank(cc)) 1840 break; 1841 } 1842 } 1843 } 1844 } else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) { 1845 if(cmd == Th(z_viquotedinsert)) { 1846 sbuf[sptr] = '^'; 1847 zrefresh(); 1848 } 1849 if (getfullchar(0) == ZLEEOF) 1850 feep = 1; 1851 else 1852 goto ins; 1853 } else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) { 1854 if(cmd == Th(z_selfinsertunmeta)) { 1855 fixunmeta(); 1856 } else { 1857#ifdef MULTIBYTE_SUPPORT 1858 if (!lastchar_wide_valid) 1859 if (getrestchar(lastchar) == WEOF) { 1860 handlefeep(zlenoargs); 1861 continue; 1862 } 1863#else 1864 ; 1865#endif 1866 } 1867 ins: 1868 if (sptr == ssbuf - 1) { 1869 char *newbuf = (char *)zhalloc((ssbuf *= 2)); 1870 strcpy(newbuf, sbuf); 1871 statusline = sbuf = newbuf; 1872 } 1873 sptr += zlecharasstring(LASTFULLCHAR, sbuf + sptr); 1874 } else { 1875 feep = 1; 1876 } 1877 if (feep) 1878 handlefeep(zlenoargs); 1879 feep = 0; 1880 } 1881 statusline = NULL; 1882 selectkeymap(okeymap, 1); 1883 zsfree(okeymap); 1884 return ret; 1885} 1886 1887/**/ 1888int 1889vihistorysearchforward(char **args) 1890{ 1891 if (*args) { 1892 int ose = visrchsense, ret; 1893 char *ost = visrchstr; 1894 1895 visrchsense = 1; 1896 visrchstr = *args; 1897 ret = virepeatsearch(zlenoargs); 1898 visrchsense = ose; 1899 visrchstr = ost; 1900 return ret; 1901 } 1902 visrchsense = 1; 1903 if (getvisrchstr()) 1904 return virepeatsearch(zlenoargs); 1905 return 1; 1906} 1907 1908/**/ 1909int 1910vihistorysearchbackward(char **args) 1911{ 1912 if (*args) { 1913 int ose = visrchsense, ret; 1914 char *ost = visrchstr; 1915 1916 visrchsense = -1; 1917 visrchstr = *args; 1918 ret = virepeatsearch(zlenoargs); 1919 visrchsense = ose; 1920 visrchstr = ost; 1921 return ret; 1922 } 1923 visrchsense = -1; 1924 if (getvisrchstr()) 1925 return virepeatsearch(zlenoargs); 1926 return 1; 1927} 1928 1929/**/ 1930int 1931virepeatsearch(UNUSED(char **args)) 1932{ 1933 Histent he; 1934 int n = zmult; 1935 char *zt; 1936 1937 if (!visrchstr) 1938 return 1; 1939 if (zmult < 0) { 1940 n = -n; 1941 visrchsense = -visrchsense; 1942 } 1943 if (!(he = quietgethist(histline))) 1944 return 1; 1945 metafy_line(); 1946 while ((he = movehistent(he, visrchsense, hist_skip_flags))) { 1947 if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP) 1948 continue; 1949 zt = GETZLETEXT(he); 1950 if (zlinecmp(zt, zlemetaline) && 1951 (*visrchstr == '^' ? strpfx(visrchstr + 1, zt) : 1952 zlinefind(zt, 0, visrchstr, 1, 1) != 0)) { 1953 if (--n <= 0) { 1954 unmetafy_line(); 1955 zle_setline(he); 1956 return 0; 1957 } 1958 } 1959 } 1960 unmetafy_line(); 1961 return 1; 1962} 1963 1964/**/ 1965int 1966virevrepeatsearch(char **args) 1967{ 1968 int ret; 1969 visrchsense = -visrchsense; 1970 ret = virepeatsearch(args); 1971 visrchsense = -visrchsense; 1972 return ret; 1973} 1974 1975/* Extra function added by A.R. Iano-Fletcher. */ 1976/*The extern variable "zlecs" is the position of the cursor. */ 1977/* history-beginning-search-backward */ 1978 1979/**/ 1980int 1981historybeginningsearchbackward(char **args) 1982{ 1983 Histent he; 1984 int cpos = zlecs; /* save cursor position */ 1985 int n = zmult; 1986 char *zt; 1987 1988 if (zmult < 0) { 1989 int ret; 1990 zmult = -n; 1991 ret = historybeginningsearchforward(args); 1992 zmult = n; 1993 return ret; 1994 } 1995 if (!(he = quietgethist(histline))) 1996 return 1; 1997 metafy_line(); 1998 while ((he = movehistent(he, -1, hist_skip_flags))) { 1999 int tst; 2000 char sav; 2001 if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP) 2002 continue; 2003 zt = GETZLETEXT(he); 2004 sav = zlemetaline[zlemetacs]; 2005 zlemetaline[zlemetacs] = '\0'; 2006 tst = zlinecmp(zt, zlemetaline); 2007 zlemetaline[zlemetacs] = sav; 2008 if (tst < 0 && zlinecmp(zt, zlemetaline)) { 2009 if (--n <= 0) { 2010 unmetafy_line(); 2011 zle_setline(he); 2012 zlecs = cpos; 2013 CCRIGHT(); 2014 return 0; 2015 } 2016 } 2017 } 2018 unmetafy_line(); 2019 return 1; 2020} 2021 2022/* Extra function added by A.R. Iano-Fletcher. */ 2023 2024/* history-beginning-search-forward */ 2025/**/ 2026int 2027historybeginningsearchforward(char **args) 2028{ 2029 Histent he; 2030 int cpos = zlecs; /* save cursor position */ 2031 int n = zmult; 2032 char *zt; 2033 2034 if (zmult < 0) { 2035 int ret; 2036 zmult = -n; 2037 ret = historybeginningsearchbackward(args); 2038 zmult = n; 2039 return ret; 2040 } 2041 if (!(he = quietgethist(histline))) 2042 return 1; 2043 metafy_line(); 2044 while ((he = movehistent(he, 1, hist_skip_flags))) { 2045 char sav; 2046 int tst; 2047 if (isset(HISTFINDNODUPS) && he->node.flags & HIST_DUP) 2048 continue; 2049 zt = GETZLETEXT(he); 2050 sav = zlemetaline[zlemetacs]; 2051 zlemetaline[zlemetacs] = '\0'; 2052 tst = zlinecmp(zt, zlemetaline) < (he->histnum == curhist); 2053 zlemetaline[zlemetacs] = sav; 2054 if (tst && zlinecmp(zt, zlemetaline)) { 2055 if (--n <= 0) { 2056 unmetafy_line(); 2057 zle_setline(he); 2058 zlecs = cpos; 2059 CCRIGHT(); 2060 return 0; 2061 } 2062 } 2063 } 2064 unmetafy_line(); 2065 return 1; 2066} 2067