1238730Sdelphij/* 2238730Sdelphij * Copyright (C) 1984-2012 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Routines to search a file for a pattern. 1360786Sps */ 1460786Sps 1560786Sps#include "less.h" 16195941Sdelphij#include "pattern.h" 1760786Sps#include "position.h" 18172471Sdelphij#include "charset.h" 1960786Sps 2060786Sps#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 2160786Sps#define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 2260786Sps 2360786Spsextern int sigs; 2460786Spsextern int how_search; 2560786Spsextern int caseless; 2660786Spsextern int linenums; 2760786Spsextern int sc_height; 2860786Spsextern int jump_sline; 2960786Spsextern int bs_mode; 30128348Stjrextern int ctldisp; 3163131Spsextern int status_col; 32170259Sdelphijextern void * constant ml_search; 3360786Spsextern POSITION start_attnpos; 3460786Spsextern POSITION end_attnpos; 35191930Sdelphijextern int utf_mode; 36191930Sdelphijextern int screen_trashed; 3760786Sps#if HILITE_SEARCH 3860786Spsextern int hilite_search; 3960786Spsextern int size_linebuf; 4060786Spsextern int squished; 4160786Spsextern int can_goto_line; 4260786Spsstatic int hide_hilite; 4360786Spsstatic POSITION prep_startpos; 4460786Spsstatic POSITION prep_endpos; 45195941Sdelphijstatic int is_caseless; 46195941Sdelphijstatic int is_ucase_pattern; 4760786Sps 4860786Spsstruct hilite 4960786Sps{ 5060786Sps struct hilite *hl_next; 5160786Sps POSITION hl_startpos; 5260786Sps POSITION hl_endpos; 5360786Sps}; 5460786Spsstatic struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; 55191930Sdelphijstatic struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION }; 5660786Sps#define hl_first hl_next 5760786Sps#endif 5860786Sps 5960786Sps/* 6060786Sps * These are the static variables that represent the "remembered" 61195941Sdelphij * search pattern and filter pattern. 6260786Sps */ 63195941Sdelphijstruct pattern_info { 64195941Sdelphij DEFINE_PATTERN(compiled); 65195941Sdelphij char* text; 66195941Sdelphij int search_type; 67195941Sdelphij}; 68237613Sdelphij 69237613Sdelphij#if NO_REGEX 70237613Sdelphij#define info_compiled(info) ((void*)0) 71237613Sdelphij#else 72237613Sdelphij#define info_compiled(info) ((info)->compiled) 73237613Sdelphij#endif 74195941Sdelphij 75195941Sdelphijstatic struct pattern_info search_info; 76195941Sdelphijstatic struct pattern_info filter_info; 7760786Sps 78173685Sdelphij/* 79221715Sdelphij * Are there any uppercase letters in this string? 80221715Sdelphij */ 81221715Sdelphij static int 82221715Sdelphijis_ucase(str) 83221715Sdelphij char *str; 84221715Sdelphij{ 85221715Sdelphij char *str_end = str + strlen(str); 86221715Sdelphij LWCHAR ch; 87221715Sdelphij 88221715Sdelphij while (str < str_end) 89221715Sdelphij { 90221715Sdelphij ch = step_char(&str, +1, str_end); 91221715Sdelphij if (IS_UPPER(ch)) 92221715Sdelphij return (1); 93221715Sdelphij } 94221715Sdelphij return (0); 95221715Sdelphij} 96221715Sdelphij 97221715Sdelphij/* 98195941Sdelphij * Compile and save a search pattern. 99173685Sdelphij */ 100173685Sdelphij static int 101195941Sdelphijset_pattern(info, pattern, search_type) 102195941Sdelphij struct pattern_info *info; 103195941Sdelphij char *pattern; 104195941Sdelphij int search_type; 105173685Sdelphij{ 106237613Sdelphij#if !NO_REGEX 107195941Sdelphij if (pattern == NULL) 108237613Sdelphij CLEAR_PATTERN(info->compiled); 109195941Sdelphij else if (compile_pattern(pattern, search_type, &info->compiled) < 0) 110195941Sdelphij return -1; 111237613Sdelphij#endif 112195941Sdelphij /* Pattern compiled successfully; save the text too. */ 113195941Sdelphij if (info->text != NULL) 114195941Sdelphij free(info->text); 115195941Sdelphij info->text = NULL; 116195941Sdelphij if (pattern != NULL) 117195941Sdelphij { 118195941Sdelphij info->text = (char *) ecalloc(1, strlen(pattern)+1); 119195941Sdelphij strcpy(info->text, pattern); 120195941Sdelphij } 121195941Sdelphij info->search_type = search_type; 122221715Sdelphij 123221715Sdelphij /* 124221715Sdelphij * Ignore case if -I is set OR 125221715Sdelphij * -i is set AND the pattern is all lowercase. 126221715Sdelphij */ 127221715Sdelphij is_ucase_pattern = is_ucase(pattern); 128221715Sdelphij if (is_ucase_pattern && caseless != OPT_ONPLUS) 129221715Sdelphij is_caseless = 0; 130221715Sdelphij else 131221715Sdelphij is_caseless = caseless; 132195941Sdelphij return 0; 133173685Sdelphij} 134173685Sdelphij 135173685Sdelphij/* 136195941Sdelphij * Discard a saved pattern. 137173685Sdelphij */ 13860786Sps static void 139195941Sdelphijclear_pattern(info) 140195941Sdelphij struct pattern_info *info; 14160786Sps{ 142195941Sdelphij if (info->text != NULL) 143195941Sdelphij free(info->text); 144195941Sdelphij info->text = NULL; 145237613Sdelphij#if !NO_REGEX 146195941Sdelphij uncompile_pattern(&info->compiled); 147237613Sdelphij#endif 148195941Sdelphij} 14960786Sps 150195941Sdelphij/* 151195941Sdelphij * Initialize saved pattern to nothing. 152195941Sdelphij */ 153195941Sdelphij static void 154195941Sdelphijinit_pattern(info) 155195941Sdelphij struct pattern_info *info; 156195941Sdelphij{ 157195941Sdelphij CLEAR_PATTERN(info->compiled); 158195941Sdelphij info->text = NULL; 159195941Sdelphij info->search_type = 0; 160195941Sdelphij} 161170259Sdelphij 162195941Sdelphij/* 163195941Sdelphij * Initialize search variables. 164195941Sdelphij */ 165195941Sdelphij public void 166195941Sdelphijinit_search() 167195941Sdelphij{ 168195941Sdelphij init_pattern(&search_info); 169195941Sdelphij init_pattern(&filter_info); 17060786Sps} 17160786Sps 17260786Sps/* 173195941Sdelphij * Determine which text conversions to perform before pattern matching. 174128348Stjr */ 175128348Stjr static int 176128348Stjrget_cvt_ops() 177128348Stjr{ 178128348Stjr int ops = 0; 179128348Stjr if (is_caseless || bs_mode == BS_SPECIAL) 180128348Stjr { 181128348Stjr if (is_caseless) 182128348Stjr ops |= CVT_TO_LC; 183128348Stjr if (bs_mode == BS_SPECIAL) 184128348Stjr ops |= CVT_BS; 185128348Stjr if (bs_mode != BS_CONTROL) 186128348Stjr ops |= CVT_CRLF; 187128348Stjr } else if (bs_mode != BS_CONTROL) 188128348Stjr { 189128348Stjr ops |= CVT_CRLF; 190128348Stjr } 191128348Stjr if (ctldisp == OPT_ONPLUS) 192128348Stjr ops |= CVT_ANSI; 193128348Stjr return (ops); 194128348Stjr} 195128348Stjr 196128348Stjr/* 19760786Sps * Is there a previous (remembered) search pattern? 19860786Sps */ 19960786Sps static int 200195941Sdelphijprev_pattern(info) 201195941Sdelphij struct pattern_info *info; 20260786Sps{ 203237613Sdelphij#if !NO_REGEX 204237613Sdelphij if ((info->search_type & SRCH_NO_REGEX) == 0) 205237613Sdelphij return (!is_null_pattern(info->compiled)); 206237613Sdelphij#endif 207237613Sdelphij return (info->text != NULL); 20860786Sps} 20960786Sps 21060786Sps#if HILITE_SEARCH 21160786Sps/* 21260786Sps * Repaint the hilites currently displayed on the screen. 21360786Sps * Repaint each line which contains highlighted text. 21460786Sps * If on==0, force all hilites off. 21560786Sps */ 21660786Sps public void 21760786Spsrepaint_hilite(on) 21860786Sps int on; 21960786Sps{ 22060786Sps int slinenum; 22160786Sps POSITION pos; 22260786Sps POSITION epos; 22360786Sps int save_hide_hilite; 22460786Sps 22560786Sps if (squished) 22660786Sps repaint(); 22760786Sps 22860786Sps save_hide_hilite = hide_hilite; 22960786Sps if (!on) 23060786Sps { 23160786Sps if (hide_hilite) 23260786Sps return; 23360786Sps hide_hilite = 1; 23460786Sps } 23560786Sps 23660786Sps if (!can_goto_line) 23760786Sps { 23860786Sps repaint(); 23960786Sps hide_hilite = save_hide_hilite; 24060786Sps return; 24160786Sps } 24260786Sps 24360786Sps for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 24460786Sps { 24560786Sps pos = position(slinenum); 24660786Sps if (pos == NULL_POSITION) 24760786Sps continue; 24860786Sps epos = position(slinenum+1); 249195941Sdelphij (void) forw_line(pos); 250195941Sdelphij goto_line(slinenum); 251195941Sdelphij put_line(); 25260786Sps } 253221715Sdelphij lower_left(); 25460786Sps hide_hilite = save_hide_hilite; 25560786Sps} 25660786Sps 25760786Sps/* 25860786Sps * Clear the attn hilite. 25960786Sps */ 26060786Sps public void 26160786Spsclear_attn() 26260786Sps{ 26360786Sps int slinenum; 26460786Sps POSITION old_start_attnpos; 26560786Sps POSITION old_end_attnpos; 26660786Sps POSITION pos; 26760786Sps POSITION epos; 268170898Sdelphij int moved = 0; 26960786Sps 27060786Sps if (start_attnpos == NULL_POSITION) 27160786Sps return; 27260786Sps old_start_attnpos = start_attnpos; 27360786Sps old_end_attnpos = end_attnpos; 27460786Sps start_attnpos = end_attnpos = NULL_POSITION; 27560786Sps 27660786Sps if (!can_goto_line) 27760786Sps { 27860786Sps repaint(); 27960786Sps return; 28060786Sps } 28160786Sps if (squished) 28260786Sps repaint(); 28360786Sps 28460786Sps for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) 28560786Sps { 28660786Sps pos = position(slinenum); 28760786Sps if (pos == NULL_POSITION) 28860786Sps continue; 28960786Sps epos = position(slinenum+1); 29060786Sps if (pos < old_end_attnpos && 29160786Sps (epos == NULL_POSITION || epos > old_start_attnpos)) 29260786Sps { 29360786Sps (void) forw_line(pos); 29460786Sps goto_line(slinenum); 29560786Sps put_line(); 296170898Sdelphij moved = 1; 29760786Sps } 29860786Sps } 299170898Sdelphij if (moved) 300170898Sdelphij lower_left(); 30160786Sps} 30260786Sps#endif 30360786Sps 30460786Sps/* 30560786Sps * Hide search string highlighting. 30660786Sps */ 30760786Sps public void 30860786Spsundo_search() 30960786Sps{ 310195941Sdelphij if (!prev_pattern(&search_info)) 31160786Sps { 31260786Sps error("No previous regular expression", NULL_PARG); 31360786Sps return; 31460786Sps } 31560786Sps#if HILITE_SEARCH 31660786Sps hide_hilite = !hide_hilite; 31760786Sps repaint_hilite(1); 31860786Sps#endif 31960786Sps} 32060786Sps 32160786Sps#if HILITE_SEARCH 32260786Sps/* 32360786Sps * Clear the hilite list. 32460786Sps */ 32560786Sps public void 326191930Sdelphijclr_hlist(anchor) 327191930Sdelphij struct hilite *anchor; 32860786Sps{ 32960786Sps struct hilite *hl; 33060786Sps struct hilite *nexthl; 33160786Sps 332191930Sdelphij for (hl = anchor->hl_first; hl != NULL; hl = nexthl) 33360786Sps { 33460786Sps nexthl = hl->hl_next; 33560786Sps free((void*)hl); 33660786Sps } 337191930Sdelphij anchor->hl_first = NULL; 33860786Sps prep_startpos = prep_endpos = NULL_POSITION; 33960786Sps} 34060786Sps 341191930Sdelphij public void 342191930Sdelphijclr_hilite() 343191930Sdelphij{ 344191930Sdelphij clr_hlist(&hilite_anchor); 345191930Sdelphij} 346191930Sdelphij 347191930Sdelphij public void 348191930Sdelphijclr_filter() 349191930Sdelphij{ 350191930Sdelphij clr_hlist(&filter_anchor); 351191930Sdelphij} 352191930Sdelphij 35360786Sps/* 35460786Sps * Should any characters in a specified range be highlighted? 355161478Sdelphij */ 356161478Sdelphij static int 357161478Sdelphijis_hilited_range(pos, epos) 358161478Sdelphij POSITION pos; 359161478Sdelphij POSITION epos; 360161478Sdelphij{ 361161478Sdelphij struct hilite *hl; 362161478Sdelphij 363161478Sdelphij /* 364161478Sdelphij * Look at each highlight and see if any part of it falls in the range. 365161478Sdelphij */ 366161478Sdelphij for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) 367161478Sdelphij { 368161478Sdelphij if (hl->hl_endpos > pos && 369161478Sdelphij (epos == NULL_POSITION || epos > hl->hl_startpos)) 370161478Sdelphij return (1); 371161478Sdelphij } 372161478Sdelphij return (0); 373161478Sdelphij} 374161478Sdelphij 375191930Sdelphij/* 376191930Sdelphij * Is a line "filtered" -- that is, should it be hidden? 377191930Sdelphij */ 378191930Sdelphij public int 379191930Sdelphijis_filtered(pos) 380191930Sdelphij POSITION pos; 381191930Sdelphij{ 382191930Sdelphij struct hilite *hl; 383191930Sdelphij 384191930Sdelphij if (ch_getflags() & CH_HELPFILE) 385191930Sdelphij return (0); 386191930Sdelphij 387191930Sdelphij /* 388191930Sdelphij * Look at each filter and see if the start position 389191930Sdelphij * equals the start position of the line. 390191930Sdelphij */ 391191930Sdelphij for (hl = filter_anchor.hl_first; hl != NULL; hl = hl->hl_next) 392191930Sdelphij { 393191930Sdelphij if (hl->hl_startpos == pos) 394191930Sdelphij return (1); 395191930Sdelphij } 396191930Sdelphij return (0); 397191930Sdelphij} 398191930Sdelphij 399161478Sdelphij/* 400161478Sdelphij * Should any characters in a specified range be highlighted? 40160786Sps * If nohide is nonzero, don't consider hide_hilite. 40260786Sps */ 40360786Sps public int 404161478Sdelphijis_hilited(pos, epos, nohide, p_matches) 40560786Sps POSITION pos; 40660786Sps POSITION epos; 40760786Sps int nohide; 408161478Sdelphij int *p_matches; 40960786Sps{ 410161478Sdelphij int match; 41160786Sps 412161478Sdelphij if (p_matches != NULL) 413161478Sdelphij *p_matches = 0; 414161478Sdelphij 41563131Sps if (!status_col && 41663131Sps start_attnpos != NULL_POSITION && 41760786Sps pos < end_attnpos && 41860786Sps (epos == NULL_POSITION || epos > start_attnpos)) 41960786Sps /* 42060786Sps * The attn line overlaps this range. 42160786Sps */ 42260786Sps return (1); 42360786Sps 424161478Sdelphij match = is_hilited_range(pos, epos); 425161478Sdelphij if (!match) 426161478Sdelphij return (0); 427161478Sdelphij 428161478Sdelphij if (p_matches != NULL) 429161478Sdelphij /* 430161478Sdelphij * Report matches, even if we're hiding highlights. 431161478Sdelphij */ 432161478Sdelphij *p_matches = 1; 433161478Sdelphij 43460786Sps if (hilite_search == 0) 43560786Sps /* 43660786Sps * Not doing highlighting. 43760786Sps */ 43860786Sps return (0); 43960786Sps 44060786Sps if (!nohide && hide_hilite) 44160786Sps /* 44260786Sps * Highlighting is hidden. 44360786Sps */ 44460786Sps return (0); 44560786Sps 446161478Sdelphij return (1); 44760786Sps} 44860786Sps 44960786Sps/* 45060786Sps * Add a new hilite to a hilite list. 45160786Sps */ 45260786Sps static void 45360786Spsadd_hilite(anchor, hl) 45460786Sps struct hilite *anchor; 45560786Sps struct hilite *hl; 45660786Sps{ 45760786Sps struct hilite *ihl; 45860786Sps 45960786Sps /* 46060786Sps * Hilites are sorted in the list; find where new one belongs. 46160786Sps * Insert new one after ihl. 46260786Sps */ 46360786Sps for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) 46460786Sps { 46560786Sps if (ihl->hl_next->hl_startpos > hl->hl_startpos) 46660786Sps break; 46760786Sps } 46860786Sps 46960786Sps /* 47060786Sps * Truncate hilite so it doesn't overlap any existing ones 47160786Sps * above and below it. 47260786Sps */ 47360786Sps if (ihl != anchor) 47460786Sps hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); 47560786Sps if (ihl->hl_next != NULL) 47660786Sps hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos); 47760786Sps if (hl->hl_startpos >= hl->hl_endpos) 47860786Sps { 47960786Sps /* 48060786Sps * Hilite was truncated out of existence. 48160786Sps */ 48260786Sps free(hl); 48360786Sps return; 48460786Sps } 48560786Sps hl->hl_next = ihl->hl_next; 48660786Sps ihl->hl_next = hl; 48760786Sps} 48860786Sps 48960786Sps/* 490237613Sdelphij * Hilight every character in a range of displayed characters. 491237613Sdelphij */ 492237613Sdelphij static void 493237613Sdelphijcreate_hilites(linepos, start_index, end_index, chpos) 494237613Sdelphij POSITION linepos; 495237613Sdelphij int start_index; 496237613Sdelphij int end_index; 497237613Sdelphij int *chpos; 498237613Sdelphij{ 499237613Sdelphij struct hilite *hl; 500237613Sdelphij int i; 501237613Sdelphij 502237613Sdelphij /* Start the first hilite. */ 503237613Sdelphij hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 504237613Sdelphij hl->hl_startpos = linepos + chpos[start_index]; 505237613Sdelphij 506237613Sdelphij /* 507237613Sdelphij * Step through the displayed chars. 508237613Sdelphij * If the source position (before cvt) of the char is one more 509237613Sdelphij * than the source pos of the previous char (the usual case), 510237613Sdelphij * just increase the size of the current hilite by one. 511237613Sdelphij * Otherwise (there are backspaces or something involved), 512237613Sdelphij * finish the current hilite and start a new one. 513237613Sdelphij */ 514237613Sdelphij for (i = start_index+1; i <= end_index; i++) 515237613Sdelphij { 516237613Sdelphij if (chpos[i] != chpos[i-1] + 1 || i == end_index) 517237613Sdelphij { 518237613Sdelphij hl->hl_endpos = linepos + chpos[i-1] + 1; 519237613Sdelphij add_hilite(&hilite_anchor, hl); 520237613Sdelphij /* Start new hilite unless this is the last char. */ 521237613Sdelphij if (i < end_index) 522237613Sdelphij { 523237613Sdelphij hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); 524237613Sdelphij hl->hl_startpos = linepos + chpos[i]; 525237613Sdelphij } 526237613Sdelphij } 527237613Sdelphij } 528237613Sdelphij} 529237613Sdelphij 530237613Sdelphij/* 53160786Sps * Make a hilite for each string in a physical line which matches 53260786Sps * the current pattern. 53360786Sps * sp,ep delimit the first match already found. 53460786Sps */ 53560786Sps static void 536195941Sdelphijhilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops) 53760786Sps POSITION linepos; 53860786Sps char *line; 539170259Sdelphij int line_len; 540195941Sdelphij int *chpos; 54160786Sps char *sp; 54260786Sps char *ep; 543128348Stjr int cvt_ops; 54460786Sps{ 54560786Sps char *searchp; 546170259Sdelphij char *line_end = line + line_len; 54760786Sps 54860786Sps if (sp == NULL || ep == NULL) 54960786Sps return; 55060786Sps /* 55160786Sps * sp and ep delimit the first match in the line. 55260786Sps * Mark the corresponding file positions, then 55360786Sps * look for further matches and mark them. 55460786Sps * {{ This technique, of calling match_pattern on subsequent 55560786Sps * substrings of the line, may mark more than is correct 55660786Sps * if the pattern starts with "^". This bug is fixed 55760786Sps * for those regex functions that accept a notbol parameter 558170259Sdelphij * (currently POSIX, PCRE and V8-with-regexec2). }} 55960786Sps */ 56060786Sps searchp = line; 56160786Sps do { 562237613Sdelphij create_hilites(linepos, sp-line, ep-line, chpos); 56360786Sps /* 56460786Sps * If we matched more than zero characters, 56560786Sps * move to the first char after the string we matched. 56660786Sps * If we matched zero, just move to the next char. 56760786Sps */ 56860786Sps if (ep > searchp) 56960786Sps searchp = ep; 570170259Sdelphij else if (searchp != line_end) 57160786Sps searchp++; 57260786Sps else /* end of line */ 57360786Sps break; 574237613Sdelphij } while (match_pattern(info_compiled(&search_info), search_info.text, 575195941Sdelphij searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type)); 57660786Sps} 57760786Sps#endif 57860786Sps 57960786Sps/* 58060786Sps * Change the caseless-ness of searches. 58160786Sps * Updates the internal search state to reflect a change in the -i flag. 58260786Sps */ 58360786Sps public void 58460786Spschg_caseless() 58560786Sps{ 58660786Sps if (!is_ucase_pattern) 58760786Sps /* 58860786Sps * Pattern did not have uppercase. 58960786Sps * Just set the search caselessness to the global caselessness. 59060786Sps */ 59160786Sps is_caseless = caseless; 59260786Sps else 59360786Sps /* 59460786Sps * Pattern did have uppercase. 59560786Sps * Discard the pattern; we can't change search caselessness now. 59660786Sps */ 597195941Sdelphij clear_pattern(&search_info); 59860786Sps} 59960786Sps 60060786Sps#if HILITE_SEARCH 60160786Sps/* 60260786Sps * Find matching text which is currently on screen and highlight it. 60360786Sps */ 60460786Sps static void 60560786Spshilite_screen() 60660786Sps{ 60760786Sps struct scrpos scrpos; 60860786Sps 60960786Sps get_scrpos(&scrpos); 61060786Sps if (scrpos.pos == NULL_POSITION) 61160786Sps return; 61260786Sps prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 61360786Sps repaint_hilite(1); 61460786Sps} 61560786Sps 61660786Sps/* 61760786Sps * Change highlighting parameters. 61860786Sps */ 61960786Sps public void 62060786Spschg_hilite() 62160786Sps{ 62260786Sps /* 62360786Sps * Erase any highlights currently on screen. 62460786Sps */ 62560786Sps clr_hilite(); 62660786Sps hide_hilite = 0; 62760786Sps 62860786Sps if (hilite_search == OPT_ONPLUS) 62960786Sps /* 63060786Sps * Display highlights. 63160786Sps */ 63260786Sps hilite_screen(); 63360786Sps} 63460786Sps#endif 63560786Sps 63660786Sps/* 63760786Sps * Figure out where to start a search. 63860786Sps */ 63960786Sps static POSITION 64060786Spssearch_pos(search_type) 64160786Sps int search_type; 64260786Sps{ 64360786Sps POSITION pos; 64460786Sps int linenum; 64560786Sps 64660786Sps if (empty_screen()) 64760786Sps { 64860786Sps /* 64960786Sps * Start at the beginning (or end) of the file. 65060786Sps * The empty_screen() case is mainly for 65160786Sps * command line initiated searches; 65260786Sps * for example, "+/xyz" on the command line. 65360786Sps * Also for multi-file (SRCH_PAST_EOF) searches. 65460786Sps */ 65560786Sps if (search_type & SRCH_FORW) 65660786Sps { 657221715Sdelphij pos = ch_zero(); 65860786Sps } else 65960786Sps { 66060786Sps pos = ch_length(); 66160786Sps if (pos == NULL_POSITION) 66260786Sps { 66360786Sps (void) ch_end_seek(); 66460786Sps pos = ch_length(); 66560786Sps } 66660786Sps } 667221715Sdelphij linenum = 0; 668221715Sdelphij } else 66960786Sps { 670221715Sdelphij int add_one = 0; 671221715Sdelphij 672221715Sdelphij if (how_search == OPT_ON) 67360786Sps { 674221715Sdelphij /* 675221715Sdelphij * Search does not include current screen. 676221715Sdelphij */ 677221715Sdelphij if (search_type & SRCH_FORW) 678221715Sdelphij linenum = BOTTOM_PLUS_ONE; 679221715Sdelphij else 680221715Sdelphij linenum = TOP; 681221715Sdelphij } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) 682221715Sdelphij { 683221715Sdelphij /* 684221715Sdelphij * Search includes all of displayed screen. 685221715Sdelphij */ 686221715Sdelphij if (search_type & SRCH_FORW) 687221715Sdelphij linenum = TOP; 688221715Sdelphij else 689221715Sdelphij linenum = BOTTOM_PLUS_ONE; 69060786Sps } else 69160786Sps { 692221715Sdelphij /* 693221715Sdelphij * Search includes the part of current screen beyond the jump target. 694221715Sdelphij * It starts at the jump target (if searching backwards), 695221715Sdelphij * or at the jump target plus one (if forwards). 696221715Sdelphij */ 697221715Sdelphij linenum = jump_sline; 698221715Sdelphij if (search_type & SRCH_FORW) 699221715Sdelphij add_one = 1; 70060786Sps } 701221715Sdelphij linenum = adjsline(linenum); 702221715Sdelphij pos = position(linenum); 703221715Sdelphij if (add_one) 704221715Sdelphij pos = forw_raw_line(pos, (char **)NULL, (int *)NULL); 70560786Sps } 706221715Sdelphij 707221715Sdelphij /* 708221715Sdelphij * If the line is empty, look around for a plausible starting place. 709221715Sdelphij */ 710221715Sdelphij if (search_type & SRCH_FORW) 711221715Sdelphij { 712221715Sdelphij while (pos == NULL_POSITION) 713221715Sdelphij { 714221715Sdelphij if (++linenum >= sc_height) 715221715Sdelphij break; 716221715Sdelphij pos = position(linenum); 717221715Sdelphij } 718221715Sdelphij } else 719221715Sdelphij { 720221715Sdelphij while (pos == NULL_POSITION) 721221715Sdelphij { 722221715Sdelphij if (--linenum < 0) 723221715Sdelphij break; 724221715Sdelphij pos = position(linenum); 725221715Sdelphij } 726221715Sdelphij } 72760786Sps return (pos); 72860786Sps} 72960786Sps 73060786Sps/* 73160786Sps * Search a subset of the file, specified by start/end position. 73260786Sps */ 73360786Sps static int 73460786Spssearch_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) 73560786Sps POSITION pos; 73660786Sps POSITION endpos; 73760786Sps int search_type; 73860786Sps int matches; 73960786Sps int maxlines; 74060786Sps POSITION *plinepos; 74160786Sps POSITION *pendpos; 74260786Sps{ 74360786Sps char *line; 744173685Sdelphij char *cline; 745170259Sdelphij int line_len; 746128348Stjr LINENUM linenum; 74760786Sps char *sp, *ep; 74860786Sps int line_match; 749128348Stjr int cvt_ops; 750195941Sdelphij int cvt_len; 751195941Sdelphij int *chpos; 75260786Sps POSITION linepos, oldpos; 75360786Sps 75460786Sps linenum = find_linenum(pos); 75560786Sps oldpos = pos; 75660786Sps for (;;) 75760786Sps { 75860786Sps /* 75960786Sps * Get lines until we find a matching one or until 76060786Sps * we hit end-of-file (or beginning-of-file if we're 76160786Sps * going backwards), or until we hit the end position. 76260786Sps */ 76360786Sps if (ABORT_SIGS()) 76460786Sps { 76560786Sps /* 76660786Sps * A signal aborts the search. 76760786Sps */ 76860786Sps return (-1); 76960786Sps } 77060786Sps 77160786Sps if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0) 77260786Sps { 77360786Sps /* 77460786Sps * Reached end position without a match. 77560786Sps */ 77660786Sps if (pendpos != NULL) 77760786Sps *pendpos = pos; 77860786Sps return (matches); 77960786Sps } 78060786Sps if (maxlines > 0) 78160786Sps maxlines--; 78260786Sps 78360786Sps if (search_type & SRCH_FORW) 78460786Sps { 78560786Sps /* 78660786Sps * Read the next line, and save the 78760786Sps * starting position of that line in linepos. 78860786Sps */ 78960786Sps linepos = pos; 790170259Sdelphij pos = forw_raw_line(pos, &line, &line_len); 79160786Sps if (linenum != 0) 79260786Sps linenum++; 79360786Sps } else 79460786Sps { 79560786Sps /* 79660786Sps * Read the previous line and save the 79760786Sps * starting position of that line in linepos. 79860786Sps */ 799170259Sdelphij pos = back_raw_line(pos, &line, &line_len); 80060786Sps linepos = pos; 80160786Sps if (linenum != 0) 80260786Sps linenum--; 80360786Sps } 80460786Sps 80560786Sps if (pos == NULL_POSITION) 80660786Sps { 80760786Sps /* 80860786Sps * Reached EOF/BOF without a match. 80960786Sps */ 81060786Sps if (pendpos != NULL) 81160786Sps *pendpos = oldpos; 81260786Sps return (matches); 81360786Sps } 81460786Sps 81560786Sps /* 81660786Sps * If we're using line numbers, we might as well 81760786Sps * remember the information we have now (the position 81860786Sps * and line number of the current line). 81960786Sps * Don't do it for every line because it slows down 82060786Sps * the search. Remember the line number only if 82160786Sps * we're "far" from the last place we remembered it. 82260786Sps */ 823195941Sdelphij if (linenums && abs((int)(pos - oldpos)) > 2048) 82460786Sps add_lnum(linenum, pos); 82560786Sps oldpos = pos; 82660786Sps 827191930Sdelphij if (is_filtered(linepos)) 828191930Sdelphij continue; 829191930Sdelphij 83060786Sps /* 83160786Sps * If it's a caseless search, convert the line to lowercase. 83260786Sps * If we're doing backspace processing, delete backspaces. 83360786Sps */ 834128348Stjr cvt_ops = get_cvt_ops(); 835195941Sdelphij cvt_len = cvt_length(line_len, cvt_ops); 836195941Sdelphij cline = (char *) ecalloc(1, cvt_len); 837195941Sdelphij chpos = cvt_alloc_chpos(cvt_len); 838195941Sdelphij cvt_text(cline, line, chpos, &line_len, cvt_ops); 83960786Sps 840191930Sdelphij#if HILITE_SEARCH 84160786Sps /* 842191930Sdelphij * Check to see if the line matches the filter pattern. 843191930Sdelphij * If so, add an entry to the filter list. 84460786Sps */ 845195941Sdelphij if ((search_type & SRCH_FIND_ALL) && prev_pattern(&filter_info)) { 846237613Sdelphij int line_filter = match_pattern(info_compiled(&filter_info), filter_info.text, 847195941Sdelphij cline, line_len, &sp, &ep, 0, filter_info.search_type); 848191930Sdelphij if (line_filter) 849191930Sdelphij { 850191930Sdelphij struct hilite *hl = (struct hilite *) 851191930Sdelphij ecalloc(1, sizeof(struct hilite)); 852191930Sdelphij hl->hl_startpos = linepos; 853191930Sdelphij hl->hl_endpos = pos; 854191930Sdelphij add_hilite(&filter_anchor, hl); 855191930Sdelphij } 856173685Sdelphij } 857191930Sdelphij#endif 858191930Sdelphij 85960786Sps /* 860191930Sdelphij * Test the next line to see if we have a match. 861191930Sdelphij * We are successful if we either want a match and got one, 862191930Sdelphij * or if we want a non-match and got one. 86360786Sps */ 864195941Sdelphij if (prev_pattern(&search_info)) 86560786Sps { 866237613Sdelphij line_match = match_pattern(info_compiled(&search_info), search_info.text, 867221715Sdelphij cline, line_len, &sp, &ep, 0, search_type); 86860786Sps if (line_match) 86960786Sps { 87060786Sps /* 871191930Sdelphij * Got a match. 87260786Sps */ 873191930Sdelphij if (search_type & SRCH_FIND_ALL) 874191930Sdelphij { 875191930Sdelphij#if HILITE_SEARCH 876191930Sdelphij /* 877191930Sdelphij * We are supposed to find all matches in the range. 878191930Sdelphij * Just add the matches in this line to the 879191930Sdelphij * hilite list and keep searching. 880191930Sdelphij */ 881195941Sdelphij hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); 882191930Sdelphij#endif 883191930Sdelphij } else if (--matches <= 0) 884191930Sdelphij { 885191930Sdelphij /* 886191930Sdelphij * Found the one match we're looking for. 887191930Sdelphij * Return it. 888191930Sdelphij */ 889191930Sdelphij#if HILITE_SEARCH 890191930Sdelphij if (hilite_search == OPT_ON) 891191930Sdelphij { 892191930Sdelphij /* 893191930Sdelphij * Clear the hilite list and add only 894191930Sdelphij * the matches in this one line. 895191930Sdelphij */ 896191930Sdelphij clr_hilite(); 897195941Sdelphij hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); 898191930Sdelphij } 899191930Sdelphij#endif 900191930Sdelphij free(cline); 901195941Sdelphij free(chpos); 902191930Sdelphij if (plinepos != NULL) 903191930Sdelphij *plinepos = linepos; 904191930Sdelphij return (0); 905191930Sdelphij } 90660786Sps } 90760786Sps } 908191930Sdelphij free(cline); 909195941Sdelphij free(chpos); 91060786Sps } 91160786Sps} 91260786Sps 913191930Sdelphij/* 914170259Sdelphij * search for a pattern in history. If found, compile that pattern. 915170259Sdelphij */ 916170259Sdelphij static int 917170259Sdelphijhist_pattern(search_type) 918170259Sdelphij int search_type; 919170259Sdelphij{ 920170259Sdelphij#if CMD_HISTORY 921170259Sdelphij char *pattern; 922170259Sdelphij 923170259Sdelphij set_mlist(ml_search, 0); 924170259Sdelphij pattern = cmd_lastpattern(); 925170259Sdelphij if (pattern == NULL) 926170259Sdelphij return (0); 927170259Sdelphij 928195941Sdelphij if (set_pattern(&search_info, pattern, search_type) < 0) 929170259Sdelphij return (0); 930170259Sdelphij 931170259Sdelphij#if HILITE_SEARCH 932170259Sdelphij if (hilite_search == OPT_ONPLUS && !hide_hilite) 933170259Sdelphij hilite_screen(); 934170259Sdelphij#endif 935170259Sdelphij 936170259Sdelphij return (1); 937170259Sdelphij#else /* CMD_HISTORY */ 938170259Sdelphij return (0); 939170259Sdelphij#endif /* CMD_HISTORY */ 940170259Sdelphij} 941170259Sdelphij 94260786Sps/* 94360786Sps * Search for the n-th occurrence of a specified pattern, 94460786Sps * either forward or backward. 94560786Sps * Return the number of matches not yet found in this file 94660786Sps * (that is, n minus the number of matches found). 94760786Sps * Return -1 if the search should be aborted. 94860786Sps * Caller may continue the search in another file 94960786Sps * if less than n matches are found in this file. 95060786Sps */ 95160786Sps public int 95260786Spssearch(search_type, pattern, n) 95360786Sps int search_type; 95460786Sps char *pattern; 95560786Sps int n; 95660786Sps{ 95760786Sps POSITION pos; 95860786Sps 95960786Sps if (pattern == NULL || *pattern == '\0') 96060786Sps { 96160786Sps /* 96260786Sps * A null pattern means use the previously compiled pattern. 96360786Sps */ 964221715Sdelphij search_type |= SRCH_AFTER_TARGET; 965195941Sdelphij if (!prev_pattern(&search_info) && !hist_pattern(search_type)) 96660786Sps { 96760786Sps error("No previous regular expression", NULL_PARG); 96860786Sps return (-1); 96960786Sps } 97060786Sps if ((search_type & SRCH_NO_REGEX) != 971195941Sdelphij (search_info.search_type & SRCH_NO_REGEX)) 97260786Sps { 97360786Sps error("Please re-enter search pattern", NULL_PARG); 97460786Sps return -1; 97560786Sps } 97660786Sps#if HILITE_SEARCH 97760786Sps if (hilite_search == OPT_ON) 97860786Sps { 97960786Sps /* 98060786Sps * Erase the highlights currently on screen. 98160786Sps * If the search fails, we'll redisplay them later. 98260786Sps */ 98360786Sps repaint_hilite(0); 98460786Sps } 98560786Sps if (hilite_search == OPT_ONPLUS && hide_hilite) 98660786Sps { 98760786Sps /* 98860786Sps * Highlight any matches currently on screen, 98960786Sps * before we actually start the search. 99060786Sps */ 99160786Sps hide_hilite = 0; 99260786Sps hilite_screen(); 99360786Sps } 99460786Sps hide_hilite = 0; 99560786Sps#endif 99660786Sps } else 99760786Sps { 99860786Sps /* 99960786Sps * Compile the pattern. 100060786Sps */ 1001195941Sdelphij if (set_pattern(&search_info, pattern, search_type) < 0) 100260786Sps return (-1); 100360786Sps#if HILITE_SEARCH 100460786Sps if (hilite_search) 100560786Sps { 100660786Sps /* 100760786Sps * Erase the highlights currently on screen. 100860786Sps * Also permanently delete them from the hilite list. 100960786Sps */ 101060786Sps repaint_hilite(0); 101160786Sps hide_hilite = 0; 101260786Sps clr_hilite(); 101360786Sps } 101460786Sps if (hilite_search == OPT_ONPLUS) 101560786Sps { 101660786Sps /* 101760786Sps * Highlight any matches currently on screen, 101860786Sps * before we actually start the search. 101960786Sps */ 102060786Sps hilite_screen(); 102160786Sps } 102260786Sps#endif 102360786Sps } 102460786Sps 102560786Sps /* 102660786Sps * Figure out where to start the search. 102760786Sps */ 102860786Sps pos = search_pos(search_type); 102960786Sps if (pos == NULL_POSITION) 103060786Sps { 103160786Sps /* 103260786Sps * Can't find anyplace to start searching from. 103360786Sps */ 103460786Sps if (search_type & SRCH_PAST_EOF) 103560786Sps return (n); 103660786Sps /* repaint(); -- why was this here? */ 103760786Sps error("Nothing to search", NULL_PARG); 103860786Sps return (-1); 103960786Sps } 104060786Sps 104160786Sps n = search_range(pos, NULL_POSITION, search_type, n, -1, 104260786Sps &pos, (POSITION*)NULL); 104360786Sps if (n != 0) 104460786Sps { 104560786Sps /* 104660786Sps * Search was unsuccessful. 104760786Sps */ 104860786Sps#if HILITE_SEARCH 104960786Sps if (hilite_search == OPT_ON && n > 0) 105060786Sps /* 105160786Sps * Redisplay old hilites. 105260786Sps */ 105360786Sps repaint_hilite(1); 105460786Sps#endif 105560786Sps return (n); 105660786Sps } 105760786Sps 105860786Sps if (!(search_type & SRCH_NO_MOVE)) 105960786Sps { 106060786Sps /* 106160786Sps * Go to the matching line. 106260786Sps */ 106360786Sps jump_loc(pos, jump_sline); 106460786Sps } 106560786Sps 106660786Sps#if HILITE_SEARCH 106760786Sps if (hilite_search == OPT_ON) 106860786Sps /* 106960786Sps * Display new hilites in the matching line. 107060786Sps */ 107160786Sps repaint_hilite(1); 107260786Sps#endif 107360786Sps return (0); 107460786Sps} 107560786Sps 107660786Sps 107760786Sps#if HILITE_SEARCH 107860786Sps/* 107960786Sps * Prepare hilites in a given range of the file. 108060786Sps * 108160786Sps * The pair (prep_startpos,prep_endpos) delimits a contiguous region 108260786Sps * of the file that has been "prepared"; that is, scanned for matches for 108360786Sps * the current search pattern, and hilites have been created for such matches. 108460786Sps * If prep_startpos == NULL_POSITION, the prep region is empty. 108560786Sps * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 108660786Sps * prep_hilite asks that the range (spos,epos) be covered by the prep region. 108760786Sps */ 108860786Sps public void 108960786Spsprep_hilite(spos, epos, maxlines) 109060786Sps POSITION spos; 109160786Sps POSITION epos; 109260786Sps int maxlines; 109360786Sps{ 109460786Sps POSITION nprep_startpos = prep_startpos; 109560786Sps POSITION nprep_endpos = prep_endpos; 109660786Sps POSITION new_epos; 109760786Sps POSITION max_epos; 109860786Sps int result; 109960786Sps int i; 1100195941Sdelphij 110160786Sps/* 110260786Sps * Search beyond where we're asked to search, so the prep region covers 110360786Sps * more than we need. Do one big search instead of a bunch of small ones. 110460786Sps */ 110560786Sps#define SEARCH_MORE (3*size_linebuf) 110660786Sps 1107195941Sdelphij if (!prev_pattern(&search_info) && !is_filtering()) 110860786Sps return; 110960786Sps 111060786Sps /* 111160786Sps * If we're limited to a max number of lines, figure out the 111260786Sps * file position we should stop at. 111360786Sps */ 111460786Sps if (maxlines < 0) 111560786Sps max_epos = NULL_POSITION; 111660786Sps else 111760786Sps { 111860786Sps max_epos = spos; 111960786Sps for (i = 0; i < maxlines; i++) 1120170259Sdelphij max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL); 112160786Sps } 112260786Sps 112360786Sps /* 112460786Sps * Find two ranges: 112560786Sps * The range that we need to search (spos,epos); and the range that 112660786Sps * the "prep" region will then cover (nprep_startpos,nprep_endpos). 112760786Sps */ 112860786Sps 112960786Sps if (prep_startpos == NULL_POSITION || 113060786Sps (epos != NULL_POSITION && epos < prep_startpos) || 113160786Sps spos > prep_endpos) 113260786Sps { 113360786Sps /* 113460786Sps * New range is not contiguous with old prep region. 113560786Sps * Discard the old prep region and start a new one. 113660786Sps */ 113760786Sps clr_hilite(); 1138191930Sdelphij clr_filter(); 113960786Sps if (epos != NULL_POSITION) 114060786Sps epos += SEARCH_MORE; 114160786Sps nprep_startpos = spos; 114260786Sps } else 114360786Sps { 114460786Sps /* 114560786Sps * New range partially or completely overlaps old prep region. 114660786Sps */ 114760786Sps if (epos == NULL_POSITION) 114860786Sps { 114960786Sps /* 115060786Sps * New range goes to end of file. 115160786Sps */ 115260786Sps ; 115360786Sps } else if (epos > prep_endpos) 115460786Sps { 115560786Sps /* 115660786Sps * New range ends after old prep region. 115760786Sps * Extend prep region to end at end of new range. 115860786Sps */ 115960786Sps epos += SEARCH_MORE; 116060786Sps } else /* (epos <= prep_endpos) */ 116160786Sps { 116260786Sps /* 116360786Sps * New range ends within old prep region. 116460786Sps * Truncate search to end at start of old prep region. 116560786Sps */ 116660786Sps epos = prep_startpos; 116760786Sps } 116860786Sps 116960786Sps if (spos < prep_startpos) 117060786Sps { 117160786Sps /* 117260786Sps * New range starts before old prep region. 117360786Sps * Extend old prep region backwards to start at 117460786Sps * start of new range. 117560786Sps */ 117660786Sps if (spos < SEARCH_MORE) 117760786Sps spos = 0; 117860786Sps else 117960786Sps spos -= SEARCH_MORE; 118060786Sps nprep_startpos = spos; 118160786Sps } else /* (spos >= prep_startpos) */ 118260786Sps { 118360786Sps /* 118460786Sps * New range starts within or after old prep region. 118560786Sps * Trim search to start at end of old prep region. 118660786Sps */ 118760786Sps spos = prep_endpos; 118860786Sps } 118960786Sps } 119060786Sps 119160786Sps if (epos != NULL_POSITION && max_epos != NULL_POSITION && 119260786Sps epos > max_epos) 119360786Sps /* 119460786Sps * Don't go past the max position we're allowed. 119560786Sps */ 119660786Sps epos = max_epos; 119760786Sps 119860786Sps if (epos == NULL_POSITION || epos > spos) 119960786Sps { 1200195941Sdelphij int search_type = SRCH_FORW | SRCH_FIND_ALL; 1201195941Sdelphij search_type |= (search_info.search_type & SRCH_NO_REGEX); 1202195941Sdelphij result = search_range(spos, epos, search_type, 0, 120360786Sps maxlines, (POSITION*)NULL, &new_epos); 120460786Sps if (result < 0) 120560786Sps return; 120660786Sps if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) 120760786Sps nprep_endpos = new_epos; 120860786Sps } 120960786Sps prep_startpos = nprep_startpos; 121060786Sps prep_endpos = nprep_endpos; 121160786Sps} 1212191930Sdelphij 1213191930Sdelphij/* 1214191930Sdelphij * Set the pattern to be used for line filtering. 1215191930Sdelphij */ 1216191930Sdelphij public void 1217191930Sdelphijset_filter_pattern(pattern, search_type) 1218191930Sdelphij char *pattern; 1219191930Sdelphij int search_type; 1220191930Sdelphij{ 1221191930Sdelphij clr_filter(); 1222191930Sdelphij if (pattern == NULL || *pattern == '\0') 1223195941Sdelphij clear_pattern(&filter_info); 1224191930Sdelphij else 1225195941Sdelphij set_pattern(&filter_info, pattern, search_type); 1226191930Sdelphij screen_trashed = 1; 1227191930Sdelphij} 1228191930Sdelphij 1229191930Sdelphij/* 1230191930Sdelphij * Is there a line filter in effect? 1231191930Sdelphij */ 1232191930Sdelphij public int 1233191930Sdelphijis_filtering() 1234191930Sdelphij{ 1235191930Sdelphij if (ch_getflags() & CH_HELPFILE) 1236191930Sdelphij return (0); 1237195941Sdelphij return prev_pattern(&filter_info); 1238191930Sdelphij} 123960786Sps#endif 124060786Sps 124160786Sps#if HAVE_V8_REGCOMP 124260786Sps/* 124360786Sps * This function is called by the V8 regcomp to report 124460786Sps * errors in regular expressions. 124560786Sps */ 124660786Sps void 124760786Spsregerror(s) 124860786Sps char *s; 124960786Sps{ 125060786Sps PARG parg; 125160786Sps 125260786Sps parg.p_string = s; 125360786Sps error("%s", &parg); 125460786Sps} 125560786Sps#endif 125660786Sps 1257