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