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