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