160812Sps/* $FreeBSD$ */ 260786Sps/* 3240121Sdelphij * Copyright (C) 1984-2012 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 * 8240121Sdelphij * For more information, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * Routines to search a file for a pattern. 1460786Sps */ 1560786Sps 1660786Sps#include "less.h" 17195941Sdelphij#include "pattern.h" 1860786Sps#include "position.h" 19172471Sdelphij#include "charset.h" 2060786Sps 2160786Sps#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 2260786Sps#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 2360786Sps 2460786Spsextern int sigs; 2560786Spsextern int how_search; 2660786Spsextern int caseless; 2760786Spsextern int linenums; 2860786Spsextern int sc_height; 2960786Spsextern int jump_sline; 3060786Spsextern int bs_mode; 31170259Sdelphijextern int less_is_more; 32128348Stjrextern int ctldisp; 3363131Spsextern int status_col; 34170259Sdelphijextern void * constant ml_search; 3560786Spsextern POSITION start_attnpos; 3660786Spsextern POSITION end_attnpos; 37191930Sdelphijextern int utf_mode; 38191930Sdelphijextern int screen_trashed; 3960786Sps#if HILITE_SEARCH 4060786Spsextern int hilite_search; 4160786Spsextern int size_linebuf; 4260786Spsextern int squished; 4360786Spsextern int can_goto_line; 4460786Spsstatic int hide_hilite; 4560786Spsstatic POSITION prep_startpos; 4660786Spsstatic POSITION prep_endpos; 47195941Sdelphijstatic int is_caseless; 48195941Sdelphijstatic int is_ucase_pattern; 4960786Sps 5060786Spsstruct hilite 5160786Sps{ 5260786Sps struct hilite *hl_next; 5360786Sps POSITION hl_startpos; 5460786Sps POSITION hl_endpos; 5560786Sps}; 5660786Spsstatic struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; 57191930Sdelphijstatic struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION }; 5860786Sps#define hl_first hl_next 5960786Sps#endif 6060786Sps 6160786Sps/* 6260786Sps * These are the static variables that represent the "remembered" 63195941Sdelphij * search pattern and filter pattern. 6460786Sps */ 65195941Sdelphijstruct pattern_info { 66195941Sdelphij DEFINE_PATTERN(compiled); 67195941Sdelphij char* text; 68195941Sdelphij int search_type; 69195941Sdelphij}; 70240121Sdelphij 71240121Sdelphij#if NO_REGEX 72240121Sdelphij#define info_compiled(info) ((void*)0) 73240121Sdelphij#else 74240121Sdelphij#define info_compiled(info) ((info)->compiled) 75240121Sdelphij#endif 76195941Sdelphij 77195941Sdelphijstatic struct pattern_info search_info; 78195941Sdelphijstatic struct pattern_info filter_info; 7960786Sps 80173685Sdelphij/* 81221715Sdelphij * Are there any uppercase letters in this string? 82221715Sdelphij */ 83221715Sdelphij static int 84221715Sdelphijis_ucase(str) 85221715Sdelphij char *str; 86221715Sdelphij{ 87221715Sdelphij char *str_end = str + strlen(str); 88221715Sdelphij LWCHAR ch; 89221715Sdelphij 90221715Sdelphij while (str < str_end) 91221715Sdelphij { 92221715Sdelphij ch = step_char(&str, +1, str_end); 93221715Sdelphij if (IS_UPPER(ch)) 94221715Sdelphij return (1); 95221715Sdelphij } 96221715Sdelphij return (0); 97221715Sdelphij} 98221715Sdelphij 99221715Sdelphij/* 100195941Sdelphij * Compile and save a search pattern. 101173685Sdelphij */ 102173685Sdelphij static int 103195941Sdelphijset_pattern(info, pattern, search_type) 104195941Sdelphij struct pattern_info *info; 105195941Sdelphij char *pattern; 106195941Sdelphij int search_type; 107173685Sdelphij{ 108240121Sdelphij#if !NO_REGEX 109195941Sdelphij if (pattern == NULL) 110240121Sdelphij CLEAR_PATTERN(info->compiled); 111195941Sdelphij else if (compile_pattern(pattern, search_type, &info->compiled) < 0) 112195941Sdelphij return -1; 113240121Sdelphij#endif 114195941Sdelphij /* Pattern compiled successfully; save the text too. */ 115195941Sdelphij if (info->text != NULL) 116195941Sdelphij free(info->text); 117195941Sdelphij info->text = NULL; 118195941Sdelphij if (pattern != NULL) 119195941Sdelphij { 120195941Sdelphij info->text = (char *) ecalloc(1, strlen(pattern)+1); 121195941Sdelphij strcpy(info->text, pattern); 122195941Sdelphij } 123195941Sdelphij info->search_type = search_type; 124221715Sdelphij 125221715Sdelphij /* 126221715Sdelphij * Ignore case if -I is set OR 127221715Sdelphij * -i is set AND the pattern is all lowercase. 128221715Sdelphij */ 129221715Sdelphij is_ucase_pattern = is_ucase(pattern); 130221715Sdelphij if (is_ucase_pattern && caseless != OPT_ONPLUS) 131221715Sdelphij is_caseless = 0; 132221715Sdelphij else 133221715Sdelphij is_caseless = caseless; 134195941Sdelphij return 0; 135173685Sdelphij} 136173685Sdelphij 137173685Sdelphij/* 138195941Sdelphij * Discard a saved pattern. 139173685Sdelphij */ 14060786Sps static void 141195941Sdelphijclear_pattern(info) 142195941Sdelphij struct pattern_info *info; 14360786Sps{ 144195941Sdelphij if (info->text != NULL) 145195941Sdelphij free(info->text); 146195941Sdelphij info->text = NULL; 147240121Sdelphij#if !NO_REGEX 148195941Sdelphij uncompile_pattern(&info->compiled); 149240121Sdelphij#endif 150195941Sdelphij} 15160786Sps 152195941Sdelphij/* 153195941Sdelphij * Initialize saved pattern to nothing. 154195941Sdelphij */ 155195941Sdelphij static void 156195941Sdelphijinit_pattern(info) 157195941Sdelphij struct pattern_info *info; 158195941Sdelphij{ 159195941Sdelphij CLEAR_PATTERN(info->compiled); 160195941Sdelphij info->text = NULL; 161195941Sdelphij info->search_type = 0; 162195941Sdelphij} 163170259Sdelphij 164195941Sdelphij/* 165195941Sdelphij * Initialize search variables. 166195941Sdelphij */ 167195941Sdelphij public void 168195941Sdelphijinit_search() 169195941Sdelphij{ 170195941Sdelphij init_pattern(&search_info); 171195941Sdelphij init_pattern(&filter_info); 17260786Sps} 17360786Sps 17460786Sps/* 175195941Sdelphij * Determine which text conversions to perform before pattern matching. 176128348Stjr */ 177128348Stjr static int 178128348Stjrget_cvt_ops() 179128348Stjr{ 180128348Stjr int ops = 0; 181128348Stjr if (is_caseless || bs_mode == BS_SPECIAL) 182128348Stjr { 183128348Stjr if (is_caseless) 184128348Stjr ops |= CVT_TO_LC; 185128348Stjr if (bs_mode == BS_SPECIAL) 186128348Stjr ops |= CVT_BS; 187128348Stjr if (bs_mode != BS_CONTROL) 188128348Stjr ops |= CVT_CRLF; 189128348Stjr } else if (bs_mode != BS_CONTROL) 190128348Stjr { 191128348Stjr ops |= CVT_CRLF; 192128348Stjr } 193128348Stjr if (ctldisp == OPT_ONPLUS) 194128348Stjr ops |= CVT_ANSI; 195128348Stjr return (ops); 196128348Stjr} 197128348Stjr 198128348Stjr/* 19960786Sps * Is there a previous (remembered) search pattern? 20060786Sps */ 20160786Sps static int 202195941Sdelphijprev_pattern(info) 203195941Sdelphij struct pattern_info *info; 20460786Sps{ 205240121Sdelphij#if !NO_REGEX 206240121Sdelphij if ((info->search_type & SRCH_NO_REGEX) == 0) 207240121Sdelphij return (!is_null_pattern(info->compiled)); 208240121Sdelphij#endif 209240121Sdelphij return (info->text != NULL); 21060786Sps} 21160786Sps 21260786Sps#if HILITE_SEARCH 21360786Sps/* 21460786Sps * Repaint the hilites currently displayed on the screen. 21560786Sps * Repaint each line which contains highlighted text. 21660786Sps * If on==0, force all hilites off. 21760786Sps */ 21860786Sps public void 21960786Spsrepaint_hilite(on) 22060786Sps int on; 22160786Sps{ 22260786Sps int slinenum; 22360786Sps POSITION pos; 22460786Sps POSITION epos; 22560786Sps int save_hide_hilite; 22660786Sps 22760786Sps if (squished) 22860786Sps repaint(); 22960786Sps 23060786Sps save_hide_hilite = hide_hilite; 23160786Sps if (!on) 23260786Sps { 23360786Sps if (hide_hilite) 23460786Sps return; 23560786Sps hide_hilite = 1; 23660786Sps } 23760786Sps 23860786Sps if (!can_goto_line) 23960786Sps { 24060786Sps repaint(); 24160786Sps hide_hilite = save_hide_hilite; 24260786Sps return; 24360786Sps } 24460786Sps 24560786Sps for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 24660786Sps { 24760786Sps pos = position(slinenum); 24860786Sps if (pos == NULL_POSITION) 24960786Sps continue; 25060786Sps epos = position(slinenum+1); 251195941Sdelphij (void) forw_line(pos); 252195941Sdelphij goto_line(slinenum); 253195941Sdelphij put_line(); 25460786Sps } 255221715Sdelphij lower_left(); 25660786Sps hide_hilite = save_hide_hilite; 25760786Sps} 25860786Sps 25960786Sps/* 26060786Sps * Clear the attn hilite. 26160786Sps */ 26260786Sps public void 26360786Spsclear_attn() 26460786Sps{ 26560786Sps int slinenum; 26660786Sps POSITION old_start_attnpos; 26760786Sps POSITION old_end_attnpos; 26860786Sps POSITION pos; 26960786Sps POSITION epos; 270170898Sdelphij int moved = 0; 27160786Sps 27260786Sps if (start_attnpos == NULL_POSITION) 27360786Sps return; 27460786Sps old_start_attnpos = start_attnpos; 27560786Sps old_end_attnpos = end_attnpos; 27660786Sps start_attnpos = end_attnpos = NULL_POSITION; 27760786Sps 27860786Sps if (!can_goto_line) 27960786Sps { 28060786Sps repaint(); 28160786Sps return; 28260786Sps } 28360786Sps if (squished) 28460786Sps repaint(); 28560786Sps 28660786Sps for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 28760786Sps { 28860786Sps pos = position(slinenum); 28960786Sps if (pos == NULL_POSITION) 29060786Sps continue; 29160786Sps epos = position(slinenum+1); 29260786Sps if (pos < old_end_attnpos && 29360786Sps (epos == NULL_POSITION || epos > old_start_attnpos)) 29460786Sps { 29560786Sps (void) forw_line(pos); 29660786Sps goto_line(slinenum); 29760786Sps put_line(); 298170898Sdelphij moved = 1; 29960786Sps } 30060786Sps } 301170898Sdelphij if (moved) 302170898Sdelphij lower_left(); 30360786Sps} 30460786Sps#endif 30560786Sps 30660786Sps/* 30760786Sps * Hide search string highlighting. 30860786Sps */ 30960786Sps public void 31060786Spsundo_search() 31160786Sps{ 312195941Sdelphij if (!prev_pattern(&search_info)) 31360786Sps { 31460786Sps error("No previous regular expression", NULL_PARG); 31560786Sps return; 31660786Sps } 31760786Sps#if HILITE_SEARCH 31860786Sps hide_hilite = !hide_hilite; 31960786Sps repaint_hilite(1); 32060786Sps#endif 32160786Sps} 32260786Sps 32360786Sps#if HILITE_SEARCH 32460786Sps/* 32560786Sps * Clear the hilite list. 32660786Sps */ 32760786Sps public void 328191930Sdelphijclr_hlist(anchor) 329191930Sdelphij struct hilite *anchor; 33060786Sps{ 33160786Sps struct hilite *hl; 33260786Sps struct hilite *nexthl; 33360786Sps 334191930Sdelphij for (hl = anchor->hl_first; hl != NULL; hl = nexthl) 33560786Sps { 33660786Sps nexthl = hl->hl_next; 33760786Sps free((void*)hl); 33860786Sps } 339191930Sdelphij anchor->hl_first = NULL; 34060786Sps prep_startpos = prep_endpos = NULL_POSITION; 34160786Sps} 34260786Sps 343191930Sdelphij public void 344191930Sdelphijclr_hilite() 345191930Sdelphij{ 346191930Sdelphij clr_hlist(&hilite_anchor); 347191930Sdelphij} 348191930Sdelphij 349191930Sdelphij public void 350191930Sdelphijclr_filter() 351191930Sdelphij{ 352191930Sdelphij clr_hlist(&filter_anchor); 353191930Sdelphij} 354191930Sdelphij 35560786Sps/* 35660786Sps * Should any characters in a specified range be highlighted? 357161478Sdelphij */ 358161478Sdelphij static int 359161478Sdelphijis_hilited_range(pos, epos) 360161478Sdelphij POSITION pos; 361161478Sdelphij POSITION epos; 362161478Sdelphij{ 363161478Sdelphij struct hilite *hl; 364161478Sdelphij 365161478Sdelphij /* 366161478Sdelphij * Look at each highlight and see if any part of it falls in the range. 367161478Sdelphij */ 368161478Sdelphij for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) 369161478Sdelphij { 370161478Sdelphij if (hl->hl_endpos > pos && 371161478Sdelphij (epos == NULL_POSITION || epos > hl->hl_startpos)) 372161478Sdelphij return (1); 373161478Sdelphij } 374161478Sdelphij return (0); 375161478Sdelphij} 376161478Sdelphij 377191930Sdelphij/* 378191930Sdelphij * Is a line "filtered" -- that is, should it be hidden? 379191930Sdelphij */ 380191930Sdelphij public int 381191930Sdelphijis_filtered(pos) 382191930Sdelphij POSITION pos; 383191930Sdelphij{ 384191930Sdelphij struct hilite *hl; 385191930Sdelphij 386191930Sdelphij if (ch_getflags() & CH_HELPFILE) 387191930Sdelphij return (0); 388191930Sdelphij 389191930Sdelphij /* 390191930Sdelphij * Look at each filter and see if the start position 391191930Sdelphij * equals the start position of the line. 392191930Sdelphij */ 393191930Sdelphij for (hl = filter_anchor.hl_first; hl != NULL; hl = hl->hl_next) 394191930Sdelphij { 395191930Sdelphij if (hl->hl_startpos == pos) 396191930Sdelphij return (1); 397191930Sdelphij } 398191930Sdelphij return (0); 399191930Sdelphij} 400191930Sdelphij 401161478Sdelphij/* 402161478Sdelphij * Should any characters in a specified range be highlighted? 40360786Sps * If nohide is nonzero, don't consider hide_hilite. 40460786Sps */ 40560786Sps public int 406161478Sdelphijis_hilited(pos, epos, nohide, p_matches) 40760786Sps POSITION pos; 40860786Sps POSITION epos; 40960786Sps int nohide; 410161478Sdelphij int *p_matches; 41160786Sps{ 412161478Sdelphij int match; 41360786Sps 414161478Sdelphij if (p_matches != NULL) 415161478Sdelphij *p_matches = 0; 416161478Sdelphij 41763131Sps if (!status_col && 41863131Sps start_attnpos != NULL_POSITION && 41960786Sps pos < end_attnpos && 42060786Sps (epos == NULL_POSITION || epos > start_attnpos)) 42160786Sps /* 42260786Sps * The attn line overlaps this range. 42360786Sps */ 42460786Sps return (1); 42560786Sps 426161478Sdelphij match = is_hilited_range(pos, epos); 427161478Sdelphij if (!match) 428161478Sdelphij return (0); 429161478Sdelphij 430161478Sdelphij if (p_matches != NULL) 431161478Sdelphij /* 432161478Sdelphij * Report matches, even if we're hiding highlights. 433161478Sdelphij */ 434161478Sdelphij *p_matches = 1; 435161478Sdelphij 43660786Sps if (hilite_search == 0) 43760786Sps /* 43860786Sps * Not doing highlighting. 43960786Sps */ 44060786Sps return (0); 44160786Sps 44260786Sps if (!nohide && hide_hilite) 44360786Sps /* 44460786Sps * Highlighting is hidden. 44560786Sps */ 44660786Sps return (0); 44760786Sps 448161478Sdelphij return (1); 44960786Sps} 45060786Sps 45160786Sps/* 45260786Sps * Add a new hilite to a hilite list. 45360786Sps */ 45460786Sps static void 45560786Spsadd_hilite(anchor, hl) 45660786Sps struct hilite *anchor; 45760786Sps struct hilite *hl; 45860786Sps{ 45960786Sps struct hilite *ihl; 46060786Sps 46160786Sps /* 46260786Sps * Hilites are sorted in the list; find where new one belongs. 46360786Sps * Insert new one after ihl. 46460786Sps */ 46560786Sps for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) 46660786Sps { 46760786Sps if (ihl->hl_next->hl_startpos > hl->hl_startpos) 46860786Sps break; 46960786Sps } 47060786Sps 47160786Sps /* 47260786Sps * Truncate hilite so it doesn't overlap any existing ones 47360786Sps * above and below it. 47460786Sps */ 47560786Sps if (ihl != anchor) 47660786Sps hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); 47760786Sps if (ihl->hl_next != NULL) 47860786Sps hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos); 47960786Sps if (hl->hl_startpos >= hl->hl_endpos) 48060786Sps { 48160786Sps /* 48260786Sps * Hilite was truncated out of existence. 48360786Sps */ 48460786Sps free(hl); 48560786Sps return; 48660786Sps } 48760786Sps hl->hl_next = ihl->hl_next; 48860786Sps ihl->hl_next = hl; 48960786Sps} 49060786Sps 49160786Sps/* 492240121Sdelphij * Hilight every character in a range of displayed characters. 493240121Sdelphij */ 494240121Sdelphij static void 495240121Sdelphijcreate_hilites(linepos, start_index, end_index, chpos) 496240121Sdelphij POSITION linepos; 497240121Sdelphij int start_index; 498240121Sdelphij int end_index; 499240121Sdelphij int *chpos; 500240121Sdelphij{ 501240121Sdelphij struct hilite *hl; 502240121Sdelphij int i; 503240121Sdelphij 504240121Sdelphij /* Start the first hilite. */ 505240121Sdelphij hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 506240121Sdelphij hl->hl_startpos = linepos + chpos[start_index]; 507240121Sdelphij 508240121Sdelphij /* 509240121Sdelphij * Step through the displayed chars. 510240121Sdelphij * If the source position (before cvt) of the char is one more 511240121Sdelphij * than the source pos of the previous char (the usual case), 512240121Sdelphij * just increase the size of the current hilite by one. 513240121Sdelphij * Otherwise (there are backspaces or something involved), 514240121Sdelphij * finish the current hilite and start a new one. 515240121Sdelphij */ 516240121Sdelphij for (i = start_index+1; i <= end_index; i++) 517240121Sdelphij { 518240121Sdelphij if (chpos[i] != chpos[i-1] + 1 || i == end_index) 519240121Sdelphij { 520240121Sdelphij hl->hl_endpos = linepos + chpos[i-1] + 1; 521240121Sdelphij add_hilite(&hilite_anchor, hl); 522240121Sdelphij /* Start new hilite unless this is the last char. */ 523240121Sdelphij if (i < end_index) 524240121Sdelphij { 525240121Sdelphij hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 526240121Sdelphij hl->hl_startpos = linepos + chpos[i]; 527240121Sdelphij } 528240121Sdelphij } 529240121Sdelphij } 530240121Sdelphij} 531240121Sdelphij 532240121Sdelphij/* 53360786Sps * Make a hilite for each string in a physical line which matches 53460786Sps * the current pattern. 53560786Sps * sp,ep delimit the first match already found. 53660786Sps */ 53760786Sps static void 538195941Sdelphijhilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops) 53960786Sps POSITION linepos; 54060786Sps char *line; 541170259Sdelphij int line_len; 542195941Sdelphij int *chpos; 54360786Sps char *sp; 54460786Sps char *ep; 545128348Stjr int cvt_ops; 54660786Sps{ 54760786Sps char *searchp; 548170259Sdelphij char *line_end = line + line_len; 54960786Sps 55060786Sps if (sp == NULL || ep == NULL) 55160786Sps return; 55260786Sps /* 55360786Sps * sp and ep delimit the first match in the line. 55460786Sps * Mark the corresponding file positions, then 55560786Sps * look for further matches and mark them. 55660786Sps * {{ This technique, of calling match_pattern on subsequent 55760786Sps * substrings of the line, may mark more than is correct 55860786Sps * if the pattern starts with "^". This bug is fixed 55960786Sps * for those regex functions that accept a notbol parameter 560170259Sdelphij * (currently POSIX, PCRE and V8-with-regexec2). }} 56160786Sps */ 56260786Sps searchp = line; 56360786Sps do { 564240121Sdelphij create_hilites(linepos, sp-line, ep-line, chpos); 56560786Sps /* 56660786Sps * If we matched more than zero characters, 56760786Sps * move to the first char after the string we matched. 56860786Sps * If we matched zero, just move to the next char. 56960786Sps */ 57060786Sps if (ep > searchp) 57160786Sps searchp = ep; 572170259Sdelphij else if (searchp != line_end) 57360786Sps searchp++; 57460786Sps else /* end of line */ 57560786Sps break; 576240121Sdelphij } while (match_pattern(info_compiled(&search_info), search_info.text, 577195941Sdelphij searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type)); 57860786Sps} 57960786Sps#endif 58060786Sps 58160786Sps/* 58260786Sps * Change the caseless-ness of searches. 58360786Sps * Updates the internal search state to reflect a change in the -i flag. 58460786Sps */ 58560786Sps public void 58660786Spschg_caseless() 58760786Sps{ 58860786Sps if (!is_ucase_pattern) 58960786Sps /* 59060786Sps * Pattern did not have uppercase. 59160786Sps * Just set the search caselessness to the global caselessness. 59260786Sps */ 59360786Sps is_caseless = caseless; 59460786Sps else 59560786Sps /* 59660786Sps * Pattern did have uppercase. 59760786Sps * Discard the pattern; we can't change search caselessness now. 59860786Sps */ 599195941Sdelphij clear_pattern(&search_info); 60060786Sps} 60160786Sps 60260786Sps#if HILITE_SEARCH 60360786Sps/* 60460786Sps * Find matching text which is currently on screen and highlight it. 60560786Sps */ 60660786Sps static void 60760786Spshilite_screen() 60860786Sps{ 60960786Sps struct scrpos scrpos; 61060786Sps 61160786Sps get_scrpos(&scrpos); 61260786Sps if (scrpos.pos == NULL_POSITION) 61360786Sps return; 61460786Sps prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 61560786Sps repaint_hilite(1); 61660786Sps} 61760786Sps 61860786Sps/* 61960786Sps * Change highlighting parameters. 62060786Sps */ 62160786Sps public void 62260786Spschg_hilite() 62360786Sps{ 62460786Sps /* 62560786Sps * Erase any highlights currently on screen. 62660786Sps */ 62760786Sps clr_hilite(); 62860786Sps hide_hilite = 0; 62960786Sps 63060786Sps if (hilite_search == OPT_ONPLUS) 63160786Sps /* 63260786Sps * Display highlights. 63360786Sps */ 63460786Sps hilite_screen(); 63560786Sps} 63660786Sps#endif 63760786Sps 63860786Sps/* 63960786Sps * Figure out where to start a search. 64060786Sps */ 64160786Sps static POSITION 64260786Spssearch_pos(search_type) 64360786Sps int search_type; 64460786Sps{ 64560786Sps POSITION pos; 64660786Sps int linenum; 64760786Sps 64860786Sps if (empty_screen()) 64960786Sps { 65060786Sps /* 65160786Sps * Start at the beginning (or end) of the file. 65260786Sps * The empty_screen() case is mainly for 65360786Sps * command line initiated searches; 65460786Sps * for example, "+/xyz" on the command line. 65560786Sps * Also for multi-file (SRCH_PAST_EOF) searches. 65660786Sps */ 65760786Sps if (search_type & SRCH_FORW) 65860786Sps { 659221715Sdelphij pos = ch_zero(); 66060786Sps } else 66160786Sps { 66260786Sps pos = ch_length(); 66360786Sps if (pos == NULL_POSITION) 66460786Sps { 66560786Sps (void) ch_end_seek(); 66660786Sps pos = ch_length(); 66760786Sps } 66860786Sps } 669221715Sdelphij linenum = 0; 670221715Sdelphij } else 67160786Sps { 672221715Sdelphij int add_one = 0; 673221715Sdelphij 674221715Sdelphij if (how_search == OPT_ON) 67560786Sps { 676221715Sdelphij /* 677221715Sdelphij * Search does not include current screen. 678221715Sdelphij */ 679221715Sdelphij if (search_type & SRCH_FORW) 680221715Sdelphij linenum = BOTTOM_PLUS_ONE; 681221715Sdelphij else 682221715Sdelphij linenum = TOP; 683221715Sdelphij } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) 684221715Sdelphij { 685221715Sdelphij /* 686221715Sdelphij * Search includes all of displayed screen. 687221715Sdelphij */ 688221715Sdelphij if (search_type & SRCH_FORW) 689221715Sdelphij linenum = TOP; 690221715Sdelphij else 691221715Sdelphij linenum = BOTTOM_PLUS_ONE; 69260786Sps } else 69360786Sps { 694221715Sdelphij /* 695221715Sdelphij * Search includes the part of current screen beyond the jump target. 696221715Sdelphij * It starts at the jump target (if searching backwards), 697221715Sdelphij * or at the jump target plus one (if forwards). 698221715Sdelphij */ 699221715Sdelphij linenum = jump_sline; 700221715Sdelphij if (search_type & SRCH_FORW) 701221715Sdelphij add_one = 1; 70260786Sps } 703221715Sdelphij linenum = adjsline(linenum); 704221715Sdelphij pos = position(linenum); 705221715Sdelphij if (add_one) 706221715Sdelphij pos = forw_raw_line(pos, (char **)NULL, (int *)NULL); 70760786Sps } 708221715Sdelphij 709221715Sdelphij /* 710221715Sdelphij * If the line is empty, look around for a plausible starting place. 711221715Sdelphij */ 712221715Sdelphij if (search_type & SRCH_FORW) 713221715Sdelphij { 714221715Sdelphij while (pos == NULL_POSITION) 715221715Sdelphij { 716221715Sdelphij if (++linenum >= sc_height) 717221715Sdelphij break; 718221715Sdelphij pos = position(linenum); 719221715Sdelphij } 720221715Sdelphij } else 721221715Sdelphij { 722221715Sdelphij while (pos == NULL_POSITION) 723221715Sdelphij { 724221715Sdelphij if (--linenum < 0) 725221715Sdelphij break; 726221715Sdelphij pos = position(linenum); 727221715Sdelphij } 728221715Sdelphij } 72960786Sps return (pos); 73060786Sps} 73160786Sps 73260786Sps/* 73360786Sps * Search a subset of the file, specified by start/end position. 73460786Sps */ 73560786Sps static int 73660786Spssearch_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) 73760786Sps POSITION pos; 73860786Sps POSITION endpos; 73960786Sps int search_type; 74060786Sps int matches; 74160786Sps int maxlines; 74260786Sps POSITION *plinepos; 74360786Sps POSITION *pendpos; 74460786Sps{ 74560786Sps char *line; 746173685Sdelphij char *cline; 747170259Sdelphij int line_len; 748128348Stjr LINENUM linenum; 74960786Sps char *sp, *ep; 75060786Sps int line_match; 751128348Stjr int cvt_ops; 752195941Sdelphij int cvt_len; 753195941Sdelphij int *chpos; 75460786Sps POSITION linepos, oldpos; 75560786Sps 75660786Sps linenum = find_linenum(pos); 75760786Sps oldpos = pos; 75860786Sps for (;;) 75960786Sps { 76060786Sps /* 76160786Sps * Get lines until we find a matching one or until 76260786Sps * we hit end-of-file (or beginning-of-file if we're 76360786Sps * going backwards), or until we hit the end position. 76460786Sps */ 76560786Sps if (ABORT_SIGS()) 76660786Sps { 76760786Sps /* 76860786Sps * A signal aborts the search. 76960786Sps */ 77060786Sps return (-1); 77160786Sps } 77260786Sps 77360786Sps if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0) 77460786Sps { 77560786Sps /* 77660786Sps * Reached end position without a match. 77760786Sps */ 77860786Sps if (pendpos != NULL) 77960786Sps *pendpos = pos; 78060786Sps return (matches); 78160786Sps } 78260786Sps if (maxlines > 0) 78360786Sps maxlines--; 78460786Sps 78560786Sps if (search_type & SRCH_FORW) 78660786Sps { 78760786Sps /* 78860786Sps * Read the next line, and save the 78960786Sps * starting position of that line in linepos. 79060786Sps */ 79160786Sps linepos = pos; 792170259Sdelphij pos = forw_raw_line(pos, &line, &line_len); 79360786Sps if (linenum != 0) 79460786Sps linenum++; 79560786Sps } else 79660786Sps { 79760786Sps /* 79860786Sps * Read the previous line and save the 79960786Sps * starting position of that line in linepos. 80060786Sps */ 801170259Sdelphij pos = back_raw_line(pos, &line, &line_len); 80260786Sps linepos = pos; 80360786Sps if (linenum != 0) 80460786Sps linenum--; 80560786Sps } 80660786Sps 80760786Sps if (pos == NULL_POSITION) 80860786Sps { 80960786Sps /* 81060786Sps * Reached EOF/BOF without a match. 81160786Sps */ 81260786Sps if (pendpos != NULL) 81360786Sps *pendpos = oldpos; 81460786Sps return (matches); 81560786Sps } 81660786Sps 81760786Sps /* 81860786Sps * If we're using line numbers, we might as well 81960786Sps * remember the information we have now (the position 82060786Sps * and line number of the current line). 82160786Sps * Don't do it for every line because it slows down 82260786Sps * the search. Remember the line number only if 82360786Sps * we're "far" from the last place we remembered it. 82460786Sps */ 825195941Sdelphij if (linenums && abs((int)(pos - oldpos)) > 2048) 82660786Sps add_lnum(linenum, pos); 82760786Sps oldpos = pos; 82860786Sps 829191930Sdelphij if (is_filtered(linepos)) 830191930Sdelphij continue; 831191930Sdelphij 83260786Sps /* 83360786Sps * If it's a caseless search, convert the line to lowercase. 83460786Sps * If we're doing backspace processing, delete backspaces. 83560786Sps */ 836128348Stjr cvt_ops = get_cvt_ops(); 837195941Sdelphij cvt_len = cvt_length(line_len, cvt_ops); 838195941Sdelphij cline = (char *) ecalloc(1, cvt_len); 839195941Sdelphij chpos = cvt_alloc_chpos(cvt_len); 840195941Sdelphij cvt_text(cline, line, chpos, &line_len, cvt_ops); 84160786Sps 842191930Sdelphij#if HILITE_SEARCH 84360786Sps /* 844191930Sdelphij * Check to see if the line matches the filter pattern. 845191930Sdelphij * If so, add an entry to the filter list. 84660786Sps */ 847195941Sdelphij if ((search_type & SRCH_FIND_ALL) && prev_pattern(&filter_info)) { 848240121Sdelphij int line_filter = match_pattern(info_compiled(&filter_info), filter_info.text, 849195941Sdelphij cline, line_len, &sp, &ep, 0, filter_info.search_type); 850191930Sdelphij if (line_filter) 851191930Sdelphij { 852191930Sdelphij struct hilite *hl = (struct hilite *) 853191930Sdelphij ecalloc(1, sizeof(struct hilite)); 854191930Sdelphij hl->hl_startpos = linepos; 855191930Sdelphij hl->hl_endpos = pos; 856191930Sdelphij add_hilite(&filter_anchor, hl); 857191930Sdelphij } 858173685Sdelphij } 859191930Sdelphij#endif 860191930Sdelphij 86160786Sps /* 862191930Sdelphij * Test the next line to see if we have a match. 863191930Sdelphij * We are successful if we either want a match and got one, 864191930Sdelphij * or if we want a non-match and got one. 86560786Sps */ 866195941Sdelphij if (prev_pattern(&search_info)) 86760786Sps { 868240121Sdelphij line_match = match_pattern(info_compiled(&search_info), search_info.text, 869221715Sdelphij cline, line_len, &sp, &ep, 0, search_type); 87060786Sps if (line_match) 87160786Sps { 87260786Sps /* 873191930Sdelphij * Got a match. 87460786Sps */ 875191930Sdelphij if (search_type & SRCH_FIND_ALL) 876191930Sdelphij { 877191930Sdelphij#if HILITE_SEARCH 878191930Sdelphij /* 879191930Sdelphij * We are supposed to find all matches in the range. 880191930Sdelphij * Just add the matches in this line to the 881191930Sdelphij * hilite list and keep searching. 882191930Sdelphij */ 883195941Sdelphij hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); 884191930Sdelphij#endif 885191930Sdelphij } else if (--matches <= 0) 886191930Sdelphij { 887191930Sdelphij /* 888191930Sdelphij * Found the one match we're looking for. 889191930Sdelphij * Return it. 890191930Sdelphij */ 891191930Sdelphij#if HILITE_SEARCH 892191930Sdelphij if (hilite_search == OPT_ON) 893191930Sdelphij { 894191930Sdelphij /* 895191930Sdelphij * Clear the hilite list and add only 896191930Sdelphij * the matches in this one line. 897191930Sdelphij */ 898191930Sdelphij clr_hilite(); 899195941Sdelphij hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); 900191930Sdelphij } 901191930Sdelphij#endif 902191930Sdelphij free(cline); 903195941Sdelphij free(chpos); 904191930Sdelphij if (plinepos != NULL) 905191930Sdelphij *plinepos = linepos; 906191930Sdelphij return (0); 907191930Sdelphij } 90860786Sps } 90960786Sps } 910191930Sdelphij free(cline); 911195941Sdelphij free(chpos); 91260786Sps } 91360786Sps} 91460786Sps 915191930Sdelphij/* 916170259Sdelphij * search for a pattern in history. If found, compile that pattern. 917170259Sdelphij */ 918170259Sdelphij static int 919170259Sdelphijhist_pattern(search_type) 920170259Sdelphij int search_type; 921170259Sdelphij{ 922170259Sdelphij#if CMD_HISTORY 923170259Sdelphij char *pattern; 924170259Sdelphij 925170259Sdelphij set_mlist(ml_search, 0); 926170259Sdelphij pattern = cmd_lastpattern(); 927170259Sdelphij if (pattern == NULL) 928170259Sdelphij return (0); 929170259Sdelphij 930195941Sdelphij if (set_pattern(&search_info, pattern, search_type) < 0) 931170259Sdelphij return (0); 932170259Sdelphij 933170259Sdelphij#if HILITE_SEARCH 934170259Sdelphij if (hilite_search == OPT_ONPLUS && !hide_hilite) 935170259Sdelphij hilite_screen(); 936170259Sdelphij#endif 937170259Sdelphij 938170259Sdelphij return (1); 939170259Sdelphij#else /* CMD_HISTORY */ 940170259Sdelphij return (0); 941170259Sdelphij#endif /* CMD_HISTORY */ 942170259Sdelphij} 943170259Sdelphij 94460786Sps/* 94560786Sps * Search for the n-th occurrence of a specified pattern, 94660786Sps * either forward or backward. 94760786Sps * Return the number of matches not yet found in this file 94860786Sps * (that is, n minus the number of matches found). 94960786Sps * Return -1 if the search should be aborted. 95060786Sps * Caller may continue the search in another file 95160786Sps * if less than n matches are found in this file. 95260786Sps */ 95360786Sps public int 95460786Spssearch(search_type, pattern, n) 95560786Sps int search_type; 95660786Sps char *pattern; 95760786Sps int n; 95860786Sps{ 95960786Sps POSITION pos; 96060786Sps 96160786Sps if (pattern == NULL || *pattern == '\0') 96260786Sps { 96360786Sps /* 96460786Sps * A null pattern means use the previously compiled pattern. 96560786Sps */ 966221715Sdelphij search_type |= SRCH_AFTER_TARGET; 967195941Sdelphij if (!prev_pattern(&search_info) && !hist_pattern(search_type)) 96860786Sps { 96960786Sps error("No previous regular expression", NULL_PARG); 97060786Sps return (-1); 97160786Sps } 97260786Sps if ((search_type & SRCH_NO_REGEX) != 973195941Sdelphij (search_info.search_type & SRCH_NO_REGEX)) 97460786Sps { 97560786Sps error("Please re-enter search pattern", NULL_PARG); 97660786Sps return -1; 97760786Sps } 97860786Sps#if HILITE_SEARCH 97960786Sps if (hilite_search == OPT_ON) 98060786Sps { 98160786Sps /* 98260786Sps * Erase the highlights currently on screen. 98360786Sps * If the search fails, we'll redisplay them later. 98460786Sps */ 98560786Sps repaint_hilite(0); 98660786Sps } 98760786Sps if (hilite_search == OPT_ONPLUS && hide_hilite) 98860786Sps { 98960786Sps /* 99060786Sps * Highlight any matches currently on screen, 99160786Sps * before we actually start the search. 99260786Sps */ 99360786Sps hide_hilite = 0; 99460786Sps hilite_screen(); 99560786Sps } 99660786Sps hide_hilite = 0; 99760786Sps#endif 99860786Sps } else 99960786Sps { 100060786Sps /* 100160786Sps * Compile the pattern. 100260786Sps */ 1003195941Sdelphij if (set_pattern(&search_info, pattern, search_type) < 0) 100460786Sps return (-1); 100560786Sps#if HILITE_SEARCH 100660786Sps if (hilite_search) 100760786Sps { 100860786Sps /* 100960786Sps * Erase the highlights currently on screen. 101060786Sps * Also permanently delete them from the hilite list. 101160786Sps */ 101260786Sps repaint_hilite(0); 101360786Sps hide_hilite = 0; 101460786Sps clr_hilite(); 101560786Sps } 101660786Sps if (hilite_search == OPT_ONPLUS) 101760786Sps { 101860786Sps /* 101960786Sps * Highlight any matches currently on screen, 102060786Sps * before we actually start the search. 102160786Sps */ 102260786Sps hilite_screen(); 102360786Sps } 102460786Sps#endif 102560786Sps } 102660786Sps 102760786Sps /* 102860786Sps * Figure out where to start the search. 102960786Sps */ 103060786Sps pos = search_pos(search_type); 103160786Sps if (pos == NULL_POSITION) 103260786Sps { 103360786Sps /* 103460786Sps * Can't find anyplace to start searching from. 103560786Sps */ 103660786Sps if (search_type & SRCH_PAST_EOF) 103760786Sps return (n); 103860786Sps /* repaint(); -- why was this here? */ 103960786Sps error("Nothing to search", NULL_PARG); 104060786Sps return (-1); 104160786Sps } 104260786Sps 104360786Sps n = search_range(pos, NULL_POSITION, search_type, n, -1, 104460786Sps &pos, (POSITION*)NULL); 104560786Sps if (n != 0) 104660786Sps { 104760786Sps /* 104860786Sps * Search was unsuccessful. 104960786Sps */ 105060786Sps#if HILITE_SEARCH 105160786Sps if (hilite_search == OPT_ON && n > 0) 105260786Sps /* 105360786Sps * Redisplay old hilites. 105460786Sps */ 105560786Sps repaint_hilite(1); 105660786Sps#endif 105760786Sps return (n); 105860786Sps } 105960786Sps 106060786Sps if (!(search_type & SRCH_NO_MOVE)) 106160786Sps { 106260786Sps /* 106360786Sps * Go to the matching line. 106460786Sps */ 106560786Sps jump_loc(pos, jump_sline); 106660786Sps } 106760786Sps 106860786Sps#if HILITE_SEARCH 106960786Sps if (hilite_search == OPT_ON) 107060786Sps /* 107160786Sps * Display new hilites in the matching line. 107260786Sps */ 107360786Sps repaint_hilite(1); 107460786Sps#endif 107560786Sps return (0); 107660786Sps} 107760786Sps 107860786Sps 107960786Sps#if HILITE_SEARCH 108060786Sps/* 108160786Sps * Prepare hilites in a given range of the file. 108260786Sps * 108360786Sps * The pair (prep_startpos,prep_endpos) delimits a contiguous region 108460786Sps * of the file that has been "prepared"; that is, scanned for matches for 108560786Sps * the current search pattern, and hilites have been created for such matches. 108660786Sps * If prep_startpos == NULL_POSITION, the prep region is empty. 108760786Sps * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 108860786Sps * prep_hilite asks that the range (spos,epos) be covered by the prep region. 108960786Sps */ 109060786Sps public void 109160786Spsprep_hilite(spos, epos, maxlines) 109260786Sps POSITION spos; 109360786Sps POSITION epos; 109460786Sps int maxlines; 109560786Sps{ 109660786Sps POSITION nprep_startpos = prep_startpos; 109760786Sps POSITION nprep_endpos = prep_endpos; 109860786Sps POSITION new_epos; 109960786Sps POSITION max_epos; 110060786Sps int result; 110160786Sps int i; 1102195941Sdelphij 110360786Sps/* 110460786Sps * Search beyond where we're asked to search, so the prep region covers 110560786Sps * more than we need. Do one big search instead of a bunch of small ones. 110660786Sps */ 110760786Sps#define SEARCH_MORE (3*size_linebuf) 110860786Sps 1109195941Sdelphij if (!prev_pattern(&search_info) && !is_filtering()) 111060786Sps return; 111160786Sps 111260786Sps /* 111360786Sps * If we're limited to a max number of lines, figure out the 111460786Sps * file position we should stop at. 111560786Sps */ 111660786Sps if (maxlines < 0) 111760786Sps max_epos = NULL_POSITION; 111860786Sps else 111960786Sps { 112060786Sps max_epos = spos; 112160786Sps for (i = 0; i < maxlines; i++) 1122170259Sdelphij max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL); 112360786Sps } 112460786Sps 112560786Sps /* 112660786Sps * Find two ranges: 112760786Sps * The range that we need to search (spos,epos); and the range that 112860786Sps * the "prep" region will then cover (nprep_startpos,nprep_endpos). 112960786Sps */ 113060786Sps 113160786Sps if (prep_startpos == NULL_POSITION || 113260786Sps (epos != NULL_POSITION && epos < prep_startpos) || 113360786Sps spos > prep_endpos) 113460786Sps { 113560786Sps /* 113660786Sps * New range is not contiguous with old prep region. 113760786Sps * Discard the old prep region and start a new one. 113860786Sps */ 113960786Sps clr_hilite(); 1140191930Sdelphij clr_filter(); 114160786Sps if (epos != NULL_POSITION) 114260786Sps epos += SEARCH_MORE; 114360786Sps nprep_startpos = spos; 114460786Sps } else 114560786Sps { 114660786Sps /* 114760786Sps * New range partially or completely overlaps old prep region. 114860786Sps */ 114960786Sps if (epos == NULL_POSITION) 115060786Sps { 115160786Sps /* 115260786Sps * New range goes to end of file. 115360786Sps */ 115460786Sps ; 115560786Sps } else if (epos > prep_endpos) 115660786Sps { 115760786Sps /* 115860786Sps * New range ends after old prep region. 115960786Sps * Extend prep region to end at end of new range. 116060786Sps */ 116160786Sps epos += SEARCH_MORE; 116260786Sps } else /* (epos <= prep_endpos) */ 116360786Sps { 116460786Sps /* 116560786Sps * New range ends within old prep region. 116660786Sps * Truncate search to end at start of old prep region. 116760786Sps */ 116860786Sps epos = prep_startpos; 116960786Sps } 117060786Sps 117160786Sps if (spos < prep_startpos) 117260786Sps { 117360786Sps /* 117460786Sps * New range starts before old prep region. 117560786Sps * Extend old prep region backwards to start at 117660786Sps * start of new range. 117760786Sps */ 117860786Sps if (spos < SEARCH_MORE) 117960786Sps spos = 0; 118060786Sps else 118160786Sps spos -= SEARCH_MORE; 118260786Sps nprep_startpos = spos; 118360786Sps } else /* (spos >= prep_startpos) */ 118460786Sps { 118560786Sps /* 118660786Sps * New range starts within or after old prep region. 118760786Sps * Trim search to start at end of old prep region. 118860786Sps */ 118960786Sps spos = prep_endpos; 119060786Sps } 119160786Sps } 119260786Sps 119360786Sps if (epos != NULL_POSITION && max_epos != NULL_POSITION && 119460786Sps epos > max_epos) 119560786Sps /* 119660786Sps * Don't go past the max position we're allowed. 119760786Sps */ 119860786Sps epos = max_epos; 119960786Sps 120060786Sps if (epos == NULL_POSITION || epos > spos) 120160786Sps { 1202195941Sdelphij int search_type = SRCH_FORW | SRCH_FIND_ALL; 1203195941Sdelphij search_type |= (search_info.search_type & SRCH_NO_REGEX); 1204195941Sdelphij result = search_range(spos, epos, search_type, 0, 120560786Sps maxlines, (POSITION*)NULL, &new_epos); 120660786Sps if (result < 0) 120760786Sps return; 120860786Sps if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) 120960786Sps nprep_endpos = new_epos; 121060786Sps } 121160786Sps prep_startpos = nprep_startpos; 121260786Sps prep_endpos = nprep_endpos; 121360786Sps} 1214191930Sdelphij 1215191930Sdelphij/* 1216191930Sdelphij * Set the pattern to be used for line filtering. 1217191930Sdelphij */ 1218191930Sdelphij public void 1219191930Sdelphijset_filter_pattern(pattern, search_type) 1220191930Sdelphij char *pattern; 1221191930Sdelphij int search_type; 1222191930Sdelphij{ 1223191930Sdelphij clr_filter(); 1224191930Sdelphij if (pattern == NULL || *pattern == '\0') 1225195941Sdelphij clear_pattern(&filter_info); 1226191930Sdelphij else 1227195941Sdelphij set_pattern(&filter_info, pattern, search_type); 1228191930Sdelphij screen_trashed = 1; 1229191930Sdelphij} 1230191930Sdelphij 1231191930Sdelphij/* 1232191930Sdelphij * Is there a line filter in effect? 1233191930Sdelphij */ 1234191930Sdelphij public int 1235191930Sdelphijis_filtering() 1236191930Sdelphij{ 1237191930Sdelphij if (ch_getflags() & CH_HELPFILE) 1238191930Sdelphij return (0); 1239195941Sdelphij return prev_pattern(&filter_info); 1240191930Sdelphij} 124160786Sps#endif 124260786Sps 124360786Sps#if HAVE_V8_REGCOMP 124460786Sps/* 124560786Sps * This function is called by the V8 regcomp to report 124660786Sps * errors in regular expressions. 124760786Sps */ 124860786Sps void 124960786Spsregerror(s) 125060786Sps char *s; 125160786Sps{ 125260786Sps PARG parg; 125360786Sps 125460786Sps parg.p_string = s; 125560786Sps error("%s", &parg); 125660786Sps} 125760786Sps#endif 125860786Sps 1259