search.c revision 63131
160812Sps/* $FreeBSD: head/contrib/less/search.c 63131 2000-07-14 09:57:37Z ps $ */ 260786Sps/* 360786Sps * Copyright (C) 1984-2000 Mark Nudelman 460786Sps * 560786Sps * You may distribute under the terms of either the GNU General Public 660786Sps * License or the Less License, as specified in the README file. 760786Sps * 860786Sps * For more information about less, or for information on how to 960786Sps * contact the author, see the README file. 1060786Sps */ 1160786Sps 1260786Sps 1360786Sps/* 1460786Sps * Routines to search a file for a pattern. 1560786Sps */ 1660786Sps 1760786Sps#include "less.h" 1860786Sps#include "position.h" 1960786Sps 2060786Sps#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 2160786Sps#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 2260786Sps 2360786Sps#if HAVE_POSIX_REGCOMP 2460786Sps#include <regex.h> 2560786Sps#ifdef REG_EXTENDED 2660812Sps#define REGCOMP_FLAG (more_mode ? 0 : REG_EXTENDED) 2760786Sps#else 2860786Sps#define REGCOMP_FLAG 0 2960786Sps#endif 3060786Sps#endif 3160786Sps#if HAVE_PCRE 3260786Sps#include <pcre.h> 3360786Sps#endif 3460786Sps#if HAVE_RE_COMP 3560786Spschar *re_comp(); 3660786Spsint re_exec(); 3760786Sps#endif 3860786Sps#if HAVE_REGCMP 3960786Spschar *regcmp(); 4060786Spschar *regex(); 4160786Spsextern char *__loc1; 4260786Sps#endif 4360786Sps#if HAVE_V8_REGCOMP 4460786Sps#include "regexp.h" 4560786Sps#endif 4660786Sps 4760786Spsstatic int match(); 4860786Sps 4960786Spsextern int sigs; 5060786Spsextern int how_search; 5160786Spsextern int caseless; 5260786Spsextern int linenums; 5360786Spsextern int sc_height; 5460786Spsextern int jump_sline; 5560786Spsextern int bs_mode; 5660812Spsextern int more_mode; 5763131Spsextern int status_col; 5860786Spsextern POSITION start_attnpos; 5960786Spsextern POSITION end_attnpos; 6060786Sps#if HILITE_SEARCH 6160786Spsextern int hilite_search; 6260786Spsextern int screen_trashed; 6360786Spsextern int size_linebuf; 6460786Spsextern int squished; 6560786Spsextern int can_goto_line; 6660786Spsstatic int hide_hilite; 6760786Spsstatic POSITION prep_startpos; 6860786Spsstatic POSITION prep_endpos; 6960786Sps 7060786Spsstruct hilite 7160786Sps{ 7260786Sps struct hilite *hl_next; 7360786Sps POSITION hl_startpos; 7460786Sps POSITION hl_endpos; 7560786Sps}; 7660786Spsstatic struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; 7760786Sps#define hl_first hl_next 7860786Sps#endif 7960786Sps 8060786Sps/* 8160786Sps * These are the static variables that represent the "remembered" 8260786Sps * search pattern. 8360786Sps */ 8460786Sps#if HAVE_POSIX_REGCOMP 8560786Spsstatic regex_t *regpattern = NULL; 8660786Sps#endif 8760786Sps#if HAVE_PCRE 8860786Spspcre *regpattern = NULL; 8960786Sps#endif 9060786Sps#if HAVE_RE_COMP 9160786Spsint re_pattern = 0; 9260786Sps#endif 9360786Sps#if HAVE_REGCMP 9460786Spsstatic char *cpattern = NULL; 9560786Sps#endif 9660786Sps#if HAVE_V8_REGCOMP 9760786Spsstatic struct regexp *regpattern = NULL; 9860786Sps#endif 9960786Sps 10060786Spsstatic int is_caseless; 10160786Spsstatic int is_ucase_pattern; 10260786Spsstatic int last_search_type; 10360786Spsstatic char *last_pattern = NULL; 10460786Sps 10560786Sps/* 10660786Sps * Convert text. Perform one or more of these transformations: 10760786Sps */ 10860786Sps#define CVT_TO_LC 01 /* Convert upper-case to lower-case */ 10960786Sps#define CVT_BS 02 /* Do backspace processing */ 11060786Sps#define CVT_CRLF 04 /* Remove CR after LF */ 11160786Sps 11260786Sps static void 11360786Spscvt_text(odst, osrc, ops) 11460786Sps char *odst; 11560786Sps char *osrc; 11660786Sps int ops; 11760786Sps{ 11860786Sps register char *dst; 11960786Sps register char *src; 12060786Sps 12160786Sps for (src = osrc, dst = odst; *src != '\0'; src++, dst++) 12260786Sps { 12360786Sps if ((ops & CVT_TO_LC) && isupper((unsigned char) *src)) 12460786Sps /* Convert uppercase to lowercase. */ 12560786Sps *dst = tolower((unsigned char) *src); 12660786Sps else if ((ops & CVT_BS) && *src == '\b' && dst > odst) 12760786Sps /* Delete BS and preceding char. */ 12860786Sps dst -= 2; 12960786Sps else 13060786Sps /* Just copy. */ 13160786Sps *dst = *src; 13260786Sps } 13360786Sps if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r') 13460786Sps dst--; 13560786Sps *dst = '\0'; 13660786Sps} 13760786Sps 13860786Sps/* 13960786Sps * Are there any uppercase letters in this string? 14060786Sps */ 14160786Sps static int 14260786Spsis_ucase(s) 14360786Sps char *s; 14460786Sps{ 14560786Sps register char *p; 14660786Sps 14760786Sps for (p = s; *p != '\0'; p++) 14860786Sps if (isupper((unsigned char) *p)) 14960786Sps return (1); 15060786Sps return (0); 15160786Sps} 15260786Sps 15360786Sps/* 15460786Sps * Is there a previous (remembered) search pattern? 15560786Sps */ 15660786Sps static int 15760786Spsprev_pattern() 15860786Sps{ 15960786Sps if (last_search_type & SRCH_NO_REGEX) 16060786Sps return (last_pattern != NULL); 16160786Sps#if HAVE_POSIX_REGCOMP 16260786Sps return (regpattern != NULL); 16360786Sps#endif 16460786Sps#if HAVE_PCRE 16560786Sps return (regpattern != NULL); 16660786Sps#endif 16760786Sps#if HAVE_RE_COMP 16860786Sps return (re_pattern != 0); 16960786Sps#endif 17060786Sps#if HAVE_REGCMP 17160786Sps return (cpattern != NULL); 17260786Sps#endif 17360786Sps#if HAVE_V8_REGCOMP 17460786Sps return (regpattern != NULL); 17560786Sps#endif 17660786Sps#if NO_REGEX 17760786Sps return (last_pattern != NULL); 17860786Sps#endif 17960786Sps} 18060786Sps 18160786Sps#if HILITE_SEARCH 18260786Sps/* 18360786Sps * Repaint the hilites currently displayed on the screen. 18460786Sps * Repaint each line which contains highlighted text. 18560786Sps * If on==0, force all hilites off. 18660786Sps */ 18760786Sps public void 18860786Spsrepaint_hilite(on) 18960786Sps int on; 19060786Sps{ 19160786Sps int slinenum; 19260786Sps POSITION pos; 19360786Sps POSITION epos; 19460786Sps int save_hide_hilite; 19560786Sps 19660786Sps if (squished) 19760786Sps repaint(); 19860786Sps 19960786Sps save_hide_hilite = hide_hilite; 20060786Sps if (!on) 20160786Sps { 20260786Sps if (hide_hilite) 20360786Sps return; 20460786Sps hide_hilite = 1; 20560786Sps } 20660786Sps 20760786Sps if (!can_goto_line) 20860786Sps { 20960786Sps repaint(); 21060786Sps hide_hilite = save_hide_hilite; 21160786Sps return; 21260786Sps } 21360786Sps 21460786Sps for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 21560786Sps { 21660786Sps pos = position(slinenum); 21760786Sps if (pos == NULL_POSITION) 21860786Sps continue; 21960786Sps epos = position(slinenum+1); 22060786Sps /* 22160786Sps * If any character in the line is highlighted, 22260786Sps * repaint the line. 22360786Sps */ 22460786Sps if (is_hilited(pos, epos, 1)) 22560786Sps { 22660786Sps (void) forw_line(pos); 22760786Sps goto_line(slinenum); 22860786Sps put_line(); 22960786Sps } 23060786Sps } 23160786Sps hide_hilite = save_hide_hilite; 23260786Sps} 23360786Sps 23460786Sps/* 23560786Sps * Clear the attn hilite. 23660786Sps */ 23760786Sps public void 23860786Spsclear_attn() 23960786Sps{ 24060786Sps int slinenum; 24160786Sps POSITION old_start_attnpos; 24260786Sps POSITION old_end_attnpos; 24360786Sps POSITION pos; 24460786Sps POSITION epos; 24560786Sps 24660786Sps if (start_attnpos == NULL_POSITION) 24760786Sps return; 24860786Sps old_start_attnpos = start_attnpos; 24960786Sps old_end_attnpos = end_attnpos; 25060786Sps start_attnpos = end_attnpos = NULL_POSITION; 25160786Sps 25260786Sps if (!can_goto_line) 25360786Sps { 25460786Sps repaint(); 25560786Sps return; 25660786Sps } 25760786Sps if (squished) 25860786Sps repaint(); 25960786Sps 26060786Sps for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 26160786Sps { 26260786Sps pos = position(slinenum); 26360786Sps if (pos == NULL_POSITION) 26460786Sps continue; 26560786Sps epos = position(slinenum+1); 26660786Sps if (pos < old_end_attnpos && 26760786Sps (epos == NULL_POSITION || epos > old_start_attnpos)) 26860786Sps { 26960786Sps (void) forw_line(pos); 27060786Sps goto_line(slinenum); 27160786Sps put_line(); 27260786Sps } 27360786Sps } 27460786Sps} 27560786Sps#endif 27660786Sps 27760786Sps/* 27860786Sps * Hide search string highlighting. 27960786Sps */ 28060786Sps public void 28160786Spsundo_search() 28260786Sps{ 28360786Sps if (!prev_pattern()) 28460786Sps { 28560786Sps error("No previous regular expression", NULL_PARG); 28660786Sps return; 28760786Sps } 28860786Sps#if HILITE_SEARCH 28960786Sps hide_hilite = !hide_hilite; 29060786Sps repaint_hilite(1); 29160786Sps#endif 29260786Sps} 29360786Sps 29460786Sps/* 29560786Sps * Compile a search pattern, for future use by match_pattern. 29660786Sps */ 29760786Sps static int 29860786Spscompile_pattern(pattern, search_type) 29960786Sps char *pattern; 30060786Sps int search_type; 30160786Sps{ 30260786Sps if ((search_type & SRCH_NO_REGEX) == 0) 30360786Sps { 30460786Sps#if HAVE_POSIX_REGCOMP 30560786Sps regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t)); 30660786Sps if (regcomp(s, pattern, REGCOMP_FLAG)) 30760786Sps { 30860786Sps free(s); 30960786Sps error("Invalid pattern", NULL_PARG); 31060786Sps return (-1); 31160786Sps } 31260786Sps if (regpattern != NULL) 31360786Sps regfree(regpattern); 31460786Sps regpattern = s; 31560786Sps#endif 31660786Sps#if HAVE_PCRE 31760786Sps pcre *comp; 31860786Sps const char *errstring; 31960786Sps int erroffset; 32060786Sps PARG parg; 32160786Sps comp = pcre_compile(pattern, 0, 32260786Sps &errstring, &erroffset, NULL); 32360786Sps if (comp == NULL) 32460786Sps { 32560786Sps parg.p_string = (char *) errstring; 32660786Sps error("%s", &parg); 32760786Sps return (-1); 32860786Sps } 32960786Sps regpattern = comp; 33060786Sps#endif 33160786Sps#if HAVE_RE_COMP 33260786Sps PARG parg; 33360786Sps if ((parg.p_string = re_comp(pattern)) != NULL) 33460786Sps { 33560786Sps error("%s", &parg); 33660786Sps return (-1); 33760786Sps } 33860786Sps re_pattern = 1; 33960786Sps#endif 34060786Sps#if HAVE_REGCMP 34160786Sps char *s; 34260786Sps if ((s = regcmp(pattern, 0)) == NULL) 34360786Sps { 34460786Sps error("Invalid pattern", NULL_PARG); 34560786Sps return (-1); 34660786Sps } 34760786Sps if (cpattern != NULL) 34860786Sps free(cpattern); 34960786Sps cpattern = s; 35060786Sps#endif 35160786Sps#if HAVE_V8_REGCOMP 35260786Sps struct regexp *s; 35360786Sps if ((s = regcomp(pattern)) == NULL) 35460786Sps { 35560786Sps /* 35660786Sps * regcomp has already printed an error message 35760786Sps * via regerror(). 35860786Sps */ 35960786Sps return (-1); 36060786Sps } 36160786Sps if (regpattern != NULL) 36260786Sps free(regpattern); 36360786Sps regpattern = s; 36460786Sps#endif 36560786Sps } 36660786Sps 36760786Sps if (last_pattern != NULL) 36860786Sps free(last_pattern); 36960786Sps last_pattern = (char *) calloc(1, strlen(pattern)+1); 37060786Sps if (last_pattern != NULL) 37160786Sps strcpy(last_pattern, pattern); 37260786Sps 37360786Sps last_search_type = search_type; 37460786Sps return (0); 37560786Sps} 37660786Sps 37760786Sps/* 37860786Sps * Forget that we have a compiled pattern. 37960786Sps */ 38060786Sps static void 38160786Spsuncompile_pattern() 38260786Sps{ 38360786Sps#if HAVE_POSIX_REGCOMP 38460786Sps if (regpattern != NULL) 38560786Sps regfree(regpattern); 38660786Sps regpattern = NULL; 38760786Sps#endif 38860786Sps#if HAVE_PCRE 38960786Sps if (regpattern != NULL) 39060786Sps pcre_free(regpattern); 39160786Sps regpattern = NULL; 39260786Sps#endif 39360786Sps#if HAVE_RE_COMP 39460786Sps re_pattern = 0; 39560786Sps#endif 39660786Sps#if HAVE_REGCMP 39760786Sps if (cpattern != NULL) 39860786Sps free(cpattern); 39960786Sps cpattern = NULL; 40060786Sps#endif 40160786Sps#if HAVE_V8_REGCOMP 40260786Sps if (regpattern != NULL) 40360786Sps free(regpattern); 40460786Sps regpattern = NULL; 40560786Sps#endif 40660786Sps last_pattern = NULL; 40760786Sps} 40860786Sps 40960786Sps/* 41060786Sps * Perform a pattern match with the previously compiled pattern. 41160786Sps * Set sp and ep to the start and end of the matched string. 41260786Sps */ 41360786Sps static int 41460786Spsmatch_pattern(line, sp, ep, notbol) 41560786Sps char *line; 41660786Sps char **sp; 41760786Sps char **ep; 41860786Sps int notbol; 41960786Sps{ 42060786Sps int matched; 42160786Sps 42260786Sps if (last_search_type & SRCH_NO_REGEX) 42360786Sps return (match(last_pattern, line, sp, ep)); 42460786Sps 42560786Sps#if HAVE_POSIX_REGCOMP 42660786Sps { 42760786Sps regmatch_t rm; 42860786Sps int flags = (notbol) ? REG_NOTBOL : 0; 42960786Sps matched = !regexec(regpattern, line, 1, &rm, flags); 43060786Sps if (!matched) 43160786Sps return (0); 43260786Sps#ifndef __WATCOMC__ 43360786Sps *sp = line + rm.rm_so; 43460786Sps *ep = line + rm.rm_eo; 43560786Sps#else 43660786Sps *sp = rm.rm_sp; 43760786Sps *ep = rm.rm_ep; 43860786Sps#endif 43960786Sps } 44060786Sps#endif 44160786Sps#if HAVE_PCRE 44260786Sps { 44360786Sps int flags = (notbol) ? PCRE_NOTBOL : 0; 44460786Sps int ovector[3]; 44560786Sps matched = pcre_exec(regpattern, NULL, line, strlen(line), 44660786Sps 0, flags, ovector, 3) >= 0; 44760786Sps if (!matched) 44860786Sps return (0); 44960786Sps *sp = line + ovector[0]; 45060786Sps *ep = line + ovector[1]; 45160786Sps } 45260786Sps#endif 45360786Sps#if HAVE_RE_COMP 45460786Sps matched = (re_exec(line) == 1); 45560786Sps /* 45660786Sps * re_exec doesn't seem to provide a way to get the matched string. 45760786Sps */ 45860786Sps *sp = *ep = NULL; 45960786Sps#endif 46060786Sps#if HAVE_REGCMP 46160786Sps *ep = regex(cpattern, line); 46260786Sps matched = (*ep != NULL); 46360786Sps if (!matched) 46460786Sps return (0); 46560786Sps *sp = __loc1; 46660786Sps#endif 46760786Sps#if HAVE_V8_REGCOMP 46860786Sps#if HAVE_REGEXEC2 46960786Sps matched = regexec2(regpattern, line, notbol); 47060786Sps#else 47160786Sps matched = regexec(regpattern, line); 47260786Sps#endif 47360786Sps if (!matched) 47460786Sps return (0); 47560786Sps *sp = regpattern->startp[0]; 47660786Sps *ep = regpattern->endp[0]; 47760786Sps#endif 47860786Sps#if NO_REGEX 47960786Sps matched = match(last_pattern, line, sp, ep); 48060786Sps#endif 48160786Sps return (matched); 48260786Sps} 48360786Sps 48460786Sps#if HILITE_SEARCH 48560786Sps/* 48660786Sps * Clear the hilite list. 48760786Sps */ 48860786Sps public void 48960786Spsclr_hilite() 49060786Sps{ 49160786Sps struct hilite *hl; 49260786Sps struct hilite *nexthl; 49360786Sps 49460786Sps for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl) 49560786Sps { 49660786Sps nexthl = hl->hl_next; 49760786Sps free((void*)hl); 49860786Sps } 49960786Sps hilite_anchor.hl_first = NULL; 50060786Sps prep_startpos = prep_endpos = NULL_POSITION; 50160786Sps} 50260786Sps 50360786Sps/* 50460786Sps * Should any characters in a specified range be highlighted? 50560786Sps * If nohide is nonzero, don't consider hide_hilite. 50660786Sps */ 50760786Sps public int 50860786Spsis_hilited(pos, epos, nohide) 50960786Sps POSITION pos; 51060786Sps POSITION epos; 51160786Sps int nohide; 51260786Sps{ 51360786Sps struct hilite *hl; 51460786Sps 51563131Sps if (!status_col && 51663131Sps start_attnpos != NULL_POSITION && 51760786Sps pos < end_attnpos && 51860786Sps (epos == NULL_POSITION || epos > start_attnpos)) 51960786Sps /* 52060786Sps * The attn line overlaps this range. 52160786Sps */ 52260786Sps return (1); 52360786Sps 52460786Sps if (hilite_search == 0) 52560786Sps /* 52660786Sps * Not doing highlighting. 52760786Sps */ 52860786Sps return (0); 52960786Sps 53060786Sps if (!nohide && hide_hilite) 53160786Sps /* 53260786Sps * Highlighting is hidden. 53360786Sps */ 53460786Sps return (0); 53560786Sps 53660786Sps /* 53760786Sps * Look at each highlight and see if any part of it falls in the range. 53860786Sps */ 53960786Sps for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) 54060786Sps { 54160786Sps if (hl->hl_endpos > pos && 54260786Sps (epos == NULL_POSITION || epos > hl->hl_startpos)) 54360786Sps return (1); 54460786Sps } 54560786Sps return (0); 54660786Sps} 54760786Sps 54860786Sps/* 54960786Sps * Add a new hilite to a hilite list. 55060786Sps */ 55160786Sps static void 55260786Spsadd_hilite(anchor, hl) 55360786Sps struct hilite *anchor; 55460786Sps struct hilite *hl; 55560786Sps{ 55660786Sps struct hilite *ihl; 55760786Sps 55860786Sps /* 55960786Sps * Hilites are sorted in the list; find where new one belongs. 56060786Sps * Insert new one after ihl. 56160786Sps */ 56260786Sps for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) 56360786Sps { 56460786Sps if (ihl->hl_next->hl_startpos > hl->hl_startpos) 56560786Sps break; 56660786Sps } 56760786Sps 56860786Sps /* 56960786Sps * Truncate hilite so it doesn't overlap any existing ones 57060786Sps * above and below it. 57160786Sps */ 57260786Sps if (ihl != anchor) 57360786Sps hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); 57460786Sps if (ihl->hl_next != NULL) 57560786Sps hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos); 57660786Sps if (hl->hl_startpos >= hl->hl_endpos) 57760786Sps { 57860786Sps /* 57960786Sps * Hilite was truncated out of existence. 58060786Sps */ 58160786Sps free(hl); 58260786Sps return; 58360786Sps } 58460786Sps hl->hl_next = ihl->hl_next; 58560786Sps ihl->hl_next = hl; 58660786Sps} 58760786Sps 58860786Sps/* 58960786Sps * Adjust hl_startpos & hl_endpos to account for backspace processing. 59060786Sps */ 59160786Sps static void 59260786Spsadj_hilite(anchor, linepos) 59360786Sps struct hilite *anchor; 59460786Sps POSITION linepos; 59560786Sps{ 59660786Sps char *line; 59760786Sps struct hilite *hl; 59860786Sps int checkstart; 59960786Sps POSITION opos; 60060786Sps POSITION npos; 60160786Sps 60260786Sps /* 60360786Sps * The line was already scanned and hilites were added (in hilite_line). 60460786Sps * But it was assumed that each char position in the line 60560786Sps * correponds to one char position in the file. 60660786Sps * This may not be true if there are backspaces in the line. 60760786Sps * Get the raw line again. Look at each character. 60860786Sps */ 60960786Sps (void) forw_raw_line(linepos, &line); 61060786Sps opos = npos = linepos; 61160786Sps hl = anchor->hl_first; 61260786Sps checkstart = TRUE; 61360786Sps while (hl != NULL) 61460786Sps { 61560786Sps /* 61660786Sps * See if we need to adjust the current hl_startpos or 61760786Sps * hl_endpos. After adjusting startpos[i], move to endpos[i]. 61860786Sps * After adjusting endpos[i], move to startpos[i+1]. 61960786Sps * The hilite list must be sorted thus: 62060786Sps * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc. 62160786Sps */ 62260786Sps if (checkstart && hl->hl_startpos == opos) 62360786Sps { 62460786Sps hl->hl_startpos = npos; 62560786Sps checkstart = FALSE; 62660786Sps continue; /* {{ not really necessary }} */ 62760786Sps } else if (!checkstart && hl->hl_endpos == opos) 62860786Sps { 62960786Sps hl->hl_endpos = npos; 63060786Sps checkstart = TRUE; 63160786Sps hl = hl->hl_next; 63260786Sps continue; /* {{ necessary }} */ 63360786Sps } 63460786Sps if (*line == '\0') 63560786Sps break; 63660786Sps opos++; 63760786Sps npos++; 63860786Sps line++; 63960786Sps while (line[0] == '\b' && line[1] != '\0') 64060786Sps { 64160786Sps /* 64260786Sps * Found a backspace. The file position moves 64360786Sps * forward by 2 relative to the processed line 64460786Sps * which was searched in hilite_line. 64560786Sps */ 64660786Sps npos += 2; 64760786Sps line += 2; 64860786Sps } 64960786Sps } 65060786Sps} 65160786Sps 65260786Sps/* 65360786Sps * Make a hilite for each string in a physical line which matches 65460786Sps * the current pattern. 65560786Sps * sp,ep delimit the first match already found. 65660786Sps */ 65760786Sps static void 65860786Spshilite_line(linepos, line, sp, ep) 65960786Sps POSITION linepos; 66060786Sps char *line; 66160786Sps char *sp; 66260786Sps char *ep; 66360786Sps{ 66460786Sps char *searchp; 66560786Sps struct hilite *hl; 66660786Sps struct hilite hilites; 66760786Sps 66860786Sps if (sp == NULL || ep == NULL) 66960786Sps return; 67060786Sps /* 67160786Sps * sp and ep delimit the first match in the line. 67260786Sps * Mark the corresponding file positions, then 67360786Sps * look for further matches and mark them. 67460786Sps * {{ This technique, of calling match_pattern on subsequent 67560786Sps * substrings of the line, may mark more than is correct 67660786Sps * if the pattern starts with "^". This bug is fixed 67760786Sps * for those regex functions that accept a notbol parameter 67860786Sps * (currently POSIX and V8-with-regexec2). }} 67960786Sps */ 68060786Sps searchp = line; 68160786Sps /* 68260786Sps * Put the hilites into a temporary list until they're adjusted. 68360786Sps */ 68460786Sps hilites.hl_first = NULL; 68560786Sps do { 68660786Sps if (ep > sp) 68760786Sps { 68860786Sps /* 68960786Sps * Assume that each char position in the "line" 69060786Sps * buffer corresponds to one char position in the file. 69160786Sps * This is not quite true; we need to adjust later. 69260786Sps */ 69360786Sps hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 69460786Sps hl->hl_startpos = linepos + (sp-line); 69560786Sps hl->hl_endpos = linepos + (ep-line); 69660786Sps add_hilite(&hilites, hl); 69760786Sps } 69860786Sps /* 69960786Sps * If we matched more than zero characters, 70060786Sps * move to the first char after the string we matched. 70160786Sps * If we matched zero, just move to the next char. 70260786Sps */ 70360786Sps if (ep > searchp) 70460786Sps searchp = ep; 70560786Sps else if (*searchp != '\0') 70660786Sps searchp++; 70760786Sps else /* end of line */ 70860786Sps break; 70960786Sps } while (match_pattern(searchp, &sp, &ep, 1)); 71060786Sps 71160786Sps if (bs_mode == BS_SPECIAL) 71260786Sps { 71360786Sps /* 71460786Sps * If there were backspaces in the original line, they 71560786Sps * were removed, and hl_startpos/hl_endpos are not correct. 71660786Sps * {{ This is very ugly. }} 71760786Sps */ 71860786Sps adj_hilite(&hilites, linepos); 71960786Sps } 72060786Sps /* 72160786Sps * Now put the hilites into the real list. 72260786Sps */ 72360786Sps while ((hl = hilites.hl_next) != NULL) 72460786Sps { 72560786Sps hilites.hl_next = hl->hl_next; 72660786Sps add_hilite(&hilite_anchor, hl); 72760786Sps } 72860786Sps} 72960786Sps#endif 73060786Sps 73160786Sps/* 73260786Sps * Change the caseless-ness of searches. 73360786Sps * Updates the internal search state to reflect a change in the -i flag. 73460786Sps */ 73560786Sps public void 73660786Spschg_caseless() 73760786Sps{ 73860786Sps if (!is_ucase_pattern) 73960786Sps /* 74060786Sps * Pattern did not have uppercase. 74160786Sps * Just set the search caselessness to the global caselessness. 74260786Sps */ 74360786Sps is_caseless = caseless; 74460786Sps else 74560786Sps /* 74660786Sps * Pattern did have uppercase. 74760786Sps * Discard the pattern; we can't change search caselessness now. 74860786Sps */ 74960786Sps uncompile_pattern(); 75060786Sps} 75160786Sps 75260786Sps#if HILITE_SEARCH 75360786Sps/* 75460786Sps * Find matching text which is currently on screen and highlight it. 75560786Sps */ 75660786Sps static void 75760786Spshilite_screen() 75860786Sps{ 75960786Sps struct scrpos scrpos; 76060786Sps 76160786Sps get_scrpos(&scrpos); 76260786Sps if (scrpos.pos == NULL_POSITION) 76360786Sps return; 76460786Sps prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 76560786Sps repaint_hilite(1); 76660786Sps} 76760786Sps 76860786Sps/* 76960786Sps * Change highlighting parameters. 77060786Sps */ 77160786Sps public void 77260786Spschg_hilite() 77360786Sps{ 77460786Sps /* 77560786Sps * Erase any highlights currently on screen. 77660786Sps */ 77760786Sps clr_hilite(); 77860786Sps hide_hilite = 0; 77960786Sps 78060786Sps if (hilite_search == OPT_ONPLUS) 78160786Sps /* 78260786Sps * Display highlights. 78360786Sps */ 78460786Sps hilite_screen(); 78560786Sps} 78660786Sps#endif 78760786Sps 78860786Sps/* 78960786Sps * Figure out where to start a search. 79060786Sps */ 79160786Sps static POSITION 79260786Spssearch_pos(search_type) 79360786Sps int search_type; 79460786Sps{ 79560786Sps POSITION pos; 79660786Sps int linenum; 79760786Sps 79860786Sps if (empty_screen()) 79960786Sps { 80060786Sps /* 80160786Sps * Start at the beginning (or end) of the file. 80260786Sps * The empty_screen() case is mainly for 80360786Sps * command line initiated searches; 80460786Sps * for example, "+/xyz" on the command line. 80560786Sps * Also for multi-file (SRCH_PAST_EOF) searches. 80660786Sps */ 80760786Sps if (search_type & SRCH_FORW) 80860786Sps { 80960786Sps return (ch_zero()); 81060786Sps } else 81160786Sps { 81260786Sps pos = ch_length(); 81360786Sps if (pos == NULL_POSITION) 81460786Sps { 81560786Sps (void) ch_end_seek(); 81660786Sps pos = ch_length(); 81760786Sps } 81860786Sps return (pos); 81960786Sps } 82060786Sps } 82160786Sps if (how_search) 82260786Sps { 82360786Sps /* 82460786Sps * Search does not include current screen. 82560786Sps */ 82660786Sps if (search_type & SRCH_FORW) 82760786Sps linenum = BOTTOM_PLUS_ONE; 82860786Sps else 82960786Sps linenum = TOP; 83060786Sps pos = position(linenum); 83160786Sps } else 83260786Sps { 83360786Sps /* 83460786Sps * Search includes current screen. 83560786Sps * It starts at the jump target (if searching backwards), 83660786Sps * or at the jump target plus one (if forwards). 83760786Sps */ 83860786Sps linenum = adjsline(jump_sline); 83960786Sps pos = position(linenum); 84060786Sps if (search_type & SRCH_FORW) 84160786Sps { 84260786Sps pos = forw_raw_line(pos, (char **)NULL); 84360786Sps while (pos == NULL_POSITION) 84460786Sps { 84560786Sps if (++linenum >= sc_height) 84660786Sps break; 84760786Sps pos = position(linenum); 84860786Sps } 84960786Sps } else 85060786Sps { 85160786Sps while (pos == NULL_POSITION) 85260786Sps { 85360786Sps if (--linenum < 0) 85460786Sps break; 85560786Sps pos = position(linenum); 85660786Sps } 85760786Sps } 85860786Sps } 85960786Sps return (pos); 86060786Sps} 86160786Sps 86260786Sps/* 86360786Sps * Search a subset of the file, specified by start/end position. 86460786Sps */ 86560786Sps static int 86660786Spssearch_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) 86760786Sps POSITION pos; 86860786Sps POSITION endpos; 86960786Sps int search_type; 87060786Sps int matches; 87160786Sps int maxlines; 87260786Sps POSITION *plinepos; 87360786Sps POSITION *pendpos; 87460786Sps{ 87560786Sps char *line; 87660786Sps int linenum; 87760786Sps char *sp, *ep; 87860786Sps int line_match; 87960786Sps POSITION linepos, oldpos; 88060786Sps 88160786Sps linenum = find_linenum(pos); 88260786Sps oldpos = pos; 88360786Sps for (;;) 88460786Sps { 88560786Sps /* 88660786Sps * Get lines until we find a matching one or until 88760786Sps * we hit end-of-file (or beginning-of-file if we're 88860786Sps * going backwards), or until we hit the end position. 88960786Sps */ 89060786Sps if (ABORT_SIGS()) 89160786Sps { 89260786Sps /* 89360786Sps * A signal aborts the search. 89460786Sps */ 89560786Sps return (-1); 89660786Sps } 89760786Sps 89860786Sps if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0) 89960786Sps { 90060786Sps /* 90160786Sps * Reached end position without a match. 90260786Sps */ 90360786Sps if (pendpos != NULL) 90460786Sps *pendpos = pos; 90560786Sps return (matches); 90660786Sps } 90760786Sps if (maxlines > 0) 90860786Sps maxlines--; 90960786Sps 91060786Sps if (search_type & SRCH_FORW) 91160786Sps { 91260786Sps /* 91360786Sps * Read the next line, and save the 91460786Sps * starting position of that line in linepos. 91560786Sps */ 91660786Sps linepos = pos; 91760786Sps pos = forw_raw_line(pos, &line); 91860786Sps if (linenum != 0) 91960786Sps linenum++; 92060786Sps } else 92160786Sps { 92260786Sps /* 92360786Sps * Read the previous line and save the 92460786Sps * starting position of that line in linepos. 92560786Sps */ 92660786Sps pos = back_raw_line(pos, &line); 92760786Sps linepos = pos; 92860786Sps if (linenum != 0) 92960786Sps linenum--; 93060786Sps } 93160786Sps 93260786Sps if (pos == NULL_POSITION) 93360786Sps { 93460786Sps /* 93560786Sps * Reached EOF/BOF without a match. 93660786Sps */ 93760786Sps if (pendpos != NULL) 93860786Sps *pendpos = oldpos; 93960786Sps return (matches); 94060786Sps } 94160786Sps 94260786Sps /* 94360786Sps * If we're using line numbers, we might as well 94460786Sps * remember the information we have now (the position 94560786Sps * and line number of the current line). 94660786Sps * Don't do it for every line because it slows down 94760786Sps * the search. Remember the line number only if 94860786Sps * we're "far" from the last place we remembered it. 94960786Sps */ 95060786Sps if (linenums && abs((int)(pos - oldpos)) > 1024) 95160786Sps add_lnum(linenum, pos); 95260786Sps oldpos = pos; 95360786Sps 95460786Sps /* 95560786Sps * If it's a caseless search, convert the line to lowercase. 95660786Sps * If we're doing backspace processing, delete backspaces. 95760786Sps */ 95860786Sps if (is_caseless || bs_mode == BS_SPECIAL) 95960786Sps { 96060786Sps int ops = 0; 96160786Sps if (is_caseless) 96260786Sps ops |= CVT_TO_LC; 96360786Sps if (bs_mode == BS_SPECIAL) 96460786Sps ops |= CVT_BS; 96560786Sps if (bs_mode != BS_CONTROL) 96660786Sps ops |= CVT_CRLF; 96760786Sps cvt_text(line, line, ops); 96860786Sps } else if (bs_mode != BS_CONTROL) 96960786Sps { 97060786Sps cvt_text(line, line, CVT_CRLF); 97160786Sps } 97260786Sps 97360786Sps /* 97460786Sps * Test the next line to see if we have a match. 97560786Sps * We are successful if we either want a match and got one, 97660786Sps * or if we want a non-match and got one. 97760786Sps */ 97860786Sps line_match = match_pattern(line, &sp, &ep, 0); 97960786Sps line_match = (!(search_type & SRCH_NO_MATCH) && line_match) || 98060786Sps ((search_type & SRCH_NO_MATCH) && !line_match); 98160786Sps if (!line_match) 98260786Sps continue; 98360786Sps /* 98460786Sps * Got a match. 98560786Sps */ 98660786Sps if (search_type & SRCH_FIND_ALL) 98760786Sps { 98860786Sps#if HILITE_SEARCH 98960786Sps /* 99060786Sps * We are supposed to find all matches in the range. 99160786Sps * Just add the matches in this line to the 99260786Sps * hilite list and keep searching. 99360786Sps */ 99460786Sps if (line_match) 99560786Sps hilite_line(linepos, line, sp, ep); 99660786Sps#endif 99760786Sps } else if (--matches <= 0) 99860786Sps { 99960786Sps /* 100060786Sps * Found the one match we're looking for. 100160786Sps * Return it. 100260786Sps */ 100360786Sps#if HILITE_SEARCH 100460786Sps if (hilite_search == 1) 100560786Sps { 100660786Sps /* 100760786Sps * Clear the hilite list and add only 100860786Sps * the matches in this one line. 100960786Sps */ 101060786Sps clr_hilite(); 101160786Sps if (line_match) 101260786Sps hilite_line(linepos, line, sp, ep); 101360786Sps } 101460786Sps#endif 101560786Sps if (plinepos != NULL) 101660786Sps *plinepos = linepos; 101760786Sps return (0); 101860786Sps } 101960786Sps } 102060786Sps} 102160786Sps 102260786Sps/* 102360786Sps * Search for the n-th occurrence of a specified pattern, 102460786Sps * either forward or backward. 102560786Sps * Return the number of matches not yet found in this file 102660786Sps * (that is, n minus the number of matches found). 102760786Sps * Return -1 if the search should be aborted. 102860786Sps * Caller may continue the search in another file 102960786Sps * if less than n matches are found in this file. 103060786Sps */ 103160786Sps public int 103260786Spssearch(search_type, pattern, n) 103360786Sps int search_type; 103460786Sps char *pattern; 103560786Sps int n; 103660786Sps{ 103760786Sps POSITION pos; 103860786Sps int ucase; 103960786Sps 104060786Sps if (pattern == NULL || *pattern == '\0') 104160786Sps { 104260786Sps /* 104360786Sps * A null pattern means use the previously compiled pattern. 104460786Sps */ 104560786Sps if (!prev_pattern()) 104660786Sps { 104760786Sps error("No previous regular expression", NULL_PARG); 104860786Sps return (-1); 104960786Sps } 105060786Sps if ((search_type & SRCH_NO_REGEX) != 105160786Sps (last_search_type & SRCH_NO_REGEX)) 105260786Sps { 105360786Sps error("Please re-enter search pattern", NULL_PARG); 105460786Sps return -1; 105560786Sps } 105660786Sps#if HILITE_SEARCH 105760786Sps if (hilite_search == OPT_ON) 105860786Sps { 105960786Sps /* 106060786Sps * Erase the highlights currently on screen. 106160786Sps * If the search fails, we'll redisplay them later. 106260786Sps */ 106360786Sps repaint_hilite(0); 106460786Sps } 106560786Sps if (hilite_search == OPT_ONPLUS && hide_hilite) 106660786Sps { 106760786Sps /* 106860786Sps * Highlight any matches currently on screen, 106960786Sps * before we actually start the search. 107060786Sps */ 107160786Sps hide_hilite = 0; 107260786Sps hilite_screen(); 107360786Sps } 107460786Sps hide_hilite = 0; 107560786Sps#endif 107660786Sps } else 107760786Sps { 107860786Sps /* 107960786Sps * Compile the pattern. 108060786Sps */ 108160786Sps ucase = is_ucase(pattern); 108260786Sps if (caseless == OPT_ONPLUS) 108360786Sps cvt_text(pattern, pattern, CVT_TO_LC); 108460786Sps if (compile_pattern(pattern, search_type) < 0) 108560786Sps return (-1); 108660786Sps /* 108760786Sps * Ignore case if -I is set OR 108860786Sps * -i is set AND the pattern is all lowercase. 108960786Sps */ 109060786Sps is_ucase_pattern = ucase; 109160786Sps if (is_ucase_pattern && caseless != OPT_ONPLUS) 109260786Sps is_caseless = 0; 109360786Sps else 109460786Sps is_caseless = caseless; 109560786Sps#if HILITE_SEARCH 109660786Sps if (hilite_search) 109760786Sps { 109860786Sps /* 109960786Sps * Erase the highlights currently on screen. 110060786Sps * Also permanently delete them from the hilite list. 110160786Sps */ 110260786Sps repaint_hilite(0); 110360786Sps hide_hilite = 0; 110460786Sps clr_hilite(); 110560786Sps } 110660786Sps if (hilite_search == OPT_ONPLUS) 110760786Sps { 110860786Sps /* 110960786Sps * Highlight any matches currently on screen, 111060786Sps * before we actually start the search. 111160786Sps */ 111260786Sps hilite_screen(); 111360786Sps } 111460786Sps#endif 111560786Sps } 111660786Sps 111760786Sps /* 111860786Sps * Figure out where to start the search. 111960786Sps */ 112060786Sps pos = search_pos(search_type); 112160786Sps if (pos == NULL_POSITION) 112260786Sps { 112360786Sps /* 112460786Sps * Can't find anyplace to start searching from. 112560786Sps */ 112660786Sps if (search_type & SRCH_PAST_EOF) 112760786Sps return (n); 112860786Sps /* repaint(); -- why was this here? */ 112960786Sps error("Nothing to search", NULL_PARG); 113060786Sps return (-1); 113160786Sps } 113260786Sps 113360786Sps n = search_range(pos, NULL_POSITION, search_type, n, -1, 113460786Sps &pos, (POSITION*)NULL); 113560786Sps if (n != 0) 113660786Sps { 113760786Sps /* 113860786Sps * Search was unsuccessful. 113960786Sps */ 114060786Sps#if HILITE_SEARCH 114160786Sps if (hilite_search == OPT_ON && n > 0) 114260786Sps /* 114360786Sps * Redisplay old hilites. 114460786Sps */ 114560786Sps repaint_hilite(1); 114660786Sps#endif 114760786Sps return (n); 114860786Sps } 114960786Sps 115060786Sps if (!(search_type & SRCH_NO_MOVE)) 115160786Sps { 115260786Sps /* 115360786Sps * Go to the matching line. 115460786Sps */ 115560786Sps jump_loc(pos, jump_sline); 115660786Sps } 115760786Sps 115860786Sps#if HILITE_SEARCH 115960786Sps if (hilite_search == OPT_ON) 116060786Sps /* 116160786Sps * Display new hilites in the matching line. 116260786Sps */ 116360786Sps repaint_hilite(1); 116460786Sps#endif 116560786Sps return (0); 116660786Sps} 116760786Sps 116860786Sps 116960786Sps#if HILITE_SEARCH 117060786Sps/* 117160786Sps * Prepare hilites in a given range of the file. 117260786Sps * 117360786Sps * The pair (prep_startpos,prep_endpos) delimits a contiguous region 117460786Sps * of the file that has been "prepared"; that is, scanned for matches for 117560786Sps * the current search pattern, and hilites have been created for such matches. 117660786Sps * If prep_startpos == NULL_POSITION, the prep region is empty. 117760786Sps * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 117860786Sps * prep_hilite asks that the range (spos,epos) be covered by the prep region. 117960786Sps */ 118060786Sps public void 118160786Spsprep_hilite(spos, epos, maxlines) 118260786Sps POSITION spos; 118360786Sps POSITION epos; 118460786Sps int maxlines; 118560786Sps{ 118660786Sps POSITION nprep_startpos = prep_startpos; 118760786Sps POSITION nprep_endpos = prep_endpos; 118860786Sps POSITION new_epos; 118960786Sps POSITION max_epos; 119060786Sps int result; 119160786Sps int i; 119260786Sps/* 119360786Sps * Search beyond where we're asked to search, so the prep region covers 119460786Sps * more than we need. Do one big search instead of a bunch of small ones. 119560786Sps */ 119660786Sps#define SEARCH_MORE (3*size_linebuf) 119760786Sps 119860786Sps if (!prev_pattern()) 119960786Sps return; 120060786Sps 120160786Sps /* 120260786Sps * If we're limited to a max number of lines, figure out the 120360786Sps * file position we should stop at. 120460786Sps */ 120560786Sps if (maxlines < 0) 120660786Sps max_epos = NULL_POSITION; 120760786Sps else 120860786Sps { 120960786Sps max_epos = spos; 121060786Sps for (i = 0; i < maxlines; i++) 121160786Sps max_epos = forw_raw_line(max_epos, (char **)NULL); 121260786Sps } 121360786Sps 121460786Sps /* 121560786Sps * Find two ranges: 121660786Sps * The range that we need to search (spos,epos); and the range that 121760786Sps * the "prep" region will then cover (nprep_startpos,nprep_endpos). 121860786Sps */ 121960786Sps 122060786Sps if (prep_startpos == NULL_POSITION || 122160786Sps (epos != NULL_POSITION && epos < prep_startpos) || 122260786Sps spos > prep_endpos) 122360786Sps { 122460786Sps /* 122560786Sps * New range is not contiguous with old prep region. 122660786Sps * Discard the old prep region and start a new one. 122760786Sps */ 122860786Sps clr_hilite(); 122960786Sps if (epos != NULL_POSITION) 123060786Sps epos += SEARCH_MORE; 123160786Sps nprep_startpos = spos; 123260786Sps } else 123360786Sps { 123460786Sps /* 123560786Sps * New range partially or completely overlaps old prep region. 123660786Sps */ 123760786Sps if (epos == NULL_POSITION) 123860786Sps { 123960786Sps /* 124060786Sps * New range goes to end of file. 124160786Sps */ 124260786Sps ; 124360786Sps } else if (epos > prep_endpos) 124460786Sps { 124560786Sps /* 124660786Sps * New range ends after old prep region. 124760786Sps * Extend prep region to end at end of new range. 124860786Sps */ 124960786Sps epos += SEARCH_MORE; 125060786Sps } else /* (epos <= prep_endpos) */ 125160786Sps { 125260786Sps /* 125360786Sps * New range ends within old prep region. 125460786Sps * Truncate search to end at start of old prep region. 125560786Sps */ 125660786Sps epos = prep_startpos; 125760786Sps } 125860786Sps 125960786Sps if (spos < prep_startpos) 126060786Sps { 126160786Sps /* 126260786Sps * New range starts before old prep region. 126360786Sps * Extend old prep region backwards to start at 126460786Sps * start of new range. 126560786Sps */ 126660786Sps if (spos < SEARCH_MORE) 126760786Sps spos = 0; 126860786Sps else 126960786Sps spos -= SEARCH_MORE; 127060786Sps nprep_startpos = spos; 127160786Sps } else /* (spos >= prep_startpos) */ 127260786Sps { 127360786Sps /* 127460786Sps * New range starts within or after old prep region. 127560786Sps * Trim search to start at end of old prep region. 127660786Sps */ 127760786Sps spos = prep_endpos; 127860786Sps } 127960786Sps } 128060786Sps 128160786Sps if (epos != NULL_POSITION && max_epos != NULL_POSITION && 128260786Sps epos > max_epos) 128360786Sps /* 128460786Sps * Don't go past the max position we're allowed. 128560786Sps */ 128660786Sps epos = max_epos; 128760786Sps 128860786Sps if (epos == NULL_POSITION || epos > spos) 128960786Sps { 129060786Sps result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0, 129160786Sps maxlines, (POSITION*)NULL, &new_epos); 129260786Sps if (result < 0) 129360786Sps return; 129460786Sps if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) 129560786Sps nprep_endpos = new_epos; 129660786Sps } 129760786Sps prep_startpos = nprep_startpos; 129860786Sps prep_endpos = nprep_endpos; 129960786Sps} 130060786Sps#endif 130160786Sps 130260786Sps/* 130360786Sps * Simple pattern matching function. 130460786Sps * It supports no metacharacters like *, etc. 130560786Sps */ 130660786Sps static int 130760786Spsmatch(pattern, buf, pfound, pend) 130860786Sps char *pattern, *buf; 130960786Sps char **pfound, **pend; 131060786Sps{ 131160786Sps register char *pp, *lp; 131260786Sps 131360786Sps for ( ; *buf != '\0'; buf++) 131460786Sps { 131560786Sps for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) 131660786Sps if (*pp == '\0' || *lp == '\0') 131760786Sps break; 131860786Sps if (*pp == '\0') 131960786Sps { 132060786Sps if (pfound != NULL) 132160786Sps *pfound = buf; 132260786Sps if (pend != NULL) 132360786Sps *pend = lp; 132460786Sps return (1); 132560786Sps } 132660786Sps } 132760786Sps return (0); 132860786Sps} 132960786Sps 133060786Sps#if HAVE_V8_REGCOMP 133160786Sps/* 133260786Sps * This function is called by the V8 regcomp to report 133360786Sps * errors in regular expressions. 133460786Sps */ 133560786Sps void 133660786Spsregerror(s) 133760786Sps char *s; 133860786Sps{ 133960786Sps PARG parg; 134060786Sps 134160786Sps parg.p_string = s; 134260786Sps error("%s", &parg); 134360786Sps} 134460786Sps#endif 134560786Sps 134660786Sps#if !HAVE_STRCHR 134760786Sps/* 134860786Sps * strchr is used by regexp.c. 134960786Sps */ 135060786Sps char * 135160786Spsstrchr(s, c) 135260786Sps char *s; 135360786Sps int c; 135460786Sps{ 135560786Sps for ( ; *s != '\0'; s++) 135660786Sps if (*s == c) 135760786Sps return (s); 135860786Sps if (c == '\0') 135960786Sps return (s); 136060786Sps return (NULL); 136160786Sps} 136260786Sps#endif 136360786Sps 1364