1/* 2 * Copyright (C) 1984-2007 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12/* 13 * Routines to search a file for a pattern. 14 */ 15 16#include "less.h" 17#include "position.h" 18#include "charset.h" 19 20#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 21#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 22 23#if HAVE_POSIX_REGCOMP 24#include <regex.h> 25#ifdef REG_EXTENDED 26#define REGCOMP_FLAG REG_EXTENDED 27#else 28#define REGCOMP_FLAG 0 29#endif 30#endif 31#if HAVE_PCRE 32#include <pcre.h> 33#endif 34#if HAVE_RE_COMP 35char *re_comp(); 36int re_exec(); 37#endif 38#if HAVE_REGCMP 39char *regcmp(); 40char *regex(); 41extern char *__loc1; 42#endif 43#if HAVE_V8_REGCOMP 44#include "regexp.h" 45#endif 46 47static int match(); 48 49extern int sigs; 50extern int how_search; 51extern int caseless; 52extern int linenums; 53extern int sc_height; 54extern int jump_sline; 55extern int bs_mode; 56extern int ctldisp; 57extern int status_col; 58extern void * constant ml_search; 59extern POSITION start_attnpos; 60extern POSITION end_attnpos; 61#if HILITE_SEARCH 62extern int hilite_search; 63extern int screen_trashed; 64extern int size_linebuf; 65extern int squished; 66extern int can_goto_line; 67extern int utf_mode; 68static int hide_hilite; 69static int oldbot; 70static POSITION prep_startpos; 71static POSITION prep_endpos; 72 73struct hilite 74{ 75 struct hilite *hl_next; 76 POSITION hl_startpos; 77 POSITION hl_endpos; 78}; 79static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; 80#define hl_first hl_next 81#endif 82 83/* 84 * These are the static variables that represent the "remembered" 85 * search pattern. 86 */ 87#if HAVE_POSIX_REGCOMP 88static regex_t *regpattern = NULL; 89#endif 90#if HAVE_PCRE 91pcre *regpattern = NULL; 92#endif 93#if HAVE_RE_COMP 94int re_pattern = 0; 95#endif 96#if HAVE_REGCMP 97static char *cpattern = NULL; 98#endif 99#if HAVE_V8_REGCOMP 100static struct regexp *regpattern = NULL; 101#endif 102 103static int is_caseless; 104static int is_ucase_pattern; 105static int last_search_type; 106static char *last_pattern = NULL; 107 108#define CVT_TO_LC 01 /* Convert upper-case to lower-case */ 109#define CVT_BS 02 /* Do backspace processing */ 110#define CVT_CRLF 04 /* Remove CR after LF */ 111#define CVT_ANSI 010 /* Remove ANSI escape sequences */ 112 113/* 114 * Get the length of a buffer needed to convert a string. 115 */ 116 static int 117cvt_length(len, ops) 118 int len; 119 int ops; 120{ 121 if (utf_mode) 122 /* 123 * Just copying a string in UTF-8 mode can cause it to grow 124 * in length. 125 * Six output bytes for one input byte is the worst case 126 * (and unfortunately is far more than is needed in any 127 * non-pathological situation, so this is very wasteful). 128 */ 129 len *= 6; 130 return len + 1; 131} 132 133/* 134 * Convert text. Perform one or more of these transformations: 135 */ 136 static void 137cvt_text(odst, osrc, lenp, ops) 138 char *odst; 139 char *osrc; 140 int *lenp; 141 int ops; 142{ 143 char *dst; 144 char *src; 145 register char *src_end; 146 LWCHAR ch; 147 148 if (lenp != NULL) 149 src_end = osrc + *lenp; 150 else 151 src_end = osrc + strlen(osrc); 152 153 for (src = osrc, dst = odst; src < src_end; ) 154 { 155 ch = step_char(&src, +1, src_end); 156 if ((ops & CVT_TO_LC) && IS_UPPER(ch)) 157 { 158 /* Convert uppercase to lowercase. */ 159 put_wchar(&dst, TO_LOWER(ch)); 160 } else if ((ops & CVT_BS) && ch == '\b' && dst > odst) 161 { 162 /* Delete backspace and preceding char. */ 163 do { 164 dst--; 165 } while (dst > odst && 166 !IS_ASCII_OCTET(*dst) && !IS_UTF8_LEAD(*dst)); 167 } else if ((ops & CVT_ANSI) && IS_CSI_START(ch)) 168 { 169 /* Skip to end of ANSI escape sequence. */ 170 src++; /* skip the CSI start char */ 171 while (src < src_end) 172 if (!is_ansi_middle(*src++)) 173 break; 174 } else 175 /* Just copy. */ 176 put_wchar(&dst, ch); 177 } 178 if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r') 179 dst--; 180 *dst = '\0'; 181 if (lenp != NULL) 182 *lenp = dst - odst; 183} 184 185/* 186 * Determine which conversions to perform. 187 */ 188 static int 189get_cvt_ops() 190{ 191 int ops = 0; 192 if (is_caseless || bs_mode == BS_SPECIAL) 193 { 194 if (is_caseless) 195 ops |= CVT_TO_LC; 196 if (bs_mode == BS_SPECIAL) 197 ops |= CVT_BS; 198 if (bs_mode != BS_CONTROL) 199 ops |= CVT_CRLF; 200 } else if (bs_mode != BS_CONTROL) 201 { 202 ops |= CVT_CRLF; 203 } 204 if (ctldisp == OPT_ONPLUS) 205 ops |= CVT_ANSI; 206 return (ops); 207} 208 209/* 210 * Are there any uppercase letters in this string? 211 */ 212 static int 213is_ucase(str) 214 char *str; 215{ 216 char *str_end = str + strlen(str); 217 LWCHAR ch; 218 219 while (str < str_end) 220 { 221 ch = step_char(&str, +1, str_end); 222 if (IS_UPPER(ch)) 223 return (1); 224 } 225 return (0); 226} 227 228/* 229 * Is there a previous (remembered) search pattern? 230 */ 231 static int 232prev_pattern() 233{ 234 if (last_search_type & SRCH_NO_REGEX) 235 return (last_pattern != NULL); 236#if HAVE_POSIX_REGCOMP 237 return (regpattern != NULL); 238#endif 239#if HAVE_PCRE 240 return (regpattern != NULL); 241#endif 242#if HAVE_RE_COMP 243 return (re_pattern != 0); 244#endif 245#if HAVE_REGCMP 246 return (cpattern != NULL); 247#endif 248#if HAVE_V8_REGCOMP 249 return (regpattern != NULL); 250#endif 251#if NO_REGEX 252 return (last_pattern != NULL); 253#endif 254} 255 256#if HILITE_SEARCH 257/* 258 * Repaint the hilites currently displayed on the screen. 259 * Repaint each line which contains highlighted text. 260 * If on==0, force all hilites off. 261 */ 262 public void 263repaint_hilite(on) 264 int on; 265{ 266 int slinenum; 267 POSITION pos; 268 POSITION epos; 269 int save_hide_hilite; 270 271 if (squished) 272 repaint(); 273 274 save_hide_hilite = hide_hilite; 275 if (!on) 276 { 277 if (hide_hilite) 278 return; 279 hide_hilite = 1; 280 } 281 282 if (!can_goto_line) 283 { 284 repaint(); 285 hide_hilite = save_hide_hilite; 286 return; 287 } 288 289 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 290 { 291 pos = position(slinenum); 292 if (pos == NULL_POSITION) 293 continue; 294 epos = position(slinenum+1); 295#if 0 296 /* 297 * If any character in the line is highlighted, 298 * repaint the line. 299 * 300 * {{ This doesn't work -- if line is drawn with highlights 301 * which should be erased (e.g. toggle -i with status column), 302 * we must redraw the line even if it has no highlights. 303 * For now, just repaint every line. }} 304 */ 305 if (is_hilited(pos, epos, 1, NULL)) 306#endif 307 { 308 (void) forw_line(pos); 309 goto_line(slinenum); 310 put_line(); 311 } 312 } 313 if (!oldbot) 314 lower_left(); 315 hide_hilite = save_hide_hilite; 316} 317 318/* 319 * Clear the attn hilite. 320 */ 321 public void 322clear_attn() 323{ 324 int slinenum; 325 POSITION old_start_attnpos; 326 POSITION old_end_attnpos; 327 POSITION pos; 328 POSITION epos; 329 int moved = 0; 330 331 if (start_attnpos == NULL_POSITION) 332 return; 333 old_start_attnpos = start_attnpos; 334 old_end_attnpos = end_attnpos; 335 start_attnpos = end_attnpos = NULL_POSITION; 336 337 if (!can_goto_line) 338 { 339 repaint(); 340 return; 341 } 342 if (squished) 343 repaint(); 344 345 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 346 { 347 pos = position(slinenum); 348 if (pos == NULL_POSITION) 349 continue; 350 epos = position(slinenum+1); 351 if (pos < old_end_attnpos && 352 (epos == NULL_POSITION || epos > old_start_attnpos)) 353 { 354 (void) forw_line(pos); 355 goto_line(slinenum); 356 put_line(); 357 moved = 1; 358 } 359 } 360 if (moved) 361 lower_left(); 362} 363#endif 364 365/* 366 * Hide search string highlighting. 367 */ 368 public void 369undo_search() 370{ 371 if (!prev_pattern()) 372 { 373 error("No previous regular expression", NULL_PARG); 374 return; 375 } 376#if HILITE_SEARCH 377 hide_hilite = !hide_hilite; 378 repaint_hilite(1); 379#endif 380} 381 382/* 383 * Compile a search pattern, for future use by match_pattern. 384 */ 385 static int 386compile_pattern2(pattern, search_type) 387 char *pattern; 388 int search_type; 389{ 390 if ((search_type & SRCH_NO_REGEX) == 0) 391 { 392#if HAVE_POSIX_REGCOMP 393 regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t)); 394 if (regcomp(s, pattern, REGCOMP_FLAG)) 395 { 396 free(s); 397 error("Invalid pattern", NULL_PARG); 398 return (-1); 399 } 400 if (regpattern != NULL) 401 regfree(regpattern); 402 regpattern = s; 403#endif 404#if HAVE_PCRE 405 pcre *comp; 406 const char *errstring; 407 int erroffset; 408 PARG parg; 409 comp = pcre_compile(pattern, 0, 410 &errstring, &erroffset, NULL); 411 if (comp == NULL) 412 { 413 parg.p_string = (char *) errstring; 414 error("%s", &parg); 415 return (-1); 416 } 417 regpattern = comp; 418#endif 419#if HAVE_RE_COMP 420 PARG parg; 421 if ((parg.p_string = re_comp(pattern)) != NULL) 422 { 423 error("%s", &parg); 424 return (-1); 425 } 426 re_pattern = 1; 427#endif 428#if HAVE_REGCMP 429 char *s; 430 if ((s = regcmp(pattern, 0)) == NULL) 431 { 432 error("Invalid pattern", NULL_PARG); 433 return (-1); 434 } 435 if (cpattern != NULL) 436 free(cpattern); 437 cpattern = s; 438#endif 439#if HAVE_V8_REGCOMP 440 struct regexp *s; 441 if ((s = regcomp(pattern)) == NULL) 442 { 443 /* 444 * regcomp has already printed an error message 445 * via regerror(). 446 */ 447 return (-1); 448 } 449 if (regpattern != NULL) 450 free(regpattern); 451 regpattern = s; 452#endif 453 } 454 455 if (last_pattern != NULL) 456 free(last_pattern); 457 last_pattern = (char *) calloc(1, strlen(pattern)+1); 458 if (last_pattern != NULL) 459 strcpy(last_pattern, pattern); 460 461 last_search_type = search_type; 462 return (0); 463} 464 465/* 466 * Like compile_pattern, but convert the pattern to lowercase if necessary. 467 */ 468 static int 469compile_pattern(pattern, search_type) 470 char *pattern; 471 int search_type; 472{ 473 char *cvt_pattern; 474 int result; 475 476 if (caseless != OPT_ONPLUS) 477 cvt_pattern = pattern; 478 else 479 { 480 cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC)); 481 cvt_text(cvt_pattern, pattern, (int *)NULL, CVT_TO_LC); 482 } 483 result = compile_pattern2(cvt_pattern, search_type); 484 if (cvt_pattern != pattern) 485 free(cvt_pattern); 486 return (result); 487} 488 489/* 490 * Forget that we have a compiled pattern. 491 */ 492 static void 493uncompile_pattern() 494{ 495#if HAVE_POSIX_REGCOMP 496 if (regpattern != NULL) 497 regfree(regpattern); 498 regpattern = NULL; 499#endif 500#if HAVE_PCRE 501 if (regpattern != NULL) 502 pcre_free(regpattern); 503 regpattern = NULL; 504#endif 505#if HAVE_RE_COMP 506 re_pattern = 0; 507#endif 508#if HAVE_REGCMP 509 if (cpattern != NULL) 510 free(cpattern); 511 cpattern = NULL; 512#endif 513#if HAVE_V8_REGCOMP 514 if (regpattern != NULL) 515 free(regpattern); 516 regpattern = NULL; 517#endif 518 last_pattern = NULL; 519} 520 521/* 522 * Perform a pattern match with the previously compiled pattern. 523 * Set sp and ep to the start and end of the matched string. 524 */ 525 static int 526match_pattern(line, line_len, sp, ep, notbol) 527 char *line; 528 int line_len; 529 char **sp; 530 char **ep; 531 int notbol; 532{ 533 int matched; 534 535 if (last_search_type & SRCH_NO_REGEX) 536 return (match(last_pattern, strlen(last_pattern), line, line_len, sp, ep)); 537 538#if HAVE_POSIX_REGCOMP 539 { 540 regmatch_t rm; 541 int flags = (notbol) ? REG_NOTBOL : 0; 542 matched = !regexec(regpattern, line, 1, &rm, flags); 543 if (!matched) 544 return (0); 545#ifndef __WATCOMC__ 546 *sp = line + rm.rm_so; 547 *ep = line + rm.rm_eo; 548#else 549 *sp = rm.rm_sp; 550 *ep = rm.rm_ep; 551#endif 552 } 553#endif 554#if HAVE_PCRE 555 { 556 int flags = (notbol) ? PCRE_NOTBOL : 0; 557 int ovector[3]; 558 matched = pcre_exec(regpattern, NULL, line, line_len, 559 0, flags, ovector, 3) >= 0; 560 if (!matched) 561 return (0); 562 *sp = line + ovector[0]; 563 *ep = line + ovector[1]; 564 } 565#endif 566#if HAVE_RE_COMP 567 matched = (re_exec(line) == 1); 568 /* 569 * re_exec doesn't seem to provide a way to get the matched string. 570 */ 571 *sp = *ep = NULL; 572#endif 573#if HAVE_REGCMP 574 *ep = regex(cpattern, line); 575 matched = (*ep != NULL); 576 if (!matched) 577 return (0); 578 *sp = __loc1; 579#endif 580#if HAVE_V8_REGCOMP 581#if HAVE_REGEXEC2 582 matched = regexec2(regpattern, line, notbol); 583#else 584 matched = regexec(regpattern, line); 585#endif 586 if (!matched) 587 return (0); 588 *sp = regpattern->startp[0]; 589 *ep = regpattern->endp[0]; 590#endif 591#if NO_REGEX 592 matched = match(last_pattern, strlen(last_pattern), line, line_len, sp, ep); 593#endif 594 return (matched); 595} 596 597#if HILITE_SEARCH 598/* 599 * Clear the hilite list. 600 */ 601 public void 602clr_hilite() 603{ 604 struct hilite *hl; 605 struct hilite *nexthl; 606 607 for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl) 608 { 609 nexthl = hl->hl_next; 610 free((void*)hl); 611 } 612 hilite_anchor.hl_first = NULL; 613 prep_startpos = prep_endpos = NULL_POSITION; 614} 615 616/* 617 * Should any characters in a specified range be highlighted? 618 */ 619 static int 620is_hilited_range(pos, epos) 621 POSITION pos; 622 POSITION epos; 623{ 624 struct hilite *hl; 625 626 /* 627 * Look at each highlight and see if any part of it falls in the range. 628 */ 629 for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) 630 { 631 if (hl->hl_endpos > pos && 632 (epos == NULL_POSITION || epos > hl->hl_startpos)) 633 return (1); 634 } 635 return (0); 636} 637 638/* 639 * Should any characters in a specified range be highlighted? 640 * If nohide is nonzero, don't consider hide_hilite. 641 */ 642 public int 643is_hilited(pos, epos, nohide, p_matches) 644 POSITION pos; 645 POSITION epos; 646 int nohide; 647 int *p_matches; 648{ 649 int match; 650 651 if (p_matches != NULL) 652 *p_matches = 0; 653 654 if (!status_col && 655 start_attnpos != NULL_POSITION && 656 pos < end_attnpos && 657 (epos == NULL_POSITION || epos > start_attnpos)) 658 /* 659 * The attn line overlaps this range. 660 */ 661 return (1); 662 663 match = is_hilited_range(pos, epos); 664 if (!match) 665 return (0); 666 667 if (p_matches != NULL) 668 /* 669 * Report matches, even if we're hiding highlights. 670 */ 671 *p_matches = 1; 672 673 if (hilite_search == 0) 674 /* 675 * Not doing highlighting. 676 */ 677 return (0); 678 679 if (!nohide && hide_hilite) 680 /* 681 * Highlighting is hidden. 682 */ 683 return (0); 684 685 return (1); 686} 687 688/* 689 * Add a new hilite to a hilite list. 690 */ 691 static void 692add_hilite(anchor, hl) 693 struct hilite *anchor; 694 struct hilite *hl; 695{ 696 struct hilite *ihl; 697 698 /* 699 * Hilites are sorted in the list; find where new one belongs. 700 * Insert new one after ihl. 701 */ 702 for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) 703 { 704 if (ihl->hl_next->hl_startpos > hl->hl_startpos) 705 break; 706 } 707 708 /* 709 * Truncate hilite so it doesn't overlap any existing ones 710 * above and below it. 711 */ 712 if (ihl != anchor) 713 hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); 714 if (ihl->hl_next != NULL) 715 hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos); 716 if (hl->hl_startpos >= hl->hl_endpos) 717 { 718 /* 719 * Hilite was truncated out of existence. 720 */ 721 free(hl); 722 return; 723 } 724 hl->hl_next = ihl->hl_next; 725 ihl->hl_next = hl; 726} 727 728/* 729 * Adjust hl_startpos & hl_endpos to account for processing by cvt_text. 730 */ 731 static void 732adj_hilite(anchor, linepos, cvt_ops) 733 struct hilite *anchor; 734 POSITION linepos; 735 int cvt_ops; 736{ 737 char *line; 738 char *oline; 739 int line_len; 740 char *line_end; 741 struct hilite *hl; 742 int checkstart; 743 POSITION opos; 744 POSITION npos; 745 LWCHAR ch; 746 int ncwidth; 747 748 /* 749 * The line was already scanned and hilites were added (in hilite_line). 750 * But it was assumed that each char position in the line 751 * correponds to one char position in the file. 752 * This may not be true if cvt_text modified the line. 753 * Get the raw line again. Look at each character. 754 */ 755 (void) forw_raw_line(linepos, &line, &line_len); 756 line_end = line + line_len; 757 opos = npos = linepos; 758 hl = anchor->hl_first; 759 checkstart = TRUE; 760 while (hl != NULL) 761 { 762 /* 763 * See if we need to adjust the current hl_startpos or 764 * hl_endpos. After adjusting startpos[i], move to endpos[i]. 765 * After adjusting endpos[i], move to startpos[i+1]. 766 * The hilite list must be sorted thus: 767 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc. 768 */ 769 if (checkstart && hl->hl_startpos == opos) 770 { 771 hl->hl_startpos = npos; 772 checkstart = FALSE; 773 continue; /* {{ not really necessary }} */ 774 } else if (!checkstart && hl->hl_endpos == opos) 775 { 776 hl->hl_endpos = npos; 777 checkstart = TRUE; 778 hl = hl->hl_next; 779 continue; /* {{ necessary }} */ 780 } 781 if (line == line_end) 782 break; 783 784 /* Get the next char from the line. */ 785 oline = line; 786 ch = step_char(&line, +1, line_end); 787 ncwidth = line - oline; 788 npos += ncwidth; 789 790 /* Figure out how this char was processed by cvt_text. */ 791 if ((cvt_ops & CVT_BS) && ch == '\b') 792 { 793 /* Skip the backspace and the following char. */ 794 oline = line; 795 ch = step_char(&line, +1, line_end); 796 ncwidth = line - oline; 797 npos += ncwidth; 798 } else if ((cvt_ops & CVT_TO_LC) && IS_UPPER(ch)) 799 { 800 /* Converted uppercase to lower. 801 * Note that this may have changed the number of bytes 802 * that the character occupies. */ 803 char dbuf[6]; 804 char *dst = dbuf; 805 put_wchar(&dst, TO_LOWER(ch)); 806 opos += dst - dbuf; 807 } else if ((cvt_ops & CVT_ANSI) && IS_CSI_START(ch)) 808 { 809 /* Skip to end of ANSI escape sequence. */ 810 line++; /* skip the CSI start char */ 811 npos++; 812 while (line < line_end) 813 { 814 npos++; 815 if (!is_ansi_middle(*line++)) 816 break; 817 } 818 } else 819 { 820 /* Ordinary unprocessed character. */ 821 opos += ncwidth; 822 } 823 } 824} 825 826/* 827 * Make a hilite for each string in a physical line which matches 828 * the current pattern. 829 * sp,ep delimit the first match already found. 830 */ 831 static void 832hilite_line(linepos, line, line_len, sp, ep, cvt_ops) 833 POSITION linepos; 834 char *line; 835 int line_len; 836 char *sp; 837 char *ep; 838 int cvt_ops; 839{ 840 char *searchp; 841 char *line_end = line + line_len; 842 struct hilite *hl; 843 struct hilite hilites; 844 845 if (sp == NULL || ep == NULL) 846 return; 847 /* 848 * sp and ep delimit the first match in the line. 849 * Mark the corresponding file positions, then 850 * look for further matches and mark them. 851 * {{ This technique, of calling match_pattern on subsequent 852 * substrings of the line, may mark more than is correct 853 * if the pattern starts with "^". This bug is fixed 854 * for those regex functions that accept a notbol parameter 855 * (currently POSIX, PCRE and V8-with-regexec2). }} 856 */ 857 searchp = line; 858 /* 859 * Put the hilites into a temporary list until they're adjusted. 860 */ 861 hilites.hl_first = NULL; 862 do { 863 if (ep > sp) 864 { 865 /* 866 * Assume that each char position in the "line" 867 * buffer corresponds to one char position in the file. 868 * This is not quite true; we need to adjust later. 869 */ 870 hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 871 hl->hl_startpos = linepos + (sp-line); 872 hl->hl_endpos = linepos + (ep-line); 873 add_hilite(&hilites, hl); 874 } 875 /* 876 * If we matched more than zero characters, 877 * move to the first char after the string we matched. 878 * If we matched zero, just move to the next char. 879 */ 880 if (ep > searchp) 881 searchp = ep; 882 else if (searchp != line_end) 883 searchp++; 884 else /* end of line */ 885 break; 886 } while (match_pattern(searchp, line_end - searchp, &sp, &ep, 1)); 887 888 /* 889 * If there were backspaces in the original line, they 890 * were removed, and hl_startpos/hl_endpos are not correct. 891 * {{ This is very ugly. }} 892 */ 893 adj_hilite(&hilites, linepos, cvt_ops); 894 895 /* 896 * Now put the hilites into the real list. 897 */ 898 while ((hl = hilites.hl_next) != NULL) 899 { 900 hilites.hl_next = hl->hl_next; 901 add_hilite(&hilite_anchor, hl); 902 } 903} 904#endif 905 906/* 907 * Change the caseless-ness of searches. 908 * Updates the internal search state to reflect a change in the -i flag. 909 */ 910 public void 911chg_caseless() 912{ 913 if (!is_ucase_pattern) 914 /* 915 * Pattern did not have uppercase. 916 * Just set the search caselessness to the global caselessness. 917 */ 918 is_caseless = caseless; 919 else 920 /* 921 * Pattern did have uppercase. 922 * Discard the pattern; we can't change search caselessness now. 923 */ 924 uncompile_pattern(); 925} 926 927#if HILITE_SEARCH 928/* 929 * Find matching text which is currently on screen and highlight it. 930 */ 931 static void 932hilite_screen() 933{ 934 struct scrpos scrpos; 935 936 get_scrpos(&scrpos); 937 if (scrpos.pos == NULL_POSITION) 938 return; 939 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 940 repaint_hilite(1); 941} 942 943/* 944 * Change highlighting parameters. 945 */ 946 public void 947chg_hilite() 948{ 949 /* 950 * Erase any highlights currently on screen. 951 */ 952 clr_hilite(); 953 hide_hilite = 0; 954 955 if (hilite_search == OPT_ONPLUS) 956 /* 957 * Display highlights. 958 */ 959 hilite_screen(); 960} 961#endif 962 963/* 964 * Figure out where to start a search. 965 */ 966 static POSITION 967search_pos(search_type) 968 int search_type; 969{ 970 POSITION pos; 971 int linenum; 972 973 if (empty_screen()) 974 { 975 /* 976 * Start at the beginning (or end) of the file. 977 * The empty_screen() case is mainly for 978 * command line initiated searches; 979 * for example, "+/xyz" on the command line. 980 * Also for multi-file (SRCH_PAST_EOF) searches. 981 */ 982 if (search_type & SRCH_FORW) 983 { 984 return (ch_zero()); 985 } else 986 { 987 pos = ch_length(); 988 if (pos == NULL_POSITION) 989 { 990 (void) ch_end_seek(); 991 pos = ch_length(); 992 } 993 return (pos); 994 } 995 } 996 if (how_search) 997 { 998 /* 999 * Search does not include current screen. 1000 */ 1001 if (search_type & SRCH_FORW) 1002 linenum = BOTTOM_PLUS_ONE; 1003 else 1004 linenum = TOP; 1005 pos = position(linenum); 1006 } else 1007 { 1008 /* 1009 * Search includes current screen. 1010 * It starts at the jump target (if searching backwards), 1011 * or at the jump target plus one (if forwards). 1012 */ 1013 linenum = adjsline(jump_sline); 1014 pos = position(linenum); 1015 if (search_type & SRCH_FORW) 1016 { 1017 pos = forw_raw_line(pos, (char **)NULL, (int *)NULL); 1018 while (pos == NULL_POSITION) 1019 { 1020 if (++linenum >= sc_height) 1021 break; 1022 pos = position(linenum); 1023 } 1024 } else 1025 { 1026 while (pos == NULL_POSITION) 1027 { 1028 if (--linenum < 0) 1029 break; 1030 pos = position(linenum); 1031 } 1032 } 1033 } 1034 return (pos); 1035} 1036 1037/* 1038 * Search a subset of the file, specified by start/end position. 1039 */ 1040 static int 1041search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) 1042 POSITION pos; 1043 POSITION endpos; 1044 int search_type; 1045 int matches; 1046 int maxlines; 1047 POSITION *plinepos; 1048 POSITION *pendpos; 1049{ 1050 char *line; 1051 char *cline; 1052 int line_len; 1053 LINENUM linenum; 1054 char *sp, *ep; 1055 int line_match; 1056 int cvt_ops; 1057 POSITION linepos, oldpos; 1058 1059 linenum = find_linenum(pos); 1060 oldpos = pos; 1061 for (;;) 1062 { 1063 /* 1064 * Get lines until we find a matching one or until 1065 * we hit end-of-file (or beginning-of-file if we're 1066 * going backwards), or until we hit the end position. 1067 */ 1068 if (ABORT_SIGS()) 1069 { 1070 /* 1071 * A signal aborts the search. 1072 */ 1073 return (-1); 1074 } 1075 1076 if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0) 1077 { 1078 /* 1079 * Reached end position without a match. 1080 */ 1081 if (pendpos != NULL) 1082 *pendpos = pos; 1083 return (matches); 1084 } 1085 if (maxlines > 0) 1086 maxlines--; 1087 1088 if (search_type & SRCH_FORW) 1089 { 1090 /* 1091 * Read the next line, and save the 1092 * starting position of that line in linepos. 1093 */ 1094 linepos = pos; 1095 pos = forw_raw_line(pos, &line, &line_len); 1096 if (linenum != 0) 1097 linenum++; 1098 } else 1099 { 1100 /* 1101 * Read the previous line and save the 1102 * starting position of that line in linepos. 1103 */ 1104 pos = back_raw_line(pos, &line, &line_len); 1105 linepos = pos; 1106 if (linenum != 0) 1107 linenum--; 1108 } 1109 1110 if (pos == NULL_POSITION) 1111 { 1112 /* 1113 * Reached EOF/BOF without a match. 1114 */ 1115 if (pendpos != NULL) 1116 *pendpos = oldpos; 1117 return (matches); 1118 } 1119 1120 /* 1121 * If we're using line numbers, we might as well 1122 * remember the information we have now (the position 1123 * and line number of the current line). 1124 * Don't do it for every line because it slows down 1125 * the search. Remember the line number only if 1126 * we're "far" from the last place we remembered it. 1127 */ 1128 if (linenums && abs((int)(pos - oldpos)) > 1024) 1129 add_lnum(linenum, pos); 1130 oldpos = pos; 1131 1132 /* 1133 * If it's a caseless search, convert the line to lowercase. 1134 * If we're doing backspace processing, delete backspaces. 1135 */ 1136 cvt_ops = get_cvt_ops(); 1137 cline = calloc(1, cvt_length(line_len, cvt_ops)); 1138 cvt_text(cline, line, &line_len, cvt_ops); 1139 1140 /* 1141 * Test the next line to see if we have a match. 1142 * We are successful if we either want a match and got one, 1143 * or if we want a non-match and got one. 1144 */ 1145 line_match = match_pattern(cline, line_len, &sp, &ep, 0); 1146 line_match = (!(search_type & SRCH_NO_MATCH) && line_match) || 1147 ((search_type & SRCH_NO_MATCH) && !line_match); 1148 if (!line_match) 1149 { 1150 free(cline); 1151 continue; 1152 } 1153 /* 1154 * Got a match. 1155 */ 1156 if (search_type & SRCH_FIND_ALL) 1157 { 1158#if HILITE_SEARCH 1159 /* 1160 * We are supposed to find all matches in the range. 1161 * Just add the matches in this line to the 1162 * hilite list and keep searching. 1163 */ 1164 if (line_match) 1165 hilite_line(linepos, cline, line_len, sp, ep, cvt_ops); 1166#endif 1167 free(cline); 1168 } else if (--matches <= 0) 1169 { 1170 /* 1171 * Found the one match we're looking for. 1172 * Return it. 1173 */ 1174#if HILITE_SEARCH 1175 if (hilite_search == OPT_ON) 1176 { 1177 /* 1178 * Clear the hilite list and add only 1179 * the matches in this one line. 1180 */ 1181 clr_hilite(); 1182 if (line_match) 1183 hilite_line(linepos, cline, line_len, sp, ep, cvt_ops); 1184 } 1185#endif 1186 free(cline); 1187 if (plinepos != NULL) 1188 *plinepos = linepos; 1189 return (0); 1190 } 1191 } 1192} 1193 1194 /* 1195 * search for a pattern in history. If found, compile that pattern. 1196 */ 1197 static int 1198hist_pattern(search_type) 1199 int search_type; 1200{ 1201#if CMD_HISTORY 1202 char *pattern; 1203 1204 set_mlist(ml_search, 0); 1205 pattern = cmd_lastpattern(); 1206 if (pattern == NULL) 1207 return (0); 1208 1209 if (compile_pattern(pattern, search_type) < 0) 1210 return (0); 1211 1212 is_ucase_pattern = is_ucase(pattern); 1213 if (is_ucase_pattern && caseless != OPT_ONPLUS) 1214 is_caseless = 0; 1215 else 1216 is_caseless = caseless; 1217 1218#if HILITE_SEARCH 1219 if (hilite_search == OPT_ONPLUS && !hide_hilite) 1220 hilite_screen(); 1221#endif 1222 1223 return (1); 1224#else /* CMD_HISTORY */ 1225 return (0); 1226#endif /* CMD_HISTORY */ 1227} 1228 1229/* 1230 * Search for the n-th occurrence of a specified pattern, 1231 * either forward or backward. 1232 * Return the number of matches not yet found in this file 1233 * (that is, n minus the number of matches found). 1234 * Return -1 if the search should be aborted. 1235 * Caller may continue the search in another file 1236 * if less than n matches are found in this file. 1237 */ 1238 public int 1239search(search_type, pattern, n) 1240 int search_type; 1241 char *pattern; 1242 int n; 1243{ 1244 POSITION pos; 1245 int result; 1246 1247 if (pattern == NULL || *pattern == '\0') 1248 { 1249 /* 1250 * A null pattern means use the previously compiled pattern. 1251 */ 1252 if (!prev_pattern() && !hist_pattern(search_type)) 1253 { 1254 error("No previous regular expression", NULL_PARG); 1255 return (-1); 1256 } 1257 if ((search_type & SRCH_NO_REGEX) != 1258 (last_search_type & SRCH_NO_REGEX)) 1259 { 1260 error("Please re-enter search pattern", NULL_PARG); 1261 return -1; 1262 } 1263#if HILITE_SEARCH 1264 if (hilite_search == OPT_ON) 1265 { 1266 /* 1267 * Erase the highlights currently on screen. 1268 * If the search fails, we'll redisplay them later. 1269 */ 1270 repaint_hilite(0); 1271 } 1272 if (hilite_search == OPT_ONPLUS && hide_hilite) 1273 { 1274 /* 1275 * Highlight any matches currently on screen, 1276 * before we actually start the search. 1277 */ 1278 hide_hilite = 0; 1279 hilite_screen(); 1280 } 1281 hide_hilite = 0; 1282#endif 1283 } else 1284 { 1285 /* 1286 * Compile the pattern. 1287 */ 1288 if (compile_pattern(pattern, search_type) < 0) 1289 return (-1); 1290 /* 1291 * Ignore case if -I is set OR 1292 * -i is set AND the pattern is all lowercase. 1293 */ 1294 is_ucase_pattern = is_ucase(pattern); 1295 if (is_ucase_pattern && caseless != OPT_ONPLUS) 1296 is_caseless = 0; 1297 else 1298 is_caseless = caseless; 1299#if HILITE_SEARCH 1300 if (hilite_search) 1301 { 1302 /* 1303 * Erase the highlights currently on screen. 1304 * Also permanently delete them from the hilite list. 1305 */ 1306 repaint_hilite(0); 1307 hide_hilite = 0; 1308 clr_hilite(); 1309 } 1310 if (hilite_search == OPT_ONPLUS) 1311 { 1312 /* 1313 * Highlight any matches currently on screen, 1314 * before we actually start the search. 1315 */ 1316 hilite_screen(); 1317 } 1318#endif 1319 } 1320 1321 /* 1322 * Figure out where to start the search. 1323 */ 1324 pos = search_pos(search_type); 1325 if (pos == NULL_POSITION) 1326 { 1327 /* 1328 * Can't find anyplace to start searching from. 1329 */ 1330 if (search_type & SRCH_PAST_EOF) 1331 return (n); 1332 /* repaint(); -- why was this here? */ 1333 error("Nothing to search", NULL_PARG); 1334 return (-1); 1335 } 1336 1337 n = search_range(pos, NULL_POSITION, search_type, n, -1, 1338 &pos, (POSITION*)NULL); 1339 if (n != 0) 1340 { 1341 /* 1342 * Search was unsuccessful. 1343 */ 1344#if HILITE_SEARCH 1345 if (hilite_search == OPT_ON && n > 0) 1346 /* 1347 * Redisplay old hilites. 1348 */ 1349 repaint_hilite(1); 1350#endif 1351 return (n); 1352 } 1353 1354 if (!(search_type & SRCH_NO_MOVE)) 1355 { 1356 /* 1357 * Go to the matching line. 1358 */ 1359 jump_loc(pos, jump_sline); 1360 } 1361 1362#if HILITE_SEARCH 1363 if (hilite_search == OPT_ON) 1364 /* 1365 * Display new hilites in the matching line. 1366 */ 1367 repaint_hilite(1); 1368#endif 1369 return (0); 1370} 1371 1372 1373#if HILITE_SEARCH 1374/* 1375 * Prepare hilites in a given range of the file. 1376 * 1377 * The pair (prep_startpos,prep_endpos) delimits a contiguous region 1378 * of the file that has been "prepared"; that is, scanned for matches for 1379 * the current search pattern, and hilites have been created for such matches. 1380 * If prep_startpos == NULL_POSITION, the prep region is empty. 1381 * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 1382 * prep_hilite asks that the range (spos,epos) be covered by the prep region. 1383 */ 1384 public void 1385prep_hilite(spos, epos, maxlines) 1386 POSITION spos; 1387 POSITION epos; 1388 int maxlines; 1389{ 1390 POSITION nprep_startpos = prep_startpos; 1391 POSITION nprep_endpos = prep_endpos; 1392 POSITION new_epos; 1393 POSITION max_epos; 1394 int result; 1395 int i; 1396/* 1397 * Search beyond where we're asked to search, so the prep region covers 1398 * more than we need. Do one big search instead of a bunch of small ones. 1399 */ 1400#define SEARCH_MORE (3*size_linebuf) 1401 1402 if (!prev_pattern()) 1403 return; 1404 1405 /* 1406 * If we're limited to a max number of lines, figure out the 1407 * file position we should stop at. 1408 */ 1409 if (maxlines < 0) 1410 max_epos = NULL_POSITION; 1411 else 1412 { 1413 max_epos = spos; 1414 for (i = 0; i < maxlines; i++) 1415 max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL); 1416 } 1417 1418 /* 1419 * Find two ranges: 1420 * The range that we need to search (spos,epos); and the range that 1421 * the "prep" region will then cover (nprep_startpos,nprep_endpos). 1422 */ 1423 1424 if (prep_startpos == NULL_POSITION || 1425 (epos != NULL_POSITION && epos < prep_startpos) || 1426 spos > prep_endpos) 1427 { 1428 /* 1429 * New range is not contiguous with old prep region. 1430 * Discard the old prep region and start a new one. 1431 */ 1432 clr_hilite(); 1433 if (epos != NULL_POSITION) 1434 epos += SEARCH_MORE; 1435 nprep_startpos = spos; 1436 } else 1437 { 1438 /* 1439 * New range partially or completely overlaps old prep region. 1440 */ 1441 if (epos == NULL_POSITION) 1442 { 1443 /* 1444 * New range goes to end of file. 1445 */ 1446 ; 1447 } else if (epos > prep_endpos) 1448 { 1449 /* 1450 * New range ends after old prep region. 1451 * Extend prep region to end at end of new range. 1452 */ 1453 epos += SEARCH_MORE; 1454 } else /* (epos <= prep_endpos) */ 1455 { 1456 /* 1457 * New range ends within old prep region. 1458 * Truncate search to end at start of old prep region. 1459 */ 1460 epos = prep_startpos; 1461 } 1462 1463 if (spos < prep_startpos) 1464 { 1465 /* 1466 * New range starts before old prep region. 1467 * Extend old prep region backwards to start at 1468 * start of new range. 1469 */ 1470 if (spos < SEARCH_MORE) 1471 spos = 0; 1472 else 1473 spos -= SEARCH_MORE; 1474 nprep_startpos = spos; 1475 } else /* (spos >= prep_startpos) */ 1476 { 1477 /* 1478 * New range starts within or after old prep region. 1479 * Trim search to start at end of old prep region. 1480 */ 1481 spos = prep_endpos; 1482 } 1483 } 1484 1485 if (epos != NULL_POSITION && max_epos != NULL_POSITION && 1486 epos > max_epos) 1487 /* 1488 * Don't go past the max position we're allowed. 1489 */ 1490 epos = max_epos; 1491 1492 if (epos == NULL_POSITION || epos > spos) 1493 { 1494 result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0, 1495 maxlines, (POSITION*)NULL, &new_epos); 1496 if (result < 0) 1497 return; 1498 if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) 1499 nprep_endpos = new_epos; 1500 } 1501 prep_startpos = nprep_startpos; 1502 prep_endpos = nprep_endpos; 1503} 1504#endif 1505 1506/* 1507 * Simple pattern matching function. 1508 * It supports no metacharacters like *, etc. 1509 */ 1510 static int 1511match(pattern, pattern_len, buf, buf_len, pfound, pend) 1512 char *pattern; 1513 int pattern_len; 1514 char *buf; 1515 int buf_len; 1516 char **pfound, **pend; 1517{ 1518 register char *pp, *lp; 1519 register char *pattern_end = pattern + pattern_len; 1520 register char *buf_end = buf + buf_len; 1521 1522 for ( ; buf < buf_end; buf++) 1523 { 1524 for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) 1525 if (pp == pattern_end || lp == buf_end) 1526 break; 1527 if (pp == pattern_end) 1528 { 1529 if (pfound != NULL) 1530 *pfound = buf; 1531 if (pend != NULL) 1532 *pend = lp; 1533 return (1); 1534 } 1535 } 1536 return (0); 1537} 1538 1539#if HAVE_V8_REGCOMP 1540/* 1541 * This function is called by the V8 regcomp to report 1542 * errors in regular expressions. 1543 */ 1544 void 1545regerror(s) 1546 char *s; 1547{ 1548 PARG parg; 1549 1550 parg.p_string = s; 1551 error("%s", &parg); 1552} 1553#endif 1554 1555