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