1 /* $FreeBSD$ */ 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 35 char *re_comp(); 36 int re_exec(); 37 #endif 38 #if HAVE_REGCMP 39 char *regcmp(); 40 char *regex(); 41 extern char *__loc1; 42 #endif 43 #if HAVE_V8_REGCOMP 44 #include "regexp.h" 45 #endif 46 47 static int match(); 48 49 extern int sigs; 50 extern int how_search; 51 extern int caseless; 52 extern int linenums; 53 extern int sc_height; 54 extern int jump_sline; 55 extern int bs_mode; 56 extern int more_mode; 57 extern int ctldisp; 58 extern int status_col; 59 extern POSITION start_attnpos; 60 extern POSITION end_attnpos; 61 #if HILITE_SEARCH 62 extern int hilite_search; 63 extern int screen_trashed; 64 extern int size_linebuf; 65 extern int squished; 66 extern int can_goto_line; 67 static int hide_hilite; 68 static POSITION prep_startpos; 69 static POSITION prep_endpos; 70 71 struct hilite 72 { 73 struct hilite *hl_next; 74 POSITION hl_startpos; 75 POSITION hl_endpos; 76 }; 77 static 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 86 static regex_t *regpattern = NULL; 87 #endif 88 #if HAVE_PCRE 89 pcre *regpattern = NULL; 90 #endif 91 #if HAVE_RE_COMP 92 int re_pattern = 0; 93 #endif 94 #if HAVE_REGCMP 95 static char *cpattern = NULL; 96 #endif 97 #if HAVE_V8_REGCOMP 98 static struct regexp *regpattern = NULL; 99 #endif 100 101 static int is_caseless; 102 static int is_ucase_pattern; 103 static int last_search_type; 104 static 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 115 cvt_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 150 get_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 174 is_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 189 prev_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 220 repaint_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 270 clear_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 313 undo_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 330 compile_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 413 uncompile_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 446 match_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 521 clr_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 540 is_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 584 add_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 624 adj_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 712 hilite_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 789 chg_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 810 hilite_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 825 chg_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 845 search_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 919 search_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 1074 search(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 1223 prep_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 1349 match(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 1378 regerror(s) 1379 char *s; 1380 { 1381 PARG parg; 1382 1383 parg.p_string = s; 1384 error("%s", &parg); 1385 } 1386 #endif 1387 1388