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