1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab 11a5f0fb15SPaul Saab /* 12a5f0fb15SPaul Saab * Routines to search a file for a pattern. 13a5f0fb15SPaul Saab */ 14a5f0fb15SPaul Saab 15a5f0fb15SPaul Saab #include "less.h" 16a5f0fb15SPaul Saab #include "position.h" 1759a2d077SXin LI #include "charset.h" 18a5f0fb15SPaul Saab 19a5f0fb15SPaul Saab #define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) 20a5f0fb15SPaul Saab #define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) 21a5f0fb15SPaul Saab 22a5f0fb15SPaul Saab extern int sigs; 23a5f0fb15SPaul Saab extern int how_search; 24a5f0fb15SPaul Saab extern int caseless; 25a5f0fb15SPaul Saab extern int linenums; 26a5f0fb15SPaul Saab extern int jump_sline; 27a5f0fb15SPaul Saab extern int bs_mode; 28d713e089SXin LI extern int proc_backspace; 29d713e089SXin LI extern int proc_return; 301ede1615STim J. Robbins extern int ctldisp; 3115596da4SPaul Saab extern int status_col; 32f6b74a7dSXin LI extern void *ml_search; 33a5f0fb15SPaul Saab extern POSITION start_attnpos; 34a5f0fb15SPaul Saab extern POSITION end_attnpos; 357374caaaSXin LI extern int utf_mode; 362235c7feSXin LI extern int sc_width; 372235c7feSXin LI extern int sc_height; 382235c7feSXin LI extern int hshift; 39*c77c4889SXin LI extern int match_shift; 40*c77c4889SXin LI extern int nosearch_header_lines; 41*c77c4889SXin LI extern int nosearch_header_cols; 42d713e089SXin LI extern int header_lines; 43d713e089SXin LI extern int header_cols; 44*c77c4889SXin LI extern LWCHAR rscroll_char; 45a5f0fb15SPaul Saab #if HILITE_SEARCH 46a5f0fb15SPaul Saab extern int hilite_search; 47*c77c4889SXin LI extern size_t size_linebuf; 48*c77c4889SXin LI extern lbool squished; 49a5f0fb15SPaul Saab extern int can_goto_line; 50*c77c4889SXin LI extern lbool no_eof_bell; 51*c77c4889SXin LI static lbool hide_hilite; 52a5f0fb15SPaul Saab static POSITION prep_startpos; 53a5f0fb15SPaul Saab static POSITION prep_endpos; 54*c77c4889SXin LI public POSITION header_start_pos = NULL_POSITION; 55*c77c4889SXin LI static POSITION header_end_pos; 56*c77c4889SXin LI public lbool search_wrapped = FALSE; 57*c77c4889SXin LI #if OSC8_LINK 58*c77c4889SXin LI public POSITION osc8_linepos = NULL_POSITION; 59*c77c4889SXin LI public POSITION osc8_match_start = NULL_POSITION; 60*c77c4889SXin LI public POSITION osc8_match_end = NULL_POSITION; 61*c77c4889SXin LI public POSITION osc8_params_start = NULL_POSITION; 62*c77c4889SXin LI public POSITION osc8_params_end = NULL_POSITION; 63*c77c4889SXin LI public POSITION osc8_uri_start = NULL_POSITION; 64*c77c4889SXin LI public POSITION osc8_uri_end = NULL_POSITION; 65*c77c4889SXin LI public POSITION osc8_text_start = NULL_POSITION; 66*c77c4889SXin LI public POSITION osc8_text_end = NULL_POSITION; 67*c77c4889SXin LI char *osc8_path = NULL; 68*c77c4889SXin LI char *osc8_uri = NULL; 69*c77c4889SXin LI constant char *osc8_search_param = NULL; 70*c77c4889SXin LI #endif 71a5f0fb15SPaul Saab 72a15691bfSXin LI /* 73a15691bfSXin LI * Structures for maintaining a set of ranges for hilites and filtered-out 74a15691bfSXin LI * lines. Each range is stored as a node within a red-black tree, and we 75a15691bfSXin LI * try to extend existing ranges (without creating overlaps) rather than 76a15691bfSXin LI * create new nodes if possible. We remember the last node found by a 77a15691bfSXin LI * search for constant-time lookup if the next search is near enough to 78a15691bfSXin LI * the previous. To aid that, we overlay a secondary doubly-linked list 79a15691bfSXin LI * on top of the red-black tree so we can find the preceding/succeeding 80a15691bfSXin LI * nodes also in constant time. 81a15691bfSXin LI * 82a15691bfSXin LI * Each node is allocated from a series of pools, each pool double the size 83a15691bfSXin LI * of the previous (for amortised constant time allocation). Since our only 84a15691bfSXin LI * tree operations are clear and node insertion, not node removal, we don't 85a15691bfSXin LI * need to maintain a usage bitmap or freelist and can just return nodes 86a15691bfSXin LI * from the pool in-order until capacity is reached. 87a15691bfSXin LI */ 88a5f0fb15SPaul Saab struct hilite 89a5f0fb15SPaul Saab { 90a5f0fb15SPaul Saab POSITION hl_startpos; 91a5f0fb15SPaul Saab POSITION hl_endpos; 92d713e089SXin LI int hl_attr; 93a5f0fb15SPaul Saab }; 94a15691bfSXin LI struct hilite_node 95a15691bfSXin LI { 96a15691bfSXin LI struct hilite_node *parent; 97a15691bfSXin LI struct hilite_node *left; 98a15691bfSXin LI struct hilite_node *right; 99a15691bfSXin LI struct hilite_node *prev; 100a15691bfSXin LI struct hilite_node *next; 101a15691bfSXin LI int red; 102a15691bfSXin LI struct hilite r; 103a15691bfSXin LI }; 104a15691bfSXin LI struct hilite_storage 105a15691bfSXin LI { 106*c77c4889SXin LI size_t capacity; 107*c77c4889SXin LI size_t used; 108a15691bfSXin LI struct hilite_storage *next; 109a15691bfSXin LI struct hilite_node *nodes; 110a15691bfSXin LI }; 111a15691bfSXin LI struct hilite_tree 112a15691bfSXin LI { 113a15691bfSXin LI struct hilite_storage *first; 114a15691bfSXin LI struct hilite_storage *current; 115a15691bfSXin LI struct hilite_node *root; 116a15691bfSXin LI struct hilite_node *lookaside; 117a15691bfSXin LI }; 118a15691bfSXin LI #define HILITE_INITIALIZER() { NULL, NULL, NULL, NULL } 119a15691bfSXin LI #define HILITE_LOOKASIDE_STEPS 2 120a15691bfSXin LI 121a15691bfSXin LI static struct hilite_tree hilite_anchor = HILITE_INITIALIZER(); 122a15691bfSXin LI static struct hilite_tree filter_anchor = HILITE_INITIALIZER(); 1232235c7feSXin LI static struct pattern_info *filter_infos = NULL; 124a15691bfSXin LI 125a5f0fb15SPaul Saab #endif 126a5f0fb15SPaul Saab 127a5f0fb15SPaul Saab /* 128a5f0fb15SPaul Saab * These are the static variables that represent the "remembered" 129f0be0a1fSXin LI * search pattern and filter pattern. 130a5f0fb15SPaul Saab */ 131f0be0a1fSXin LI struct pattern_info { 132f6b74a7dSXin LI PATTERN_TYPE compiled; 133f0be0a1fSXin LI char* text; 134f0be0a1fSXin LI int search_type; 135*c77c4889SXin LI lbool is_ucase_pattern; 1362235c7feSXin LI struct pattern_info *next; 137f0be0a1fSXin LI }; 138a5f0fb15SPaul Saab 13996e55cc7SXin LI #if NO_REGEX 14096e55cc7SXin LI #define info_compiled(info) ((void*)0) 14196e55cc7SXin LI #else 14296e55cc7SXin LI #define info_compiled(info) ((info)->compiled) 14396e55cc7SXin LI #endif 14496e55cc7SXin LI 145f0be0a1fSXin LI static struct pattern_info search_info; 14695270f73SXin LI public int is_caseless; 147a5f0fb15SPaul Saab 148423c5ce5SXin LI /* 14933096f16SXin LI * Are there any uppercase letters in this string? 15033096f16SXin LI */ 151*c77c4889SXin LI static lbool is_ucase(constant char *str) 15233096f16SXin LI { 153*c77c4889SXin LI constant char *str_end = str + strlen(str); 15433096f16SXin LI LWCHAR ch; 15533096f16SXin LI 15633096f16SXin LI while (str < str_end) 15733096f16SXin LI { 158*c77c4889SXin LI ch = step_charc(&str, +1, str_end); 15933096f16SXin LI if (IS_UPPER(ch)) 160*c77c4889SXin LI return (TRUE); 16133096f16SXin LI } 162*c77c4889SXin LI return (FALSE); 16333096f16SXin LI } 16433096f16SXin LI 16533096f16SXin LI /* 1662235c7feSXin LI * Discard a saved pattern. 1672235c7feSXin LI */ 168d713e089SXin LI static void clear_pattern(struct pattern_info *info) 1692235c7feSXin LI { 1702235c7feSXin LI if (info->text != NULL) 1712235c7feSXin LI free(info->text); 1722235c7feSXin LI info->text = NULL; 1732235c7feSXin LI #if !NO_REGEX 1742235c7feSXin LI uncompile_pattern(&info->compiled); 1752235c7feSXin LI #endif 1762235c7feSXin LI } 1772235c7feSXin LI 1782235c7feSXin LI /* 179f0be0a1fSXin LI * Compile and save a search pattern. 180423c5ce5SXin LI */ 181*c77c4889SXin LI static int set_pattern(struct pattern_info *info, constant char *pattern, int search_type, int show_error) 182423c5ce5SXin LI { 18395270f73SXin LI /* 18495270f73SXin LI * Ignore case if -I is set OR 18595270f73SXin LI * -i is set AND the pattern is all lowercase. 18695270f73SXin LI */ 187d713e089SXin LI info->is_ucase_pattern = (pattern == NULL) ? FALSE : is_ucase(pattern); 188d713e089SXin LI is_caseless = (info->is_ucase_pattern && caseless != OPT_ONPLUS) ? 0 : caseless; 18996e55cc7SXin LI #if !NO_REGEX 190f0be0a1fSXin LI if (pattern == NULL) 1912235c7feSXin LI SET_NULL_PATTERN(info->compiled); 1922235c7feSXin LI else if (compile_pattern(pattern, search_type, show_error, &info->compiled) < 0) 193f0be0a1fSXin LI return -1; 19496e55cc7SXin LI #endif 195f0be0a1fSXin LI /* Pattern compiled successfully; save the text too. */ 196f0be0a1fSXin LI if (info->text != NULL) 197f0be0a1fSXin LI free(info->text); 198f0be0a1fSXin LI info->text = NULL; 199f0be0a1fSXin LI if (pattern != NULL) 200f0be0a1fSXin LI { 201f0be0a1fSXin LI info->text = (char *) ecalloc(1, strlen(pattern)+1); 202f0be0a1fSXin LI strcpy(info->text, pattern); 203f0be0a1fSXin LI } 204f0be0a1fSXin LI info->search_type = search_type; 205f0be0a1fSXin LI return 0; 206423c5ce5SXin LI } 207423c5ce5SXin LI 208423c5ce5SXin LI /* 209f0be0a1fSXin LI * Initialize saved pattern to nothing. 210f0be0a1fSXin LI */ 211d713e089SXin LI static void init_pattern(struct pattern_info *info) 212f0be0a1fSXin LI { 2132235c7feSXin LI SET_NULL_PATTERN(info->compiled); 214f0be0a1fSXin LI info->text = NULL; 215f0be0a1fSXin LI info->search_type = 0; 2162235c7feSXin LI info->next = NULL; 217f0be0a1fSXin LI } 218f0be0a1fSXin LI 219f0be0a1fSXin LI /* 220f0be0a1fSXin LI * Initialize search variables. 221f0be0a1fSXin LI */ 222d713e089SXin LI public void init_search(void) 223f0be0a1fSXin LI { 224f0be0a1fSXin LI init_pattern(&search_info); 225f0be0a1fSXin LI } 226f0be0a1fSXin LI 227f0be0a1fSXin LI /* 228f0be0a1fSXin LI * Determine which text conversions to perform before pattern matching. 2291ede1615STim J. Robbins */ 230*c77c4889SXin LI public int get_cvt_ops(int search_type) 2311ede1615STim J. Robbins { 2321ede1615STim J. Robbins int ops = 0; 2332235c7feSXin LI 234d713e089SXin LI if (is_caseless && (!re_handles_caseless || (search_type & SRCH_NO_REGEX))) 2351ede1615STim J. Robbins ops |= CVT_TO_LC; 236d713e089SXin LI if (proc_backspace == OPT_ON || (bs_mode == BS_SPECIAL && proc_backspace == OPT_OFF)) 2371ede1615STim J. Robbins ops |= CVT_BS; 238d713e089SXin LI if (proc_return == OPT_ON || (bs_mode != BS_CONTROL && proc_backspace == OPT_OFF)) 2391ede1615STim J. Robbins ops |= CVT_CRLF; 2401ede1615STim J. Robbins if (ctldisp == OPT_ONPLUS) 2411ede1615STim J. Robbins ops |= CVT_ANSI; 2421ede1615STim J. Robbins return (ops); 2431ede1615STim J. Robbins } 2441ede1615STim J. Robbins 2451ede1615STim J. Robbins /* 246a5f0fb15SPaul Saab * Is there a previous (remembered) search pattern? 247a5f0fb15SPaul Saab */ 248d713e089SXin LI static int prev_pattern(struct pattern_info *info) 249a5f0fb15SPaul Saab { 25096e55cc7SXin LI #if !NO_REGEX 25196e55cc7SXin LI if ((info->search_type & SRCH_NO_REGEX) == 0) 252f0be0a1fSXin LI return (!is_null_pattern(info->compiled)); 25396e55cc7SXin LI #endif 25496e55cc7SXin LI return (info->text != NULL); 255a5f0fb15SPaul Saab } 256a5f0fb15SPaul Saab 257a5f0fb15SPaul Saab #if HILITE_SEARCH 258a5f0fb15SPaul Saab /* 259a5f0fb15SPaul Saab * Repaint the hilites currently displayed on the screen. 260a5f0fb15SPaul Saab * Repaint each line which contains highlighted text. 261a5f0fb15SPaul Saab * If on==0, force all hilites off. 262a5f0fb15SPaul Saab */ 263*c77c4889SXin LI public void repaint_hilite(lbool on) 264a5f0fb15SPaul Saab { 265b2ea2440SXin LI int sindex; 266a5f0fb15SPaul Saab POSITION pos; 267*c77c4889SXin LI lbool save_hide_hilite; 268a5f0fb15SPaul Saab 269a5f0fb15SPaul Saab if (squished) 270a5f0fb15SPaul Saab repaint(); 271a5f0fb15SPaul Saab 272a5f0fb15SPaul Saab save_hide_hilite = hide_hilite; 273a5f0fb15SPaul Saab if (!on) 274a5f0fb15SPaul Saab { 275a5f0fb15SPaul Saab if (hide_hilite) 276a5f0fb15SPaul Saab return; 277*c77c4889SXin LI hide_hilite = TRUE; 278a5f0fb15SPaul Saab } 279a5f0fb15SPaul Saab 280a5f0fb15SPaul Saab if (!can_goto_line) 281a5f0fb15SPaul Saab { 282a5f0fb15SPaul Saab repaint(); 283a5f0fb15SPaul Saab hide_hilite = save_hide_hilite; 284a5f0fb15SPaul Saab return; 285a5f0fb15SPaul Saab } 286a5f0fb15SPaul Saab 287b2ea2440SXin LI for (sindex = TOP; sindex < TOP + sc_height-1; sindex++) 288a5f0fb15SPaul Saab { 289b2ea2440SXin LI pos = position(sindex); 290a5f0fb15SPaul Saab if (pos == NULL_POSITION) 291a5f0fb15SPaul Saab continue; 292a5f0fb15SPaul Saab (void) forw_line(pos); 293b2ea2440SXin LI goto_line(sindex); 294d713e089SXin LI clear_eol(); 295a5f0fb15SPaul Saab put_line(); 296a5f0fb15SPaul Saab } 29795270f73SXin LI overlay_header(); 29833096f16SXin LI lower_left(); 299a5f0fb15SPaul Saab hide_hilite = save_hide_hilite; 300a5f0fb15SPaul Saab } 3012235c7feSXin LI #endif 302a5f0fb15SPaul Saab 303a5f0fb15SPaul Saab /* 304a5f0fb15SPaul Saab * Clear the attn hilite. 305a5f0fb15SPaul Saab */ 306d713e089SXin LI public void clear_attn(void) 307a5f0fb15SPaul Saab { 3082235c7feSXin LI #if HILITE_SEARCH 309b2ea2440SXin LI int sindex; 310a5f0fb15SPaul Saab POSITION old_start_attnpos; 311a5f0fb15SPaul Saab POSITION old_end_attnpos; 312a5f0fb15SPaul Saab POSITION pos; 313a5f0fb15SPaul Saab POSITION epos; 3147e990b09SXin LI int moved = 0; 315a5f0fb15SPaul Saab 316a5f0fb15SPaul Saab if (start_attnpos == NULL_POSITION) 317a5f0fb15SPaul Saab return; 318a5f0fb15SPaul Saab old_start_attnpos = start_attnpos; 319a5f0fb15SPaul Saab old_end_attnpos = end_attnpos; 320a5f0fb15SPaul Saab start_attnpos = end_attnpos = NULL_POSITION; 321a5f0fb15SPaul Saab 322a5f0fb15SPaul Saab if (!can_goto_line) 323a5f0fb15SPaul Saab { 324a5f0fb15SPaul Saab repaint(); 325a5f0fb15SPaul Saab return; 326a5f0fb15SPaul Saab } 327a5f0fb15SPaul Saab if (squished) 328a5f0fb15SPaul Saab repaint(); 329a5f0fb15SPaul Saab 330b2ea2440SXin LI for (sindex = TOP; sindex < TOP + sc_height-1; sindex++) 331a5f0fb15SPaul Saab { 332b2ea2440SXin LI pos = position(sindex); 333a5f0fb15SPaul Saab if (pos == NULL_POSITION) 334a5f0fb15SPaul Saab continue; 335b2ea2440SXin LI epos = position(sindex+1); 336b2ea2440SXin LI if (pos <= old_end_attnpos && 337a5f0fb15SPaul Saab (epos == NULL_POSITION || epos > old_start_attnpos)) 338a5f0fb15SPaul Saab { 339a5f0fb15SPaul Saab (void) forw_line(pos); 340b2ea2440SXin LI goto_line(sindex); 341d713e089SXin LI clear_eol(); 342a5f0fb15SPaul Saab put_line(); 3437e990b09SXin LI moved = 1; 344a5f0fb15SPaul Saab } 345a5f0fb15SPaul Saab } 34695270f73SXin LI if (overlay_header()) 34795270f73SXin LI moved = 1; 3487e990b09SXin LI if (moved) 3497e990b09SXin LI lower_left(); 350a5f0fb15SPaul Saab #endif 3512235c7feSXin LI } 352a5f0fb15SPaul Saab 353a5f0fb15SPaul Saab /* 3542235c7feSXin LI * Toggle or clear search string highlighting. 355a5f0fb15SPaul Saab */ 356*c77c4889SXin LI public void undo_search(lbool clear) 357a5f0fb15SPaul Saab { 3582235c7feSXin LI clear_pattern(&search_info); 359*c77c4889SXin LI #if OSC8_LINK 360*c77c4889SXin LI osc8_linepos = NULL_POSITION; 361*c77c4889SXin LI #endif 3622235c7feSXin LI #if HILITE_SEARCH 3632235c7feSXin LI if (clear) 3642235c7feSXin LI { 3652235c7feSXin LI clr_hilite(); 3662235c7feSXin LI } else 367a5f0fb15SPaul Saab { 368b2ea2440SXin LI if (hilite_anchor.first == NULL) 369b2ea2440SXin LI { 370a5f0fb15SPaul Saab error("No previous regular expression", NULL_PARG); 371a5f0fb15SPaul Saab return; 372a5f0fb15SPaul Saab } 373a5f0fb15SPaul Saab hide_hilite = !hide_hilite; 3742235c7feSXin LI } 375*c77c4889SXin LI repaint_hilite(TRUE); 376a5f0fb15SPaul Saab #endif 377a5f0fb15SPaul Saab } 378a5f0fb15SPaul Saab 379a5f0fb15SPaul Saab #if HILITE_SEARCH 380a5f0fb15SPaul Saab /* 381a5f0fb15SPaul Saab * Clear the hilite list. 382a5f0fb15SPaul Saab */ 383d713e089SXin LI public void clr_hlist(struct hilite_tree *anchor) 384a5f0fb15SPaul Saab { 385a15691bfSXin LI struct hilite_storage *hls; 386a15691bfSXin LI struct hilite_storage *nexthls; 387a5f0fb15SPaul Saab 388a15691bfSXin LI for (hls = anchor->first; hls != NULL; hls = nexthls) 389a5f0fb15SPaul Saab { 390a15691bfSXin LI nexthls = hls->next; 391a15691bfSXin LI free((void*)hls->nodes); 392a15691bfSXin LI free((void*)hls); 393a5f0fb15SPaul Saab } 394a15691bfSXin LI anchor->first = NULL; 395a15691bfSXin LI anchor->current = NULL; 396a15691bfSXin LI anchor->root = NULL; 397a15691bfSXin LI 398a15691bfSXin LI anchor->lookaside = NULL; 399a15691bfSXin LI 400a5f0fb15SPaul Saab prep_startpos = prep_endpos = NULL_POSITION; 401a5f0fb15SPaul Saab } 402a5f0fb15SPaul Saab 403d713e089SXin LI public void clr_hilite(void) 4047374caaaSXin LI { 4057374caaaSXin LI clr_hlist(&hilite_anchor); 4067374caaaSXin LI } 4077374caaaSXin LI 408d713e089SXin LI public void clr_filter(void) 4097374caaaSXin LI { 4107374caaaSXin LI clr_hlist(&filter_anchor); 4117374caaaSXin LI } 4127374caaaSXin LI 413a15691bfSXin LI /* 414a15691bfSXin LI * Find the node covering pos, or the node after it if no node covers it, 415a15691bfSXin LI * or return NULL if pos is after the last range. Remember the found node, 416a15691bfSXin LI * to speed up subsequent searches for the same or similar positions (if 417a15691bfSXin LI * we return NULL, remember the last node.) 418a15691bfSXin LI */ 419d713e089SXin LI static struct hilite_node* hlist_find(struct hilite_tree *anchor, POSITION pos) 420a15691bfSXin LI { 421a15691bfSXin LI struct hilite_node *n, *m; 422a15691bfSXin LI 423a15691bfSXin LI if (anchor->lookaside) 424a15691bfSXin LI { 425a15691bfSXin LI int steps = 0; 426a15691bfSXin LI int hit = 0; 427a15691bfSXin LI 428a15691bfSXin LI n = anchor->lookaside; 429a15691bfSXin LI 430a15691bfSXin LI for (;;) 431a15691bfSXin LI { 432a15691bfSXin LI if (pos < n->r.hl_endpos) 433a15691bfSXin LI { 434a15691bfSXin LI if (n->prev == NULL || pos >= n->prev->r.hl_endpos) 435a15691bfSXin LI { 436a15691bfSXin LI hit = 1; 437a15691bfSXin LI break; 438a15691bfSXin LI } 439a15691bfSXin LI } else if (n->next == NULL) 440a15691bfSXin LI { 441a15691bfSXin LI n = NULL; 442a15691bfSXin LI hit = 1; 443a15691bfSXin LI break; 444a15691bfSXin LI } 445a15691bfSXin LI 446a15691bfSXin LI /* 447a15691bfSXin LI * If we don't find the right node within a small 448a15691bfSXin LI * distance, don't keep doing a linear search! 449a15691bfSXin LI */ 450a15691bfSXin LI if (steps >= HILITE_LOOKASIDE_STEPS) 451a15691bfSXin LI break; 452a15691bfSXin LI steps++; 453a15691bfSXin LI 454a15691bfSXin LI if (pos < n->r.hl_endpos) 455a15691bfSXin LI anchor->lookaside = n = n->prev; 456a15691bfSXin LI else 457a15691bfSXin LI anchor->lookaside = n = n->next; 458a15691bfSXin LI } 459a15691bfSXin LI 460a15691bfSXin LI if (hit) 461a15691bfSXin LI return n; 462a15691bfSXin LI } 463a15691bfSXin LI 464a15691bfSXin LI n = anchor->root; 465a15691bfSXin LI m = NULL; 466a15691bfSXin LI 467a15691bfSXin LI while (n != NULL) 468a15691bfSXin LI { 469a15691bfSXin LI if (pos < n->r.hl_startpos) 470a15691bfSXin LI { 471a15691bfSXin LI if (n->left != NULL) 472a15691bfSXin LI { 473a15691bfSXin LI m = n; 474a15691bfSXin LI n = n->left; 475a15691bfSXin LI continue; 476a15691bfSXin LI } 477a15691bfSXin LI break; 478a15691bfSXin LI } 479a15691bfSXin LI if (pos >= n->r.hl_endpos) 480a15691bfSXin LI { 481a15691bfSXin LI if (n->right != NULL) 482a15691bfSXin LI { 483a15691bfSXin LI n = n->right; 484a15691bfSXin LI continue; 485a15691bfSXin LI } 486a15691bfSXin LI if (m != NULL) 487a15691bfSXin LI { 488a15691bfSXin LI n = m; 489a15691bfSXin LI } else 490a15691bfSXin LI { 491a15691bfSXin LI m = n; 492a15691bfSXin LI n = NULL; 493a15691bfSXin LI } 494a15691bfSXin LI } 495a15691bfSXin LI break; 496a15691bfSXin LI } 497a15691bfSXin LI 498a15691bfSXin LI if (n != NULL) 499a15691bfSXin LI anchor->lookaside = n; 500a15691bfSXin LI else if (m != NULL) 501a15691bfSXin LI anchor->lookaside = m; 502a15691bfSXin LI 503a15691bfSXin LI return n; 504a15691bfSXin LI } 505a15691bfSXin LI 506a5f0fb15SPaul Saab /* 507a5f0fb15SPaul Saab * Should any characters in a specified range be highlighted? 50889dd99dcSXin LI */ 509d713e089SXin LI static int hilited_range_attr(POSITION pos, POSITION epos) 51089dd99dcSXin LI { 511a15691bfSXin LI struct hilite_node *n = hlist_find(&hilite_anchor, pos); 512d713e089SXin LI if (n == NULL) 513d713e089SXin LI return 0; 514d713e089SXin LI if (epos != NULL_POSITION && epos <= n->r.hl_startpos) 515d713e089SXin LI return 0; 516d713e089SXin LI return n->r.hl_attr; 51789dd99dcSXin LI } 51889dd99dcSXin LI 51989dd99dcSXin LI /* 520*c77c4889SXin LI * Set header parameters. 521*c77c4889SXin LI */ 522*c77c4889SXin LI public void set_header(POSITION pos) 523*c77c4889SXin LI { 524*c77c4889SXin LI header_start_pos = (header_lines == 0) ? NULL_POSITION : pos; 525*c77c4889SXin LI if (header_start_pos != NULL_POSITION) 526*c77c4889SXin LI { 527*c77c4889SXin LI int ln; 528*c77c4889SXin LI for (ln = 0; ln < header_lines; ++ln) 529*c77c4889SXin LI { 530*c77c4889SXin LI pos = forw_raw_line(pos, NULL, NULL); 531*c77c4889SXin LI if (pos == NULL_POSITION) break; 532*c77c4889SXin LI } 533*c77c4889SXin LI header_end_pos = pos; 534*c77c4889SXin LI } 535*c77c4889SXin LI } 536*c77c4889SXin LI 537*c77c4889SXin LI /* 538*c77c4889SXin LI * Is a position within the header lines? 539*c77c4889SXin LI */ 540*c77c4889SXin LI static int pos_in_header(POSITION pos) 541*c77c4889SXin LI { 542*c77c4889SXin LI return (header_start_pos != NULL_POSITION && 543*c77c4889SXin LI pos >= header_start_pos && pos < header_end_pos); 544*c77c4889SXin LI } 545*c77c4889SXin LI 546*c77c4889SXin LI /* 5477374caaaSXin LI * Is a line "filtered" -- that is, should it be hidden? 5487374caaaSXin LI */ 549*c77c4889SXin LI public lbool is_filtered(POSITION pos) 5507374caaaSXin LI { 551a15691bfSXin LI struct hilite_node *n; 5527374caaaSXin LI 5537374caaaSXin LI if (ch_getflags() & CH_HELPFILE) 554*c77c4889SXin LI return (FALSE); 555*c77c4889SXin LI if (pos_in_header(pos)) 556*c77c4889SXin LI return (FALSE); 5577374caaaSXin LI 558a15691bfSXin LI n = hlist_find(&filter_anchor, pos); 559a15691bfSXin LI return (n != NULL && pos >= n->r.hl_startpos); 560a15691bfSXin LI } 561a15691bfSXin LI 5627374caaaSXin LI /* 563a15691bfSXin LI * If pos is hidden, return the next position which isn't, otherwise 564a15691bfSXin LI * just return pos. 5657374caaaSXin LI */ 566d713e089SXin LI public POSITION next_unfiltered(POSITION pos) 5677374caaaSXin LI { 568a15691bfSXin LI struct hilite_node *n; 569a15691bfSXin LI 570a15691bfSXin LI if (ch_getflags() & CH_HELPFILE) 571a15691bfSXin LI return (pos); 572*c77c4889SXin LI if (pos_in_header(pos)) 573*c77c4889SXin LI return (pos); 574a15691bfSXin LI 575a15691bfSXin LI n = hlist_find(&filter_anchor, pos); 576a15691bfSXin LI while (n != NULL && pos >= n->r.hl_startpos) 577a15691bfSXin LI { 578a15691bfSXin LI pos = n->r.hl_endpos; 579a15691bfSXin LI n = n->next; 5807374caaaSXin LI } 581a15691bfSXin LI return (pos); 5827374caaaSXin LI } 5837374caaaSXin LI 5847374caaaSXin LI /* 585a15691bfSXin LI * If pos is hidden, return the previous position which isn't or 0 if 586a15691bfSXin LI * we're filtered right to the beginning, otherwise just return pos. 587a15691bfSXin LI */ 588d713e089SXin LI public POSITION prev_unfiltered(POSITION pos) 589a15691bfSXin LI { 590a15691bfSXin LI struct hilite_node *n; 591a15691bfSXin LI 592a15691bfSXin LI if (ch_getflags() & CH_HELPFILE) 593a15691bfSXin LI return (pos); 594*c77c4889SXin LI if (pos_in_header(pos)) 595*c77c4889SXin LI return (pos); 596a15691bfSXin LI 597a15691bfSXin LI n = hlist_find(&filter_anchor, pos); 598a15691bfSXin LI while (n != NULL && pos >= n->r.hl_startpos) 599a15691bfSXin LI { 600a15691bfSXin LI pos = n->r.hl_startpos; 601a15691bfSXin LI if (pos == 0) 602a15691bfSXin LI break; 603a15691bfSXin LI pos--; 604a15691bfSXin LI n = n->prev; 605a15691bfSXin LI } 606a15691bfSXin LI return (pos); 607a15691bfSXin LI } 608a15691bfSXin LI 609*c77c4889SXin LI /* 610*c77c4889SXin LI * Set the hshift for the line starting at line_pos so that the string 611*c77c4889SXin LI * between start_off and end_off is visible on the screen. 612*c77c4889SXin LI */ 613*c77c4889SXin LI static void shift_visible(POSITION line_pos, size_t start_off, size_t end_off) 614*c77c4889SXin LI { 615*c77c4889SXin LI POSITION start_pos = line_pos + start_off; 616*c77c4889SXin LI POSITION end_pos = line_pos + end_off; 617*c77c4889SXin LI int start_col = col_from_pos(line_pos, start_pos, NULL_POSITION, -1); 618*c77c4889SXin LI int end_col = col_from_pos(line_pos, end_pos, start_pos, start_col); 619*c77c4889SXin LI int swidth = sc_width - line_pfx_width() - (rscroll_char ? 1 : 0); 620*c77c4889SXin LI int new_hshift; 621*c77c4889SXin LI if (start_col < 0 || end_col < 0) 622*c77c4889SXin LI return; 623*c77c4889SXin LI if (end_col < swidth) /* whole string is in first screen */ 624*c77c4889SXin LI new_hshift = 0; 625*c77c4889SXin LI else if (start_col > hshift && end_col < hshift + swidth) 626*c77c4889SXin LI new_hshift = hshift; /* already visible; leave hshift unchanged */ 627*c77c4889SXin LI else 628*c77c4889SXin LI { 629*c77c4889SXin LI int eol_col = col_from_pos(line_pos, NULL_POSITION, end_pos, end_col) - swidth; 630*c77c4889SXin LI if (start_col >= eol_col) /* whole string is in last screen */ 631*c77c4889SXin LI new_hshift = eol_col; 632*c77c4889SXin LI else /* shift it to column match_shift */ 633*c77c4889SXin LI new_hshift = (start_col < match_shift) ? 0 : start_col - match_shift; 634*c77c4889SXin LI } 635*c77c4889SXin LI if (new_hshift != hshift) 636*c77c4889SXin LI { 637*c77c4889SXin LI hshift = new_hshift; 638*c77c4889SXin LI screen_trashed(); 639*c77c4889SXin LI } 640*c77c4889SXin LI } 641a15691bfSXin LI 642a15691bfSXin LI /* 64389dd99dcSXin LI * Should any characters in a specified range be highlighted? 644a5f0fb15SPaul Saab * If nohide is nonzero, don't consider hide_hilite. 645a5f0fb15SPaul Saab */ 646d713e089SXin LI public int is_hilited_attr(POSITION pos, POSITION epos, int nohide, int *p_matches) 647a5f0fb15SPaul Saab { 648d713e089SXin LI int attr; 64989dd99dcSXin LI 65089dd99dcSXin LI if (p_matches != NULL) 65189dd99dcSXin LI *p_matches = 0; 652a5f0fb15SPaul Saab 65315596da4SPaul Saab if (!status_col && 65415596da4SPaul Saab start_attnpos != NULL_POSITION && 6552235c7feSXin LI pos <= end_attnpos && 656*c77c4889SXin LI (epos == NULL_POSITION || epos > start_attnpos)) 657a5f0fb15SPaul Saab /* 658a5f0fb15SPaul Saab * The attn line overlaps this range. 659a5f0fb15SPaul Saab */ 6602235c7feSXin LI return (AT_HILITE|AT_COLOR_ATTN); 661a5f0fb15SPaul Saab 662*c77c4889SXin LI #if OSC8_LINK 663*c77c4889SXin LI if (osc8_linepos != NULL_POSITION && 664*c77c4889SXin LI pos <= osc8_text_end && (epos == NULL_POSITION || epos > osc8_text_start)) 665*c77c4889SXin LI return (AT_HILITE|AT_COLOR_SEARCH); 666*c77c4889SXin LI #endif 667*c77c4889SXin LI 668d713e089SXin LI attr = hilited_range_attr(pos, epos); 669d713e089SXin LI if (attr == 0) 67089dd99dcSXin LI return (0); 67189dd99dcSXin LI 672b2ea2440SXin LI if (p_matches == NULL) 673b2ea2440SXin LI /* 674b2ea2440SXin LI * Kinda kludgy way to recognize that caller is checking for 675b2ea2440SXin LI * hilite in status column. In this case we want to return 676b2ea2440SXin LI * hilite status even if hiliting is disabled or hidden. 677b2ea2440SXin LI */ 678d713e089SXin LI return (attr); 679b2ea2440SXin LI 68089dd99dcSXin LI /* 68189dd99dcSXin LI * Report matches, even if we're hiding highlights. 68289dd99dcSXin LI */ 68389dd99dcSXin LI *p_matches = 1; 68489dd99dcSXin LI 685a5f0fb15SPaul Saab if (hilite_search == 0) 686a5f0fb15SPaul Saab /* 687a5f0fb15SPaul Saab * Not doing highlighting. 688a5f0fb15SPaul Saab */ 689a5f0fb15SPaul Saab return (0); 690a5f0fb15SPaul Saab 691a5f0fb15SPaul Saab if (!nohide && hide_hilite) 692a5f0fb15SPaul Saab /* 693a5f0fb15SPaul Saab * Highlighting is hidden. 694a5f0fb15SPaul Saab */ 695a5f0fb15SPaul Saab return (0); 696a5f0fb15SPaul Saab 697d713e089SXin LI return (attr); 698a5f0fb15SPaul Saab } 699a5f0fb15SPaul Saab 700a5f0fb15SPaul Saab /* 701a15691bfSXin LI * Tree node storage: get the current block of nodes if it has spare 702a15691bfSXin LI * capacity, or create a new one if not. 703a15691bfSXin LI */ 704d713e089SXin LI static struct hilite_storage * hlist_getstorage(struct hilite_tree *anchor) 705a15691bfSXin LI { 706*c77c4889SXin LI size_t capacity = 1; 707a15691bfSXin LI struct hilite_storage *s; 708a15691bfSXin LI 709a15691bfSXin LI if (anchor->current) 710a15691bfSXin LI { 711a15691bfSXin LI if (anchor->current->used < anchor->current->capacity) 712a15691bfSXin LI return anchor->current; 713a15691bfSXin LI capacity = anchor->current->capacity * 2; 714a15691bfSXin LI } 715a15691bfSXin LI 716a15691bfSXin LI s = (struct hilite_storage *) ecalloc(1, sizeof(struct hilite_storage)); 717a15691bfSXin LI s->nodes = (struct hilite_node *) ecalloc(capacity, sizeof(struct hilite_node)); 718a15691bfSXin LI s->capacity = capacity; 719a15691bfSXin LI s->used = 0; 720a15691bfSXin LI s->next = NULL; 721a15691bfSXin LI if (anchor->current) 722a15691bfSXin LI anchor->current->next = s; 723a15691bfSXin LI else 724a15691bfSXin LI anchor->first = s; 725a15691bfSXin LI anchor->current = s; 726a15691bfSXin LI return s; 727a15691bfSXin LI } 728a15691bfSXin LI 729a15691bfSXin LI /* 730a15691bfSXin LI * Tree node storage: retrieve a new empty node to be inserted into the 731a15691bfSXin LI * tree. 732a15691bfSXin LI */ 733d713e089SXin LI static struct hilite_node * hlist_getnode(struct hilite_tree *anchor) 734a15691bfSXin LI { 735a15691bfSXin LI struct hilite_storage *s = hlist_getstorage(anchor); 736a15691bfSXin LI return &s->nodes[s->used++]; 737a15691bfSXin LI } 738a15691bfSXin LI 739a15691bfSXin LI /* 740a15691bfSXin LI * Rotate the tree left around a pivot node. 741a15691bfSXin LI */ 742d713e089SXin LI static void hlist_rotate_left(struct hilite_tree *anchor, struct hilite_node *n) 743a15691bfSXin LI { 744a15691bfSXin LI struct hilite_node *np = n->parent; 745a15691bfSXin LI struct hilite_node *nr = n->right; 746a15691bfSXin LI struct hilite_node *nrl = n->right->left; 747a15691bfSXin LI 748a15691bfSXin LI if (np != NULL) 749a15691bfSXin LI { 750a15691bfSXin LI if (n == np->left) 751a15691bfSXin LI np->left = nr; 752a15691bfSXin LI else 753a15691bfSXin LI np->right = nr; 754a15691bfSXin LI } else 755a15691bfSXin LI { 756a15691bfSXin LI anchor->root = nr; 757a15691bfSXin LI } 758a15691bfSXin LI nr->left = n; 759a15691bfSXin LI n->right = nrl; 760a15691bfSXin LI 761a15691bfSXin LI nr->parent = np; 762a15691bfSXin LI n->parent = nr; 763a15691bfSXin LI if (nrl != NULL) 764a15691bfSXin LI nrl->parent = n; 765a15691bfSXin LI } 766a15691bfSXin LI 767a15691bfSXin LI /* 768a15691bfSXin LI * Rotate the tree right around a pivot node. 769a15691bfSXin LI */ 770d713e089SXin LI static void hlist_rotate_right(struct hilite_tree *anchor, struct hilite_node *n) 771a15691bfSXin LI { 772a15691bfSXin LI struct hilite_node *np = n->parent; 773a15691bfSXin LI struct hilite_node *nl = n->left; 774a15691bfSXin LI struct hilite_node *nlr = n->left->right; 775a15691bfSXin LI 776a15691bfSXin LI if (np != NULL) 777a15691bfSXin LI { 778a15691bfSXin LI if (n == np->right) 779a15691bfSXin LI np->right = nl; 780a15691bfSXin LI else 781a15691bfSXin LI np->left = nl; 782a15691bfSXin LI } else 783a15691bfSXin LI { 784a15691bfSXin LI anchor->root = nl; 785a15691bfSXin LI } 786a15691bfSXin LI nl->right = n; 787a15691bfSXin LI n->left = nlr; 788a15691bfSXin LI 789a15691bfSXin LI nl->parent = np; 790a15691bfSXin LI n->parent = nl; 791a15691bfSXin LI if (nlr != NULL) 792a15691bfSXin LI nlr->parent = n; 793a15691bfSXin LI } 794a15691bfSXin LI 795a15691bfSXin LI 796a15691bfSXin LI /* 797a5f0fb15SPaul Saab * Add a new hilite to a hilite list. 798a5f0fb15SPaul Saab */ 799d713e089SXin LI static void add_hilite(struct hilite_tree *anchor, struct hilite *hl) 800a5f0fb15SPaul Saab { 801a15691bfSXin LI struct hilite_node *p, *n, *u; 802a15691bfSXin LI 803a15691bfSXin LI /* Ignore empty ranges. */ 804a15691bfSXin LI if (hl->hl_startpos >= hl->hl_endpos) 805a15691bfSXin LI return; 806a15691bfSXin LI 807a15691bfSXin LI p = anchor->root; 808a15691bfSXin LI 809a15691bfSXin LI /* Inserting the very first node is trivial. */ 810a15691bfSXin LI if (p == NULL) 811a15691bfSXin LI { 812a15691bfSXin LI n = hlist_getnode(anchor); 813a15691bfSXin LI n->r = *hl; 814a15691bfSXin LI anchor->root = n; 815a15691bfSXin LI anchor->lookaside = n; 816a15691bfSXin LI return; 817a15691bfSXin LI } 818a5f0fb15SPaul Saab 819a5f0fb15SPaul Saab /* 820a15691bfSXin LI * Find our insertion point. If we come across any overlapping 821a15691bfSXin LI * or adjoining existing ranges, shrink our range and discard 822a15691bfSXin LI * if it become empty. 823a5f0fb15SPaul Saab */ 824a15691bfSXin LI for (;;) 825a5f0fb15SPaul Saab { 826a15691bfSXin LI if (hl->hl_startpos < p->r.hl_startpos) 827a15691bfSXin LI { 828d713e089SXin LI if (hl->hl_endpos > p->r.hl_startpos && hl->hl_attr == p->r.hl_attr) 829a15691bfSXin LI hl->hl_endpos = p->r.hl_startpos; 830a15691bfSXin LI if (p->left != NULL) 831a15691bfSXin LI { 832a15691bfSXin LI p = p->left; 833a15691bfSXin LI continue; 834a15691bfSXin LI } 835a15691bfSXin LI break; 836a15691bfSXin LI } 837d713e089SXin LI if (hl->hl_startpos < p->r.hl_endpos && hl->hl_attr == p->r.hl_attr) { 838a15691bfSXin LI hl->hl_startpos = p->r.hl_endpos; 839a15691bfSXin LI if (hl->hl_startpos >= hl->hl_endpos) 840a15691bfSXin LI return; 841a15691bfSXin LI } 842a15691bfSXin LI if (p->right != NULL) 843a15691bfSXin LI { 844a15691bfSXin LI p = p->right; 845a15691bfSXin LI continue; 846a15691bfSXin LI } 847a5f0fb15SPaul Saab break; 848a5f0fb15SPaul Saab } 849a5f0fb15SPaul Saab 850a5f0fb15SPaul Saab /* 851a15691bfSXin LI * Now we're at the right leaf, again check for contiguous ranges 852a15691bfSXin LI * and extend the existing node if possible to avoid the 853a15691bfSXin LI * insertion. Otherwise insert a new node at the leaf. 854a5f0fb15SPaul Saab */ 855a15691bfSXin LI if (hl->hl_startpos < p->r.hl_startpos) { 856d713e089SXin LI if (hl->hl_attr == p->r.hl_attr) 857d713e089SXin LI { 858a15691bfSXin LI if (hl->hl_endpos == p->r.hl_startpos) 859a5f0fb15SPaul Saab { 860a15691bfSXin LI p->r.hl_startpos = hl->hl_startpos; 861a5f0fb15SPaul Saab return; 862a5f0fb15SPaul Saab } 863a15691bfSXin LI if (p->prev != NULL && p->prev->r.hl_endpos == hl->hl_startpos) 864a15691bfSXin LI { 865a15691bfSXin LI p->prev->r.hl_endpos = hl->hl_endpos; 866a15691bfSXin LI return; 867a15691bfSXin LI } 868d713e089SXin LI } 869a15691bfSXin LI p->left = n = hlist_getnode(anchor); 870a15691bfSXin LI n->next = p; 871a15691bfSXin LI if (p->prev != NULL) 872a15691bfSXin LI { 873a15691bfSXin LI n->prev = p->prev; 874a15691bfSXin LI p->prev->next = n; 875a15691bfSXin LI } 876a15691bfSXin LI p->prev = n; 877a15691bfSXin LI } else { 878d713e089SXin LI if (hl->hl_attr == p->r.hl_attr) 879d713e089SXin LI { 880a15691bfSXin LI if (p->r.hl_endpos == hl->hl_startpos) 881a15691bfSXin LI { 882a15691bfSXin LI p->r.hl_endpos = hl->hl_endpos; 883a15691bfSXin LI return; 884a15691bfSXin LI } 885a15691bfSXin LI if (p->next != NULL && hl->hl_endpos == p->next->r.hl_startpos) { 886a15691bfSXin LI p->next->r.hl_startpos = hl->hl_startpos; 887a15691bfSXin LI return; 888a15691bfSXin LI } 889d713e089SXin LI } 890a15691bfSXin LI p->right = n = hlist_getnode(anchor); 891a15691bfSXin LI n->prev = p; 892a15691bfSXin LI if (p->next != NULL) 893a15691bfSXin LI { 894a15691bfSXin LI n->next = p->next; 895a15691bfSXin LI p->next->prev = n; 896a15691bfSXin LI } 897a15691bfSXin LI p->next = n; 898a15691bfSXin LI } 899a15691bfSXin LI n->parent = p; 900a15691bfSXin LI n->red = 1; 901a15691bfSXin LI n->r = *hl; 902a15691bfSXin LI 903a15691bfSXin LI /* 904a15691bfSXin LI * The tree is in the correct order and covers the right ranges 905a15691bfSXin LI * now, but may have become unbalanced. Rebalance it using the 906a15691bfSXin LI * standard red-black tree constraints and operations. 907a15691bfSXin LI */ 908a15691bfSXin LI for (;;) 909a15691bfSXin LI { 910a15691bfSXin LI /* case 1 - current is root, root is always black */ 911a15691bfSXin LI if (n->parent == NULL) 912a15691bfSXin LI { 913a15691bfSXin LI n->red = 0; 914a15691bfSXin LI break; 915a15691bfSXin LI } 916a15691bfSXin LI 917a15691bfSXin LI /* case 2 - parent is black, we can always be red */ 918a15691bfSXin LI if (!n->parent->red) 919a15691bfSXin LI break; 920a15691bfSXin LI 921a15691bfSXin LI /* 922a15691bfSXin LI * constraint: because the root must be black, if our 923a15691bfSXin LI * parent is red it cannot be the root therefore we must 924a15691bfSXin LI * have a grandparent 925a15691bfSXin LI */ 926a15691bfSXin LI 927a15691bfSXin LI /* 928a15691bfSXin LI * case 3 - parent and uncle are red, repaint them black, 929a15691bfSXin LI * the grandparent red, and start again at the grandparent. 930a15691bfSXin LI */ 931a15691bfSXin LI u = n->parent->parent->left; 932a15691bfSXin LI if (n->parent == u) 933a15691bfSXin LI u = n->parent->parent->right; 934a15691bfSXin LI if (u != NULL && u->red) 935a15691bfSXin LI { 936a15691bfSXin LI n->parent->red = 0; 937a15691bfSXin LI u->red = 0; 938a15691bfSXin LI n = n->parent->parent; 939a15691bfSXin LI n->red = 1; 940a15691bfSXin LI continue; 941a15691bfSXin LI } 942a15691bfSXin LI 943a15691bfSXin LI /* 944a15691bfSXin LI * case 4 - parent is red but uncle is black, parent and 945a15691bfSXin LI * grandparent on opposite sides. We need to start 946a15691bfSXin LI * changing the structure now. This and case 5 will shorten 947a15691bfSXin LI * our branch and lengthen the sibling, between them 948a15691bfSXin LI * restoring balance. 949a15691bfSXin LI */ 950a15691bfSXin LI if (n == n->parent->right && 951a15691bfSXin LI n->parent == n->parent->parent->left) 952a15691bfSXin LI { 953a15691bfSXin LI hlist_rotate_left(anchor, n->parent); 954a15691bfSXin LI n = n->left; 955a15691bfSXin LI } else if (n == n->parent->left && 956a15691bfSXin LI n->parent == n->parent->parent->right) 957a15691bfSXin LI { 958a15691bfSXin LI hlist_rotate_right(anchor, n->parent); 959a15691bfSXin LI n = n->right; 960a15691bfSXin LI } 961a15691bfSXin LI 962a15691bfSXin LI /* 963a15691bfSXin LI * case 5 - parent is red but uncle is black, parent and 964a15691bfSXin LI * grandparent on same side 965a15691bfSXin LI */ 966a15691bfSXin LI n->parent->red = 0; 967a15691bfSXin LI n->parent->parent->red = 1; 968a15691bfSXin LI if (n == n->parent->left) 969a15691bfSXin LI hlist_rotate_right(anchor, n->parent->parent); 970a15691bfSXin LI else 971a15691bfSXin LI hlist_rotate_left(anchor, n->parent->parent); 972a15691bfSXin LI break; 973a15691bfSXin LI } 974a5f0fb15SPaul Saab } 975a5f0fb15SPaul Saab 97689dd99dcSXin LI /* 97795270f73SXin LI * Highlight every character in a range of displayed characters. 97896e55cc7SXin LI */ 979*c77c4889SXin LI static void create_hilites(POSITION linepos, constant char *line, constant char *sp, constant char *ep, int attr, int *chpos) 98096e55cc7SXin LI { 981*c77c4889SXin LI size_t start_index = ptr_diff(sp, line); /*{{type-issue}}*/ 982*c77c4889SXin LI size_t end_index = ptr_diff(ep, line); 983a15691bfSXin LI struct hilite hl; 984*c77c4889SXin LI size_t i; 98596e55cc7SXin LI 98696e55cc7SXin LI /* Start the first hilite. */ 987a15691bfSXin LI hl.hl_startpos = linepos + chpos[start_index]; 988d713e089SXin LI hl.hl_attr = attr; 98996e55cc7SXin LI 99096e55cc7SXin LI /* 99196e55cc7SXin LI * Step through the displayed chars. 99296e55cc7SXin LI * If the source position (before cvt) of the char is one more 99396e55cc7SXin LI * than the source pos of the previous char (the usual case), 99496e55cc7SXin LI * just increase the size of the current hilite by one. 99596e55cc7SXin LI * Otherwise (there are backspaces or something involved), 99696e55cc7SXin LI * finish the current hilite and start a new one. 99796e55cc7SXin LI */ 99896e55cc7SXin LI for (i = start_index+1; i <= end_index; i++) 99996e55cc7SXin LI { 100096e55cc7SXin LI if (chpos[i] != chpos[i-1] + 1 || i == end_index) 100196e55cc7SXin LI { 1002a15691bfSXin LI hl.hl_endpos = linepos + chpos[i-1] + 1; 1003a15691bfSXin LI add_hilite(&hilite_anchor, &hl); 100496e55cc7SXin LI /* Start new hilite unless this is the last char. */ 100596e55cc7SXin LI if (i < end_index) 100696e55cc7SXin LI { 1007a15691bfSXin LI hl.hl_startpos = linepos + chpos[i]; 100896e55cc7SXin LI } 100996e55cc7SXin LI } 101096e55cc7SXin LI } 101196e55cc7SXin LI } 101296e55cc7SXin LI 101396e55cc7SXin LI /* 1014a5f0fb15SPaul Saab * Make a hilite for each string in a physical line which matches 1015a5f0fb15SPaul Saab * the current pattern. 1016a5f0fb15SPaul Saab * sp,ep delimit the first match already found. 1017a5f0fb15SPaul Saab */ 1018*c77c4889SXin LI static void hilite_line(POSITION linepos, constant char *line, size_t line_len, int *chpos, constant char **sp, constant char **ep, int nsp) 1019a5f0fb15SPaul Saab { 1020*c77c4889SXin LI constant char *searchp; 1021*c77c4889SXin LI constant char *line_end = line + line_len; 1022a5f0fb15SPaul Saab 1023a5f0fb15SPaul Saab /* 1024d713e089SXin LI * sp[0] and ep[0] delimit the first match in the line. 1025a5f0fb15SPaul Saab * Mark the corresponding file positions, then 1026a5f0fb15SPaul Saab * look for further matches and mark them. 1027a5f0fb15SPaul Saab * {{ This technique, of calling match_pattern on subsequent 1028a5f0fb15SPaul Saab * substrings of the line, may mark more than is correct 1029a5f0fb15SPaul Saab * if the pattern starts with "^". This bug is fixed 1030a5f0fb15SPaul Saab * for those regex functions that accept a notbol parameter 1031720c436cSXin LI * (currently POSIX, PCRE and V8-with-regexec2). }} 1032d713e089SXin LI * sp[i] and ep[i] for i>0 delimit subpattern matches. 1033d713e089SXin LI * Color each of them with its unique color. 1034a5f0fb15SPaul Saab */ 1035a5f0fb15SPaul Saab searchp = line; 1036a5f0fb15SPaul Saab do { 1037*c77c4889SXin LI constant char *lep = sp[0]; 1038d713e089SXin LI int i; 1039f80a33eaSXin LI if (sp[0] == NULL || ep[0] == NULL) 1040f80a33eaSXin LI break; 1041d713e089SXin LI for (i = 1; i < nsp; i++) 1042d713e089SXin LI { 1043d713e089SXin LI if (sp[i] == NULL || ep[i] == NULL) 1044d713e089SXin LI break; 1045d713e089SXin LI if (ep[i] > sp[i]) 1046d713e089SXin LI { 1047d713e089SXin LI create_hilites(linepos, line, lep, sp[i], 1048d713e089SXin LI AT_HILITE | AT_COLOR_SEARCH, chpos); 1049d713e089SXin LI create_hilites(linepos, line, sp[i], ep[i], 1050d713e089SXin LI AT_HILITE | AT_COLOR_SUBSEARCH(i), chpos); 1051d713e089SXin LI lep = ep[i]; 1052d713e089SXin LI } 1053d713e089SXin LI } 1054d713e089SXin LI create_hilites(linepos, line, lep, ep[0], 1055d713e089SXin LI AT_HILITE | AT_COLOR_SEARCH, chpos); 1056d713e089SXin LI 1057a5f0fb15SPaul Saab /* 1058a5f0fb15SPaul Saab * If we matched more than zero characters, 1059a5f0fb15SPaul Saab * move to the first char after the string we matched. 1060a5f0fb15SPaul Saab * If we matched zero, just move to the next char. 1061a5f0fb15SPaul Saab */ 1062d713e089SXin LI if (ep[0] > searchp) 1063d713e089SXin LI searchp = ep[0]; 1064720c436cSXin LI else if (searchp != line_end) 1065a5f0fb15SPaul Saab searchp++; 1066a5f0fb15SPaul Saab else /* end of line */ 1067a5f0fb15SPaul Saab break; 106896e55cc7SXin LI } while (match_pattern(info_compiled(&search_info), search_info.text, 1069*c77c4889SXin LI searchp, ptr_diff(line_end, searchp), sp, ep, nsp, 1, search_info.search_type)); 1070a5f0fb15SPaul Saab } 1071a5f0fb15SPaul Saab #endif 1072a5f0fb15SPaul Saab 1073a5f0fb15SPaul Saab #if HILITE_SEARCH 1074a5f0fb15SPaul Saab /* 1075a5f0fb15SPaul Saab * Find matching text which is currently on screen and highlight it. 1076a5f0fb15SPaul Saab */ 1077d713e089SXin LI static void hilite_screen(void) 1078a5f0fb15SPaul Saab { 1079a5f0fb15SPaul Saab struct scrpos scrpos; 1080a5f0fb15SPaul Saab 1081b2ea2440SXin LI get_scrpos(&scrpos, TOP); 1082a5f0fb15SPaul Saab if (scrpos.pos == NULL_POSITION) 1083a5f0fb15SPaul Saab return; 1084a5f0fb15SPaul Saab prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); 1085*c77c4889SXin LI repaint_hilite(TRUE); 1086a5f0fb15SPaul Saab } 1087a5f0fb15SPaul Saab 1088a5f0fb15SPaul Saab /* 1089a5f0fb15SPaul Saab * Change highlighting parameters. 1090a5f0fb15SPaul Saab */ 1091d713e089SXin LI public void chg_hilite(void) 1092a5f0fb15SPaul Saab { 1093a5f0fb15SPaul Saab /* 1094a5f0fb15SPaul Saab * Erase any highlights currently on screen. 1095a5f0fb15SPaul Saab */ 1096a5f0fb15SPaul Saab clr_hilite(); 1097*c77c4889SXin LI hide_hilite = FALSE; 1098a5f0fb15SPaul Saab 1099a5f0fb15SPaul Saab if (hilite_search == OPT_ONPLUS) 1100a5f0fb15SPaul Saab /* 1101a5f0fb15SPaul Saab * Display highlights. 1102a5f0fb15SPaul Saab */ 1103a5f0fb15SPaul Saab hilite_screen(); 1104a5f0fb15SPaul Saab } 1105a5f0fb15SPaul Saab #endif 1106a5f0fb15SPaul Saab 1107a5f0fb15SPaul Saab /* 1108a5f0fb15SPaul Saab * Figure out where to start a search. 1109a5f0fb15SPaul Saab */ 1110d713e089SXin LI static POSITION search_pos(int search_type) 1111a5f0fb15SPaul Saab { 1112a5f0fb15SPaul Saab POSITION pos; 1113b2ea2440SXin LI int sindex; 1114a5f0fb15SPaul Saab 1115a5f0fb15SPaul Saab if (empty_screen()) 1116a5f0fb15SPaul Saab { 1117a5f0fb15SPaul Saab /* 1118a5f0fb15SPaul Saab * Start at the beginning (or end) of the file. 1119a5f0fb15SPaul Saab * The empty_screen() case is mainly for 1120a5f0fb15SPaul Saab * command line initiated searches; 1121a5f0fb15SPaul Saab * for example, "+/xyz" on the command line. 1122a5f0fb15SPaul Saab * Also for multi-file (SRCH_PAST_EOF) searches. 1123a5f0fb15SPaul Saab */ 1124a5f0fb15SPaul Saab if (search_type & SRCH_FORW) 1125a5f0fb15SPaul Saab { 112633096f16SXin LI pos = ch_zero(); 1127a5f0fb15SPaul Saab } else 1128a5f0fb15SPaul Saab { 1129a5f0fb15SPaul Saab pos = ch_length(); 1130a5f0fb15SPaul Saab if (pos == NULL_POSITION) 1131a5f0fb15SPaul Saab { 1132a5f0fb15SPaul Saab (void) ch_end_seek(); 1133a5f0fb15SPaul Saab pos = ch_length(); 1134a5f0fb15SPaul Saab } 1135a5f0fb15SPaul Saab } 1136b2ea2440SXin LI sindex = 0; 113733096f16SXin LI } else 113833096f16SXin LI { 1139*c77c4889SXin LI lbool add_one = FALSE; 114033096f16SXin LI 114133096f16SXin LI if (how_search == OPT_ON) 1142a5f0fb15SPaul Saab { 1143a5f0fb15SPaul Saab /* 1144a5f0fb15SPaul Saab * Search does not include current screen. 1145a5f0fb15SPaul Saab */ 1146a5f0fb15SPaul Saab if (search_type & SRCH_FORW) 1147b2ea2440SXin LI sindex = sc_height-1; /* BOTTOM_PLUS_ONE */ 1148a5f0fb15SPaul Saab else 1149b2ea2440SXin LI sindex = 0; /* TOP */ 115033096f16SXin LI } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) 115133096f16SXin LI { 115233096f16SXin LI /* 115333096f16SXin LI * Search includes all of displayed screen. 115433096f16SXin LI */ 115533096f16SXin LI if (search_type & SRCH_FORW) 1156b2ea2440SXin LI sindex = 0; /* TOP */ 115733096f16SXin LI else 1158b2ea2440SXin LI sindex = sc_height-1; /* BOTTOM_PLUS_ONE */ 1159a5f0fb15SPaul Saab } else 1160a5f0fb15SPaul Saab { 1161a5f0fb15SPaul Saab /* 116233096f16SXin LI * Search includes the part of current screen beyond the jump target. 1163a5f0fb15SPaul Saab * It starts at the jump target (if searching backwards), 1164a5f0fb15SPaul Saab * or at the jump target plus one (if forwards). 1165a5f0fb15SPaul Saab */ 1166b2ea2440SXin LI sindex = sindex_from_sline(jump_sline); 116733096f16SXin LI if (search_type & SRCH_FORW) 1168*c77c4889SXin LI add_one = TRUE; 116933096f16SXin LI } 1170b2ea2440SXin LI pos = position(sindex); 117133096f16SXin LI if (add_one) 1172*c77c4889SXin LI pos = forw_raw_line(pos, NULL, NULL); 117333096f16SXin LI } 117433096f16SXin LI 117533096f16SXin LI /* 117633096f16SXin LI * If the line is empty, look around for a plausible starting place. 117733096f16SXin LI */ 1178a5f0fb15SPaul Saab if (search_type & SRCH_FORW) 1179a5f0fb15SPaul Saab { 1180a5f0fb15SPaul Saab while (pos == NULL_POSITION) 1181a5f0fb15SPaul Saab { 1182b2ea2440SXin LI if (++sindex >= sc_height) 1183a5f0fb15SPaul Saab break; 1184b2ea2440SXin LI pos = position(sindex); 1185a5f0fb15SPaul Saab } 1186a5f0fb15SPaul Saab } else 1187a5f0fb15SPaul Saab { 1188a5f0fb15SPaul Saab while (pos == NULL_POSITION) 1189a5f0fb15SPaul Saab { 1190b2ea2440SXin LI if (--sindex < 0) 1191a5f0fb15SPaul Saab break; 1192b2ea2440SXin LI pos = position(sindex); 1193a5f0fb15SPaul Saab } 1194a5f0fb15SPaul Saab } 1195a5f0fb15SPaul Saab return (pos); 1196a5f0fb15SPaul Saab } 1197a5f0fb15SPaul Saab 1198a5f0fb15SPaul Saab /* 11992235c7feSXin LI * Check to see if the line matches the filter pattern. 12002235c7feSXin LI * If so, add an entry to the filter list. 12012235c7feSXin LI */ 12022235c7feSXin LI #if HILITE_SEARCH 1203*c77c4889SXin LI static int matches_filters(POSITION pos, char *cline, size_t line_len, int *chpos, POSITION linepos, constant char **sp, constant char **ep, int nsp) 12042235c7feSXin LI { 12052235c7feSXin LI struct pattern_info *filter; 12062235c7feSXin LI 12072235c7feSXin LI for (filter = filter_infos; filter != NULL; filter = filter->next) 12082235c7feSXin LI { 12092235c7feSXin LI int line_filter = match_pattern(info_compiled(filter), filter->text, 1210d713e089SXin LI cline, line_len, sp, ep, nsp, 0, filter->search_type); 12112235c7feSXin LI if (line_filter) 12122235c7feSXin LI { 12132235c7feSXin LI struct hilite hl; 12142235c7feSXin LI hl.hl_startpos = linepos; 12152235c7feSXin LI hl.hl_endpos = pos; 12162235c7feSXin LI add_hilite(&filter_anchor, &hl); 12172235c7feSXin LI free(cline); 12182235c7feSXin LI free(chpos); 12192235c7feSXin LI return (1); 12202235c7feSXin LI } 12212235c7feSXin LI } 12222235c7feSXin LI return (0); 12232235c7feSXin LI } 12242235c7feSXin LI #endif 12252235c7feSXin LI 12262235c7feSXin LI /* 12272235c7feSXin LI * Get the position of the first char in the screen line which 12282235c7feSXin LI * puts tpos on screen. 12292235c7feSXin LI */ 1230d713e089SXin LI static POSITION get_lastlinepos(POSITION pos, POSITION tpos, int sheight) 12312235c7feSXin LI { 12322235c7feSXin LI int nlines; 12332235c7feSXin LI 12342235c7feSXin LI for (nlines = 0;; nlines++) 12352235c7feSXin LI { 12362235c7feSXin LI POSITION npos = forw_line(pos); 12372235c7feSXin LI if (npos > tpos) 12382235c7feSXin LI { 12392235c7feSXin LI if (nlines < sheight) 12402235c7feSXin LI return NULL_POSITION; 12412235c7feSXin LI return pos; 12422235c7feSXin LI } 12432235c7feSXin LI pos = npos; 12442235c7feSXin LI } 12452235c7feSXin LI } 12462235c7feSXin LI 1247*c77c4889SXin LI #if OSC8_LINK 12482235c7feSXin LI 1249*c77c4889SXin LI /* 1250*c77c4889SXin LI * osc8_parse_info points to the component fields in a parsed OSC8 sequence. 1251*c77c4889SXin LI */ 1252*c77c4889SXin LI struct osc8_parse_info { 1253*c77c4889SXin LI constant char *osc8_start; 1254*c77c4889SXin LI constant char *osc8_end; 1255*c77c4889SXin LI constant char *params_start; 1256*c77c4889SXin LI constant char *params_end; 1257*c77c4889SXin LI constant char *uri_start; 1258*c77c4889SXin LI constant char *uri_end; 1259*c77c4889SXin LI }; 1260*c77c4889SXin LI 1261*c77c4889SXin LI /* 1262*c77c4889SXin LI * Parse an OSC8 sequence in a string. 1263*c77c4889SXin LI */ 1264*c77c4889SXin LI static lbool osc8_parse(constant char *line, constant char *line_end, struct osc8_parse_info *pop) 12652235c7feSXin LI { 1266*c77c4889SXin LI constant char *oline; 1267*c77c4889SXin LI pop->osc8_start = pop->osc8_end = pop->uri_start = pop->uri_end = pop->params_start = pop->params_end = NULL; 1268*c77c4889SXin LI 1269*c77c4889SXin LI oline = line; 1270*c77c4889SXin LI LWCHAR ch = step_charc(&line, +1, line_end); 1271*c77c4889SXin LI /* oline points to character ch, line points to the one after it. */ 1272*c77c4889SXin LI struct ansi_state *pansi = ansi_start(ch); 1273*c77c4889SXin LI if (pansi == NULL) 1274*c77c4889SXin LI return FALSE; 1275*c77c4889SXin LI pop->osc8_start = oline; /* start at the ESC */ 1276*c77c4889SXin LI for (;;) 1277*c77c4889SXin LI { 1278*c77c4889SXin LI ansi_state astate = ansi_step(pansi, ch); 1279*c77c4889SXin LI osc8_state ostate = ansi_osc8_state(pansi); 1280*c77c4889SXin LI if (ostate == OSC8_NOT) 1281*c77c4889SXin LI break; 1282*c77c4889SXin LI switch (ostate) 1283*c77c4889SXin LI { 1284*c77c4889SXin LI case OSC8_PARAMS: 1285*c77c4889SXin LI if (pop->params_start == NULL) 1286*c77c4889SXin LI pop->params_start = line; 1287*c77c4889SXin LI break; 1288*c77c4889SXin LI case OSC8_URI: 1289*c77c4889SXin LI if (pop->uri_start == NULL) 1290*c77c4889SXin LI { 1291*c77c4889SXin LI pop->params_end = oline; 1292*c77c4889SXin LI pop->uri_start = line; 1293*c77c4889SXin LI } 1294*c77c4889SXin LI break; 1295*c77c4889SXin LI case OSC8_ST_ESC: 1296*c77c4889SXin LI if (pop->uri_end == NULL) 1297*c77c4889SXin LI pop->uri_end = oline; 1298*c77c4889SXin LI break; 1299*c77c4889SXin LI case OSC8_END: 1300*c77c4889SXin LI ansi_done(pansi); 1301*c77c4889SXin LI pop->osc8_end = line; 1302*c77c4889SXin LI if (pop->uri_end == NULL) /* happens when ST is "\7" */ 1303*c77c4889SXin LI pop->uri_end = oline; 1304*c77c4889SXin LI if (pop->params_end == NULL) /* should not happen */ 1305*c77c4889SXin LI pop->params_end = oline; 1306*c77c4889SXin LI return TRUE; 1307*c77c4889SXin LI default: 1308*c77c4889SXin LI break; 1309*c77c4889SXin LI } 1310*c77c4889SXin LI if (astate != ANSI_MID || line >= line_end) 1311*c77c4889SXin LI break; 1312*c77c4889SXin LI oline = line; 1313*c77c4889SXin LI ch = step_charc(&line, +1, line_end); 1314*c77c4889SXin LI } 1315*c77c4889SXin LI ansi_done(pansi); 1316*c77c4889SXin LI return FALSE; 1317*c77c4889SXin LI } 1318*c77c4889SXin LI 1319*c77c4889SXin LI /* 1320*c77c4889SXin LI * Does an OSC8 sequence contain a specified parameter? 1321*c77c4889SXin LI */ 1322*c77c4889SXin LI static lbool osc8_param_match(POSITION linepos, constant char *line, constant struct osc8_parse_info *op1, constant struct osc8_parse_info *op2, constant char *param, POSITION clickpos) 1323*c77c4889SXin LI { 1324*c77c4889SXin LI size_t param_len; 1325*c77c4889SXin LI constant char *p; 1326*c77c4889SXin LI 1327*c77c4889SXin LI if (clickpos != NULL_POSITION) 1328*c77c4889SXin LI { 1329*c77c4889SXin LI return clickpos >= linepos + ptr_diff(op1->osc8_start, line) && 1330*c77c4889SXin LI clickpos < linepos + ptr_diff(op2->osc8_end, line); 1331*c77c4889SXin LI } 1332*c77c4889SXin LI if (param == NULL) 1333*c77c4889SXin LI return TRUE; 1334*c77c4889SXin LI param_len = strlen(param); 1335*c77c4889SXin LI /* Parameters are separated by colons. */ 1336*c77c4889SXin LI for (p = op1->params_start; p + param_len <= op1->params_end; ) 1337*c77c4889SXin LI { 1338*c77c4889SXin LI if (strncmp(p, param, param_len) == 0) 1339*c77c4889SXin LI { 1340*c77c4889SXin LI p += param_len; 1341*c77c4889SXin LI if (p == op1->params_end || *p == ':') 1342*c77c4889SXin LI return TRUE; 1343*c77c4889SXin LI } 1344*c77c4889SXin LI while (p < op1->params_end && *p != ':') 1345*c77c4889SXin LI ++p; 1346*c77c4889SXin LI while (p < op1->params_end && *p == ':') 1347*c77c4889SXin LI ++p; 1348*c77c4889SXin LI } 1349*c77c4889SXin LI return FALSE; 1350*c77c4889SXin LI } 1351*c77c4889SXin LI 1352*c77c4889SXin LI /* 1353*c77c4889SXin LI * Is the URI in an OSC8 sequence empty? 1354*c77c4889SXin LI * "Empty" means zero length, or equal to "#". 1355*c77c4889SXin LI */ 1356*c77c4889SXin LI static lbool osc8_empty_uri(constant struct osc8_parse_info *op) 1357*c77c4889SXin LI { 1358*c77c4889SXin LI return op->uri_end == op->uri_start || 1359*c77c4889SXin LI (op->uri_end == op->uri_start+1 && op->uri_start[0] == '#'); 1360*c77c4889SXin LI } 1361*c77c4889SXin LI 1362*c77c4889SXin LI /* 1363*c77c4889SXin LI * Find the next OSC8 hyperlink in a line. 1364*c77c4889SXin LI * A hyperlink is two OSC8 sequences (the first with a nonempty URI) 1365*c77c4889SXin LI * plus the non-empty text between them. 1366*c77c4889SXin LI * But if searching for a parameter, allow URI and/or text to be empty. 1367*c77c4889SXin LI */ 1368*c77c4889SXin LI typedef enum { OSC8_NO_MATCH, OSC8_MATCH, OSC8_ALREADY } osc8_match; 1369*c77c4889SXin LI 1370*c77c4889SXin LI static osc8_match osc8_search_line1(int search_type, POSITION linepos, POSITION spos, constant char *line, size_t line_len, constant char *param, POSITION clickpos) 1371*c77c4889SXin LI { 1372*c77c4889SXin LI constant char *line_end = &line[line_len]; 1373*c77c4889SXin LI struct osc8_parse_info op1; 1374*c77c4889SXin LI struct osc8_parse_info op2; 1375*c77c4889SXin LI constant char *linep; 1376*c77c4889SXin LI constant size_t min_osc8_size = 6; /* "\e]8;;\7" */ 1377*c77c4889SXin LI 1378*c77c4889SXin LI if (search_type & SRCH_FORW) 1379*c77c4889SXin LI { 1380*c77c4889SXin LI for (linep = line; ; linep++) 1381*c77c4889SXin LI { 1382*c77c4889SXin LI if (linep + min_osc8_size > line_end) 1383*c77c4889SXin LI return OSC8_NO_MATCH; 1384*c77c4889SXin LI /* Find the first OSC8 sequence in the line with a nonempty URI, 1385*c77c4889SXin LI * which begins the hypertext. */ 1386*c77c4889SXin LI if (osc8_parse(linep, line_end, &op1) && 1387*c77c4889SXin LI (!osc8_empty_uri(&op1) || param != NULL)) 1388*c77c4889SXin LI { 1389*c77c4889SXin LI /* Now find the next OSC8 sequence, which ends the hypertext. */ 1390*c77c4889SXin LI constant char *linep2; 1391*c77c4889SXin LI for (linep2 = op1.osc8_end; linep2 < line_end; linep2++) 1392*c77c4889SXin LI { 1393*c77c4889SXin LI if (osc8_parse(linep2, line_end, &op2)) 1394*c77c4889SXin LI break; 1395*c77c4889SXin LI } 1396*c77c4889SXin LI if (linep2 == line_end) 1397*c77c4889SXin LI op2.osc8_end = op2.osc8_start = line_end; 1398*c77c4889SXin LI if ((op2.osc8_start > op1.osc8_end || param != NULL) && 1399*c77c4889SXin LI osc8_param_match(linepos, line, &op1, &op2, param, clickpos)) 1400*c77c4889SXin LI break; 14012235c7feSXin LI } 14022235c7feSXin LI } 1403*c77c4889SXin LI } else 1404*c77c4889SXin LI { 1405*c77c4889SXin LI op2.osc8_end = op2.osc8_start = line_end; 1406*c77c4889SXin LI for (linep = line_end - min_osc8_size; ; linep--) 1407*c77c4889SXin LI { 1408*c77c4889SXin LI if (linep < line) 1409*c77c4889SXin LI return OSC8_NO_MATCH; 1410*c77c4889SXin LI if (osc8_parse(linep, line_end, &op1)) 1411*c77c4889SXin LI { 1412*c77c4889SXin LI if (((!osc8_empty_uri(&op1) && op2.osc8_start > op1.osc8_end) || param != NULL) && 1413*c77c4889SXin LI osc8_param_match(linepos, line, &op1, &op2, param, clickpos)) 1414*c77c4889SXin LI break; 1415*c77c4889SXin LI op2 = op1; 1416*c77c4889SXin LI } 1417*c77c4889SXin LI } 1418*c77c4889SXin LI } 1419*c77c4889SXin LI if (param != NULL) 1420*c77c4889SXin LI /* Don't set osc8 globals if we're just searching for a parameter. */ 1421*c77c4889SXin LI return OSC8_MATCH; 1422*c77c4889SXin LI 1423*c77c4889SXin LI if (osc8_linepos == linepos && osc8_match_start == spos + ptr_diff(op1.osc8_start, line)) 1424*c77c4889SXin LI return OSC8_ALREADY; /* already selected */ 1425*c77c4889SXin LI 1426*c77c4889SXin LI osc8_linepos = linepos; 1427*c77c4889SXin LI osc8_match_start = spos + ptr_diff(op1.osc8_start, line); 1428*c77c4889SXin LI osc8_match_end = spos + ptr_diff(op2.osc8_start, line); 1429*c77c4889SXin LI osc8_params_start = spos + ptr_diff(op1.params_start, line); 1430*c77c4889SXin LI osc8_params_end = spos + ptr_diff(op1.params_end, line); 1431*c77c4889SXin LI osc8_uri_start = spos + ptr_diff(op1.uri_start, line); 1432*c77c4889SXin LI osc8_uri_end = spos + ptr_diff(op1.uri_end, line); 1433*c77c4889SXin LI osc8_text_start = spos + ptr_diff(op1.osc8_end, line); 1434*c77c4889SXin LI osc8_text_end = spos + ptr_diff(op2.osc8_start, line); 1435*c77c4889SXin LI 1436*c77c4889SXin LI /* Save URI for message in prompt(). */ 1437*c77c4889SXin LI osc8_uri = saven(op1.uri_start, ptr_diff(op1.uri_end, op1.uri_start)); 1438*c77c4889SXin LI return OSC8_MATCH; 1439*c77c4889SXin LI } 1440*c77c4889SXin LI 1441*c77c4889SXin LI /* 1442*c77c4889SXin LI * Find the N-th OSC8 hyperlink in a line. 1443*c77c4889SXin LI */ 1444*c77c4889SXin LI static osc8_match osc8_search_line(int search_type, POSITION linepos, constant char *line, size_t line_len, constant char *param, POSITION clickpos, int *matches) 1445*c77c4889SXin LI { 1446*c77c4889SXin LI while (*matches > 0) 1447*c77c4889SXin LI { 1448*c77c4889SXin LI POSITION spos = linepos; 1449*c77c4889SXin LI constant char *sline = line; 1450*c77c4889SXin LI size_t sline_len = line_len; 1451*c77c4889SXin LI osc8_match r; 1452*c77c4889SXin LI if (linepos == osc8_linepos && clickpos == NULL_POSITION) 1453*c77c4889SXin LI { 1454*c77c4889SXin LI /* 1455*c77c4889SXin LI * Already have a hyperlink selected. 1456*c77c4889SXin LI * Search for the next/previous one in the same line. 1457*c77c4889SXin LI */ 1458*c77c4889SXin LI if (search_type & SRCH_FORW) 1459*c77c4889SXin LI { 1460*c77c4889SXin LI size_t off = (size_t) (osc8_match_end - linepos); 1461*c77c4889SXin LI spos += off; 1462*c77c4889SXin LI sline += off; 1463*c77c4889SXin LI sline_len -= off; 1464*c77c4889SXin LI } else 1465*c77c4889SXin LI { 1466*c77c4889SXin LI sline_len = (size_t) (osc8_match_start - linepos); 1467*c77c4889SXin LI } 1468*c77c4889SXin LI } 1469*c77c4889SXin LI r = osc8_search_line1(search_type, linepos, spos, sline, sline_len, param, clickpos); 1470*c77c4889SXin LI if (r == OSC8_NO_MATCH) 1471*c77c4889SXin LI break; 1472*c77c4889SXin LI if (--*matches <= 0) 1473*c77c4889SXin LI return r; 1474*c77c4889SXin LI } 1475*c77c4889SXin LI return OSC8_NO_MATCH; 1476*c77c4889SXin LI } 1477*c77c4889SXin LI 1478*c77c4889SXin LI /* 1479*c77c4889SXin LI * Shift display to make the currently selected OSC8 hyperlink visible. 1480*c77c4889SXin LI */ 1481*c77c4889SXin LI static void osc8_shift_visible(void) 1482*c77c4889SXin LI { 1483*c77c4889SXin LI if (chop_line()) 1484*c77c4889SXin LI { 1485*c77c4889SXin LI size_t start_off = (size_t)(osc8_match_start - osc8_linepos); 1486*c77c4889SXin LI size_t end_off = (size_t)(osc8_match_end - osc8_linepos); 1487*c77c4889SXin LI shift_visible(osc8_linepos, start_off, end_off); 1488*c77c4889SXin LI } 1489*c77c4889SXin LI /* {{ What about the (plastlinepos != NULL) case in search_range? }} */ 1490*c77c4889SXin LI } 1491*c77c4889SXin LI 1492*c77c4889SXin LI #endif /* OSC8_LINK */ 14932235c7feSXin LI 14942235c7feSXin LI /* 1495a5f0fb15SPaul Saab * Search a subset of the file, specified by start/end position. 1496a5f0fb15SPaul Saab */ 1497d713e089SXin LI static int search_range(POSITION pos, POSITION endpos, int search_type, int matches, int maxlines, POSITION *plinepos, POSITION *pendpos, POSITION *plastlinepos) 1498a5f0fb15SPaul Saab { 1499*c77c4889SXin LI constant char *line; 1500423c5ce5SXin LI char *cline; 1501*c77c4889SXin LI size_t line_len; 15021ede1615STim J. Robbins LINENUM linenum; 1503d713e089SXin LI #define NSP (NUM_SEARCH_COLORS+2) 1504*c77c4889SXin LI constant char *sp[NSP]; 1505*c77c4889SXin LI constant char *ep[NSP]; 1506a5f0fb15SPaul Saab int line_match; 15071ede1615STim J. Robbins int cvt_ops; 1508*c77c4889SXin LI size_t cvt_len; 1509f0be0a1fSXin LI int *chpos; 1510a5f0fb15SPaul Saab POSITION linepos, oldpos; 1511d713e089SXin LI int skip_bytes = 0; 1512*c77c4889SXin LI size_t swidth = (size_t) (sc_width - line_pfx_width()); /*{{type-issue}}*/ 1513*c77c4889SXin LI size_t sheight = (size_t) (sc_height - sindex_from_sline(jump_sline)); 1514a5f0fb15SPaul Saab 1515a5f0fb15SPaul Saab linenum = find_linenum(pos); 1516*c77c4889SXin LI if (nosearch_header_lines && linenum <= header_lines) 1517d713e089SXin LI { 1518d713e089SXin LI linenum = header_lines + 1; 1519d713e089SXin LI pos = find_pos(linenum); 1520d713e089SXin LI } 1521d713e089SXin LI if (pos == NULL_POSITION) 1522d713e089SXin LI return (-1); 1523a5f0fb15SPaul Saab oldpos = pos; 15242235c7feSXin LI /* When the search wraps around, end at starting position. */ 15252235c7feSXin LI if ((search_type & SRCH_WRAP) && endpos == NULL_POSITION) 15262235c7feSXin LI endpos = pos; 1527a5f0fb15SPaul Saab for (;;) 1528a5f0fb15SPaul Saab { 1529a5f0fb15SPaul Saab /* 1530a5f0fb15SPaul Saab * Get lines until we find a matching one or until 1531a5f0fb15SPaul Saab * we hit end-of-file (or beginning-of-file if we're 1532a5f0fb15SPaul Saab * going backwards), or until we hit the end position. 1533a5f0fb15SPaul Saab */ 1534a5f0fb15SPaul Saab if (ABORT_SIGS()) 1535a5f0fb15SPaul Saab { 1536a5f0fb15SPaul Saab /* 1537a5f0fb15SPaul Saab * A signal aborts the search. 1538a5f0fb15SPaul Saab */ 1539a5f0fb15SPaul Saab return (-1); 1540a5f0fb15SPaul Saab } 1541a5f0fb15SPaul Saab 15422235c7feSXin LI if ((endpos != NULL_POSITION && !(search_type & SRCH_WRAP) && 15432235c7feSXin LI (((search_type & SRCH_FORW) && pos >= endpos) || 15442235c7feSXin LI ((search_type & SRCH_BACK) && pos <= endpos))) || maxlines == 0) 1545a5f0fb15SPaul Saab { 1546a5f0fb15SPaul Saab /* 1547a5f0fb15SPaul Saab * Reached end position without a match. 1548a5f0fb15SPaul Saab */ 1549a5f0fb15SPaul Saab if (pendpos != NULL) 1550a5f0fb15SPaul Saab *pendpos = pos; 1551a5f0fb15SPaul Saab return (matches); 1552a5f0fb15SPaul Saab } 1553a5f0fb15SPaul Saab if (maxlines > 0) 1554a5f0fb15SPaul Saab maxlines--; 1555a5f0fb15SPaul Saab 1556a5f0fb15SPaul Saab if (search_type & SRCH_FORW) 1557a5f0fb15SPaul Saab { 1558a5f0fb15SPaul Saab /* 1559a5f0fb15SPaul Saab * Read the next line, and save the 1560a5f0fb15SPaul Saab * starting position of that line in linepos. 1561a5f0fb15SPaul Saab */ 1562a5f0fb15SPaul Saab linepos = pos; 1563720c436cSXin LI pos = forw_raw_line(pos, &line, &line_len); 1564a5f0fb15SPaul Saab if (linenum != 0) 1565a5f0fb15SPaul Saab linenum++; 1566a5f0fb15SPaul Saab } else 1567a5f0fb15SPaul Saab { 1568a5f0fb15SPaul Saab /* 1569a5f0fb15SPaul Saab * Read the previous line and save the 1570a5f0fb15SPaul Saab * starting position of that line in linepos. 1571a5f0fb15SPaul Saab */ 1572720c436cSXin LI pos = back_raw_line(pos, &line, &line_len); 1573a5f0fb15SPaul Saab linepos = pos; 1574a5f0fb15SPaul Saab if (linenum != 0) 1575a5f0fb15SPaul Saab linenum--; 1576a5f0fb15SPaul Saab } 1577a5f0fb15SPaul Saab 1578a5f0fb15SPaul Saab if (pos == NULL_POSITION) 1579a5f0fb15SPaul Saab { 1580a5f0fb15SPaul Saab /* 1581a5f0fb15SPaul Saab * Reached EOF/BOF without a match. 1582a5f0fb15SPaul Saab */ 15832235c7feSXin LI if (search_type & SRCH_WRAP) 15842235c7feSXin LI { 15852235c7feSXin LI /* 15862235c7feSXin LI * The search wraps around the current file, so 15872235c7feSXin LI * try to continue at BOF/EOF. 15882235c7feSXin LI */ 15892235c7feSXin LI if (search_type & SRCH_FORW) 15902235c7feSXin LI { 15912235c7feSXin LI pos = ch_zero(); 15922235c7feSXin LI } else 15932235c7feSXin LI { 15942235c7feSXin LI pos = ch_length(); 15952235c7feSXin LI if (pos == NULL_POSITION) 15962235c7feSXin LI { 15972235c7feSXin LI (void) ch_end_seek(); 15982235c7feSXin LI pos = ch_length(); 15992235c7feSXin LI } 16002235c7feSXin LI } 16012235c7feSXin LI if (pos != NULL_POSITION) { 16022235c7feSXin LI /* 16032235c7feSXin LI * Wrap-around was successful. Clear 16042235c7feSXin LI * the flag so we don't wrap again, and 16052235c7feSXin LI * continue the search at new pos. 16062235c7feSXin LI */ 1607*c77c4889SXin LI search_wrapped = TRUE; 16082235c7feSXin LI search_type &= ~SRCH_WRAP; 16092235c7feSXin LI linenum = find_linenum(pos); 16102235c7feSXin LI continue; 16112235c7feSXin LI } 16122235c7feSXin LI } 1613a5f0fb15SPaul Saab if (pendpos != NULL) 1614a5f0fb15SPaul Saab *pendpos = oldpos; 1615a5f0fb15SPaul Saab return (matches); 1616a5f0fb15SPaul Saab } 1617a5f0fb15SPaul Saab 1618a5f0fb15SPaul Saab /* 1619a5f0fb15SPaul Saab * If we're using line numbers, we might as well 1620a5f0fb15SPaul Saab * remember the information we have now (the position 1621a5f0fb15SPaul Saab * and line number of the current line). 1622a5f0fb15SPaul Saab * Don't do it for every line because it slows down 1623a5f0fb15SPaul Saab * the search. Remember the line number only if 1624a5f0fb15SPaul Saab * we're "far" from the last place we remembered it. 1625a5f0fb15SPaul Saab */ 1626f0be0a1fSXin LI if (linenums && abs((int)(pos - oldpos)) > 2048) 1627a5f0fb15SPaul Saab add_lnum(linenum, pos); 1628a5f0fb15SPaul Saab oldpos = pos; 1629a5f0fb15SPaul Saab 16302235c7feSXin LI #if HILITE_SEARCH 16317374caaaSXin LI if (is_filtered(linepos)) 16327374caaaSXin LI continue; 16332235c7feSXin LI #endif 1634*c77c4889SXin LI if (nosearch_header_cols) 1635d713e089SXin LI skip_bytes = skip_columns(header_cols, &line, &line_len); 1636*c77c4889SXin LI #if OSC8_LINK 1637*c77c4889SXin LI if (search_type & SRCH_OSC8) 1638*c77c4889SXin LI { 1639*c77c4889SXin LI if (osc8_search_line(search_type, linepos, line, line_len, osc8_search_param, NULL_POSITION, &matches) != OSC8_NO_MATCH) 1640*c77c4889SXin LI { 1641*c77c4889SXin LI if (plinepos != NULL) 1642*c77c4889SXin LI *plinepos = linepos; 1643*c77c4889SXin LI osc8_shift_visible(); 1644*c77c4889SXin LI return (0); 1645*c77c4889SXin LI } 1646*c77c4889SXin LI continue; 1647*c77c4889SXin LI } 1648*c77c4889SXin LI #endif 1649a5f0fb15SPaul Saab /* 1650a5f0fb15SPaul Saab * If it's a caseless search, convert the line to lowercase. 1651a5f0fb15SPaul Saab * If we're doing backspace processing, delete backspaces. 1652a5f0fb15SPaul Saab */ 1653d713e089SXin LI cvt_ops = get_cvt_ops(search_type); 1654f0be0a1fSXin LI cvt_len = cvt_length(line_len, cvt_ops); 1655f0be0a1fSXin LI cline = (char *) ecalloc(1, cvt_len); 1656f0be0a1fSXin LI chpos = cvt_alloc_chpos(cvt_len); 1657f0be0a1fSXin LI cvt_text(cline, line, chpos, &line_len, cvt_ops); 1658a5f0fb15SPaul Saab 16597374caaaSXin LI #if HILITE_SEARCH 16607374caaaSXin LI /* 16612235c7feSXin LI * If any filters are in effect, ignore non-matching lines. 16627374caaaSXin LI */ 16632235c7feSXin LI if (filter_infos != NULL && 16642235c7feSXin LI ((search_type & SRCH_FIND_ALL) || 1665a15691bfSXin LI prep_startpos == NULL_POSITION || 16662235c7feSXin LI linepos < prep_startpos || linepos >= prep_endpos)) { 1667d713e089SXin LI if (matches_filters(pos, cline, line_len, chpos, linepos, sp, ep, NSP)) 1668a15691bfSXin LI continue; 16697374caaaSXin LI } 16707374caaaSXin LI #endif 16717374caaaSXin LI 1672a5f0fb15SPaul Saab /* 1673a5f0fb15SPaul Saab * Test the next line to see if we have a match. 1674a5f0fb15SPaul Saab * We are successful if we either want a match and got one, 1675a5f0fb15SPaul Saab * or if we want a non-match and got one. 1676a5f0fb15SPaul Saab */ 1677f0be0a1fSXin LI if (prev_pattern(&search_info)) 1678423c5ce5SXin LI { 167996e55cc7SXin LI line_match = match_pattern(info_compiled(&search_info), search_info.text, 1680d713e089SXin LI cline, line_len, sp, ep, NSP, 0, search_type); 16817374caaaSXin LI if (line_match) 16827374caaaSXin LI { 1683a5f0fb15SPaul Saab /* 1684a5f0fb15SPaul Saab * Got a match. 1685a5f0fb15SPaul Saab */ 1686a5f0fb15SPaul Saab if (search_type & SRCH_FIND_ALL) 1687a5f0fb15SPaul Saab { 1688a5f0fb15SPaul Saab #if HILITE_SEARCH 1689a5f0fb15SPaul Saab /* 1690a5f0fb15SPaul Saab * We are supposed to find all matches in the range. 1691a5f0fb15SPaul Saab * Just add the matches in this line to the 1692a5f0fb15SPaul Saab * hilite list and keep searching. 1693a5f0fb15SPaul Saab */ 1694*c77c4889SXin LI hilite_line(linepos + skip_bytes, cline, line_len, chpos, sp, ep, NSP); 1695a5f0fb15SPaul Saab #endif 1696a5f0fb15SPaul Saab } else if (--matches <= 0) 1697a5f0fb15SPaul Saab { 1698a5f0fb15SPaul Saab /* 1699a5f0fb15SPaul Saab * Found the one match we're looking for. 1700a5f0fb15SPaul Saab * Return it. 1701a5f0fb15SPaul Saab */ 1702a5f0fb15SPaul Saab #if HILITE_SEARCH 170389dd99dcSXin LI if (hilite_search == OPT_ON) 1704a5f0fb15SPaul Saab { 1705a5f0fb15SPaul Saab /* 1706a5f0fb15SPaul Saab * Clear the hilite list and add only 1707a5f0fb15SPaul Saab * the matches in this one line. 1708a5f0fb15SPaul Saab */ 1709a5f0fb15SPaul Saab clr_hilite(); 1710*c77c4889SXin LI hilite_line(linepos + skip_bytes, cline, line_len, chpos, sp, ep, NSP); 1711a5f0fb15SPaul Saab } 1712a5f0fb15SPaul Saab #endif 171395270f73SXin LI if (chop_line()) 17142235c7feSXin LI { 17152235c7feSXin LI /* 17162235c7feSXin LI * If necessary, shift horizontally to make sure 17172235c7feSXin LI * search match is fully visible. 17182235c7feSXin LI */ 1719d713e089SXin LI if (sp[0] != NULL && ep[0] != NULL) 17202235c7feSXin LI { 1721*c77c4889SXin LI size_t start_off = ptr_diff(sp[0], cline); 1722*c77c4889SXin LI size_t end_off = ptr_diff(ep[0], cline); 1723*c77c4889SXin LI shift_visible(linepos, chpos[start_off], chpos[end_off]); 17242235c7feSXin LI } 17252235c7feSXin LI } else if (plastlinepos != NULL) 17262235c7feSXin LI { 17272235c7feSXin LI /* 17282235c7feSXin LI * If the line is so long that the highlighted match 17292235c7feSXin LI * won't be seen when the line is displayed normally 17302235c7feSXin LI * (starting at the first char) because it fills the whole 17312235c7feSXin LI * screen and more, scroll forward until the last char 17322235c7feSXin LI * of the match appears in the last line on the screen. 17332235c7feSXin LI * lastlinepos is the position of the first char of that last line. 17342235c7feSXin LI */ 1735d713e089SXin LI if (ep[0] != NULL) 17362235c7feSXin LI { 1737*c77c4889SXin LI size_t end_off = ptr_diff(ep[0], cline); 17382235c7feSXin LI if (end_off >= swidth * sheight / 4) /* heuristic */ 1739*c77c4889SXin LI *plastlinepos = get_lastlinepos(linepos, linepos + chpos[end_off], (int) sheight); 17402235c7feSXin LI } 17412235c7feSXin LI } 1742423c5ce5SXin LI free(cline); 1743f0be0a1fSXin LI free(chpos); 1744a5f0fb15SPaul Saab if (plinepos != NULL) 1745a5f0fb15SPaul Saab *plinepos = linepos; 1746a5f0fb15SPaul Saab return (0); 1747a5f0fb15SPaul Saab } 1748a5f0fb15SPaul Saab } 1749a5f0fb15SPaul Saab } 17507374caaaSXin LI free(cline); 1751f0be0a1fSXin LI free(chpos); 17527374caaaSXin LI } 17537374caaaSXin LI } 1754a5f0fb15SPaul Saab 1755*c77c4889SXin LI #if OSC8_LINK 1756*c77c4889SXin LI 1757*c77c4889SXin LI /* 1758*c77c4889SXin LI * Search for and select the next OSC8 sequence, forward or backward. 1759*c77c4889SXin LI */ 1760*c77c4889SXin LI public void osc8_search(int search_type, constant char *param, int matches) 1761*c77c4889SXin LI { 1762*c77c4889SXin LI POSITION pos; 1763*c77c4889SXin LI int match; 1764*c77c4889SXin LI 1765*c77c4889SXin LI if (osc8_linepos != NULL_POSITION) 1766*c77c4889SXin LI { 1767*c77c4889SXin LI /* Continue search in same line as current match. */ 1768*c77c4889SXin LI constant char *line; 1769*c77c4889SXin LI size_t line_len; 1770*c77c4889SXin LI pos = forw_raw_line(osc8_linepos, &line, &line_len); 1771*c77c4889SXin LI if (pos != NULL_POSITION) 1772*c77c4889SXin LI { 1773*c77c4889SXin LI if (osc8_search_line(search_type, osc8_linepos, line, line_len, param, NULL_POSITION, &matches) != OSC8_NO_MATCH) 1774*c77c4889SXin LI { 1775*c77c4889SXin LI no_eof_bell = TRUE; 1776*c77c4889SXin LI jump_loc(osc8_linepos, jump_sline); 1777*c77c4889SXin LI no_eof_bell = FALSE; 1778*c77c4889SXin LI osc8_shift_visible(); 1779*c77c4889SXin LI #if HILITE_SEARCH 1780*c77c4889SXin LI repaint_hilite(TRUE); 1781*c77c4889SXin LI #endif 1782*c77c4889SXin LI return; 1783*c77c4889SXin LI } 1784*c77c4889SXin LI } 1785*c77c4889SXin LI search_type |= SRCH_AFTER_TARGET; 1786*c77c4889SXin LI } 1787*c77c4889SXin LI pos = search_pos(search_type); 1788*c77c4889SXin LI if (pos == NULL_POSITION) 1789*c77c4889SXin LI { 1790*c77c4889SXin LI error("Nothing to search", NULL_PARG); 1791*c77c4889SXin LI return; 1792*c77c4889SXin LI } 1793*c77c4889SXin LI osc8_search_param = param; 1794*c77c4889SXin LI match = search_range(pos, NULL_POSITION, search_type | SRCH_OSC8, matches, -1, &pos, NULL, NULL); 1795*c77c4889SXin LI osc8_search_param = NULL; 1796*c77c4889SXin LI if (match != 0) 1797*c77c4889SXin LI { 1798*c77c4889SXin LI error("OSC 8 link not found", NULL_PARG); 1799*c77c4889SXin LI return; 1800*c77c4889SXin LI } 1801*c77c4889SXin LI jump_loc(pos, jump_sline); 1802*c77c4889SXin LI #if HILITE_SEARCH 1803*c77c4889SXin LI repaint_hilite(TRUE); 1804*c77c4889SXin LI #endif 1805*c77c4889SXin LI } 1806*c77c4889SXin LI 1807*c77c4889SXin LI /* 1808*c77c4889SXin LI * If a mouse click is on an OSC 8 link, select the link. 1809*c77c4889SXin LI */ 1810*c77c4889SXin LI public lbool osc8_click(int sindex, int col) 1811*c77c4889SXin LI { 1812*c77c4889SXin LI #if OSC8_LINK 1813*c77c4889SXin LI POSITION linepos = position(sindex); 1814*c77c4889SXin LI POSITION clickpos; 1815*c77c4889SXin LI constant char *line; 1816*c77c4889SXin LI size_t line_len; 1817*c77c4889SXin LI int matches = 1; 1818*c77c4889SXin LI int r; 1819*c77c4889SXin LI 1820*c77c4889SXin LI if (linepos == NULL_POSITION) 1821*c77c4889SXin LI return FALSE; 1822*c77c4889SXin LI clickpos = pos_from_col(linepos, col, NULL_POSITION, -1); 1823*c77c4889SXin LI if (clickpos == NULL_POSITION) 1824*c77c4889SXin LI return FALSE; 1825*c77c4889SXin LI if (forw_raw_line(linepos, &line, &line_len) == NULL_POSITION) 1826*c77c4889SXin LI return FALSE; 1827*c77c4889SXin LI r = osc8_search_line(SRCH_FORW|SRCH_OSC8, linepos, line, line_len, NULL, clickpos, &matches); 1828*c77c4889SXin LI if (r != OSC8_NO_MATCH) 1829*c77c4889SXin LI { 1830*c77c4889SXin LI #if HILITE_SEARCH 1831*c77c4889SXin LI repaint_hilite(TRUE); 1832*c77c4889SXin LI #endif 1833*c77c4889SXin LI if (r == OSC8_ALREADY) 1834*c77c4889SXin LI osc8_open(); 1835*c77c4889SXin LI return TRUE; 1836*c77c4889SXin LI } 1837*c77c4889SXin LI #else 1838*c77c4889SXin LI (void) sindex; (void) col; 1839*c77c4889SXin LI #endif /* OSC8_LINK */ 1840*c77c4889SXin LI return FALSE; 1841*c77c4889SXin LI } 1842*c77c4889SXin LI 1843*c77c4889SXin LI /* 1844*c77c4889SXin LI * Return the length of the scheme prefix in a URI. 1845*c77c4889SXin LI */ 1846*c77c4889SXin LI static int scheme_length(constant char *uri, size_t uri_len) 1847*c77c4889SXin LI { 1848*c77c4889SXin LI size_t plen; 1849*c77c4889SXin LI for (plen = 0; plen < uri_len; plen++) 1850*c77c4889SXin LI if (uri[plen] == ':') 1851*c77c4889SXin LI return plen; 1852*c77c4889SXin LI return 0; 1853*c77c4889SXin LI } 1854*c77c4889SXin LI 1855*c77c4889SXin LI /* 1856*c77c4889SXin LI * Does a URI contain any dangerous characters? 1857*c77c4889SXin LI */ 1858*c77c4889SXin LI static lbool bad_uri(constant char *uri, size_t uri_len) 1859*c77c4889SXin LI { 1860*c77c4889SXin LI size_t i; 1861*c77c4889SXin LI for (i = 0; i < uri_len; i++) 1862*c77c4889SXin LI if (strchr("'\"", uri[i]) != NULL) 1863*c77c4889SXin LI return TRUE; 1864*c77c4889SXin LI return FALSE; 1865*c77c4889SXin LI } 1866*c77c4889SXin LI 1867*c77c4889SXin LI /* 1868*c77c4889SXin LI * Re-read the line containing the selected OSC8 link. 1869*c77c4889SXin LI */ 1870*c77c4889SXin LI static lbool osc8_read_selected(struct osc8_parse_info *op) 1871*c77c4889SXin LI { 1872*c77c4889SXin LI constant char *line; 1873*c77c4889SXin LI size_t line_len; 1874*c77c4889SXin LI POSITION pos; 1875*c77c4889SXin LI 1876*c77c4889SXin LI pos = forw_raw_line(osc8_linepos, &line, &line_len); 1877*c77c4889SXin LI if (pos == NULL_POSITION) 1878*c77c4889SXin LI return FALSE; 1879*c77c4889SXin LI op->osc8_start = &line[osc8_match_start - osc8_linepos]; 1880*c77c4889SXin LI op->osc8_end = &line[osc8_match_end - osc8_linepos]; 1881*c77c4889SXin LI op->params_start = &line[osc8_params_start - osc8_linepos]; 1882*c77c4889SXin LI op->params_end = &line[osc8_params_end - osc8_linepos]; 1883*c77c4889SXin LI op->uri_start = &line[osc8_uri_start - osc8_linepos]; 1884*c77c4889SXin LI op->uri_end = &line[osc8_uri_end - osc8_linepos]; 1885*c77c4889SXin LI return TRUE; 1886*c77c4889SXin LI } 1887*c77c4889SXin LI 1888*c77c4889SXin LI /* 1889*c77c4889SXin LI * Open the currently selected OSC8 link. 1890*c77c4889SXin LI */ 1891*c77c4889SXin LI public void osc8_open(void) 1892*c77c4889SXin LI { 1893*c77c4889SXin LI struct osc8_parse_info op; 1894*c77c4889SXin LI char env_name[64]; 1895*c77c4889SXin LI size_t scheme_len; 1896*c77c4889SXin LI constant char *handler; 1897*c77c4889SXin LI char *open_cmd; 1898*c77c4889SXin LI size_t uri_len; 1899*c77c4889SXin LI FILE *hf; 1900*c77c4889SXin LI static constant char *env_name_pfx = "LESS_OSC8_"; 1901*c77c4889SXin LI 1902*c77c4889SXin LI if (osc8_linepos == NULL_POSITION) 1903*c77c4889SXin LI { 1904*c77c4889SXin LI error("No OSC8 link selected", NULL_PARG); 1905*c77c4889SXin LI return; 1906*c77c4889SXin LI } 1907*c77c4889SXin LI if (!osc8_read_selected(&op)) 1908*c77c4889SXin LI { 1909*c77c4889SXin LI error("Cannot find OSC8 link", NULL_PARG); 1910*c77c4889SXin LI return; 1911*c77c4889SXin LI } 1912*c77c4889SXin LI /* 1913*c77c4889SXin LI * Read a "handler" shell cmd from environment variable "LESS_OSC8_scheme". 1914*c77c4889SXin LI * pr_expand the handler cmd (to expand %o -> osc8_path) and execute it. 1915*c77c4889SXin LI * Handler's stdout is an "opener" shell cmd; execute opener to open the link. 1916*c77c4889SXin LI */ 1917*c77c4889SXin LI uri_len = ptr_diff(op.uri_end, op.uri_start); 1918*c77c4889SXin LI scheme_len = scheme_length(op.uri_start, uri_len); 1919*c77c4889SXin LI if (scheme_len == 0 && op.uri_start[0] == '#') 1920*c77c4889SXin LI { 1921*c77c4889SXin LI /* Link to "id=" in same file. */ 1922*c77c4889SXin LI char *param = ecalloc(uri_len+3, sizeof(char)); 1923*c77c4889SXin LI strcpy(param, "id="); 1924*c77c4889SXin LI strncpy(param+3, op.uri_start+1, uri_len-1); 1925*c77c4889SXin LI param[uri_len+2] = '\0'; 1926*c77c4889SXin LI osc8_search(SRCH_FORW|SRCH_WRAP, param, 1); 1927*c77c4889SXin LI free(param); 1928*c77c4889SXin LI return; 1929*c77c4889SXin LI } 1930*c77c4889SXin LI #if HAVE_POPEN 1931*c77c4889SXin LI if (bad_uri(op.uri_start, uri_len)) 1932*c77c4889SXin LI { 1933*c77c4889SXin LI error("Cannot open link containing dangerous characters", NULL_PARG); 1934*c77c4889SXin LI return; 1935*c77c4889SXin LI } 1936*c77c4889SXin LI SNPRINTF3(env_name, sizeof(env_name), "%s%.*s", env_name_pfx, (int) scheme_len, op.uri_start); 1937*c77c4889SXin LI handler = lgetenv(env_name); 1938*c77c4889SXin LI if (isnullenv(handler) || strcmp(handler, "-") == 0) 1939*c77c4889SXin LI handler = lgetenv("LESS_OSC8_ANY"); 1940*c77c4889SXin LI if (isnullenv(handler)) 1941*c77c4889SXin LI { 1942*c77c4889SXin LI PARG parg; 1943*c77c4889SXin LI parg.p_string = env_name + strlen(env_name_pfx); /* {{ tricky }} */ 1944*c77c4889SXin LI error("No handler for \"%s\" link type", &parg); 1945*c77c4889SXin LI return; 1946*c77c4889SXin LI } 1947*c77c4889SXin LI /* {{ ugly global osc8_path }} */ 1948*c77c4889SXin LI osc8_path = saven(op.uri_start, uri_len); 1949*c77c4889SXin LI hf = popen(pr_expand(handler), "r"); 1950*c77c4889SXin LI free(osc8_path); 1951*c77c4889SXin LI osc8_path = NULL; 1952*c77c4889SXin LI if (hf == NULL) 1953*c77c4889SXin LI { 1954*c77c4889SXin LI PARG parg; 1955*c77c4889SXin LI parg.p_string = env_name; 1956*c77c4889SXin LI error("Cannot execute protocol handler in %s", &parg); 1957*c77c4889SXin LI return; 1958*c77c4889SXin LI } 1959*c77c4889SXin LI open_cmd = readfd(hf); 1960*c77c4889SXin LI pclose(hf); 1961*c77c4889SXin LI if (strncmp(open_cmd, ":e", 2) == 0) 1962*c77c4889SXin LI { 1963*c77c4889SXin LI edit(skipsp(&open_cmd[2])); 1964*c77c4889SXin LI } else 1965*c77c4889SXin LI { 1966*c77c4889SXin LI lsystem(open_cmd, "link done"); 1967*c77c4889SXin LI } 1968*c77c4889SXin LI free(open_cmd); 1969*c77c4889SXin LI #else 1970*c77c4889SXin LI error("Cannot open link because your system does not support popen", NULL_PARG); 1971*c77c4889SXin LI #endif /* HAVE_POPEN */ 1972*c77c4889SXin LI } 1973*c77c4889SXin LI 1974*c77c4889SXin LI /* 1975*c77c4889SXin LI * Jump to the currently selected OSC8 link. 1976*c77c4889SXin LI */ 1977*c77c4889SXin LI public void osc8_jump(void) 1978*c77c4889SXin LI { 1979*c77c4889SXin LI if (osc8_linepos == NULL_POSITION) 1980*c77c4889SXin LI { 1981*c77c4889SXin LI error("No OSC8 link selected", NULL_PARG); 1982*c77c4889SXin LI return; 1983*c77c4889SXin LI } 1984*c77c4889SXin LI jump_loc(osc8_linepos, jump_sline); 1985*c77c4889SXin LI } 1986*c77c4889SXin LI 1987*c77c4889SXin LI #endif /* OSC8_LINK */ 1988*c77c4889SXin LI 1989a5f0fb15SPaul Saab /* 1990720c436cSXin LI * search for a pattern in history. If found, compile that pattern. 1991720c436cSXin LI */ 1992d713e089SXin LI static int hist_pattern(int search_type) 1993720c436cSXin LI { 1994720c436cSXin LI #if CMD_HISTORY 1995*c77c4889SXin LI constant char *pattern; 1996720c436cSXin LI 1997720c436cSXin LI set_mlist(ml_search, 0); 1998720c436cSXin LI pattern = cmd_lastpattern(); 1999720c436cSXin LI if (pattern == NULL) 2000720c436cSXin LI return (0); 2001720c436cSXin LI 20022235c7feSXin LI if (set_pattern(&search_info, pattern, search_type, 1) < 0) 20032235c7feSXin LI return (-1); 2004720c436cSXin LI 2005720c436cSXin LI #if HILITE_SEARCH 2006720c436cSXin LI if (hilite_search == OPT_ONPLUS && !hide_hilite) 2007720c436cSXin LI hilite_screen(); 2008720c436cSXin LI #endif 2009720c436cSXin LI 2010720c436cSXin LI return (1); 2011720c436cSXin LI #else /* CMD_HISTORY */ 2012720c436cSXin LI return (0); 2013720c436cSXin LI #endif /* CMD_HISTORY */ 2014720c436cSXin LI } 2015720c436cSXin LI 2016720c436cSXin LI /* 2017f6b74a7dSXin LI * Change the caseless-ness of searches. 2018f6b74a7dSXin LI * Updates the internal search state to reflect a change in the -i flag. 2019f6b74a7dSXin LI */ 2020d713e089SXin LI public void chg_caseless(void) 2021f6b74a7dSXin LI { 2022d713e089SXin LI if (!search_info.is_ucase_pattern) 2023f6b74a7dSXin LI { 2024f6b74a7dSXin LI /* 202595270f73SXin LI * Pattern did not have uppercase. 202695270f73SXin LI * Set the search caselessness to the global caselessness. 202795270f73SXin LI */ 202895270f73SXin LI is_caseless = caseless; 202995270f73SXin LI /* 203095270f73SXin LI * If regex handles caseless, we need to discard 203195270f73SXin LI * the pattern which was compiled with the old caseless. 203295270f73SXin LI */ 203395270f73SXin LI if (!re_handles_caseless) 203495270f73SXin LI /* We handle caseless, so the pattern doesn't change. */ 203595270f73SXin LI return; 203695270f73SXin LI } 203795270f73SXin LI /* 2038f6b74a7dSXin LI * Regenerate the pattern using the new state. 2039f6b74a7dSXin LI */ 2040f6b74a7dSXin LI clear_pattern(&search_info); 20412235c7feSXin LI (void) hist_pattern(search_info.search_type); 2042f6b74a7dSXin LI } 2043f6b74a7dSXin LI 2044f6b74a7dSXin LI /* 2045a5f0fb15SPaul Saab * Search for the n-th occurrence of a specified pattern, 2046a5f0fb15SPaul Saab * either forward or backward. 2047a5f0fb15SPaul Saab * Return the number of matches not yet found in this file 2048a5f0fb15SPaul Saab * (that is, n minus the number of matches found). 2049a5f0fb15SPaul Saab * Return -1 if the search should be aborted. 2050a5f0fb15SPaul Saab * Caller may continue the search in another file 2051a5f0fb15SPaul Saab * if less than n matches are found in this file. 2052a5f0fb15SPaul Saab */ 2053*c77c4889SXin LI public int search(int search_type, constant char *pattern, int n) 2054a5f0fb15SPaul Saab { 2055a5f0fb15SPaul Saab POSITION pos; 20562235c7feSXin LI POSITION opos; 20572235c7feSXin LI POSITION lastlinepos = NULL_POSITION; 2058a5f0fb15SPaul Saab 2059a5f0fb15SPaul Saab if (pattern == NULL || *pattern == '\0') 2060a5f0fb15SPaul Saab { 2061a5f0fb15SPaul Saab /* 2062a5f0fb15SPaul Saab * A null pattern means use the previously compiled pattern. 2063a5f0fb15SPaul Saab */ 206433096f16SXin LI search_type |= SRCH_AFTER_TARGET; 20652235c7feSXin LI if (!prev_pattern(&search_info)) 2066a5f0fb15SPaul Saab { 20672235c7feSXin LI int r = hist_pattern(search_type); 20682235c7feSXin LI if (r == 0) 2069a5f0fb15SPaul Saab error("No previous regular expression", NULL_PARG); 20702235c7feSXin LI if (r <= 0) 2071a5f0fb15SPaul Saab return (-1); 2072a5f0fb15SPaul Saab } 2073a5f0fb15SPaul Saab if ((search_type & SRCH_NO_REGEX) != 2074f0be0a1fSXin LI (search_info.search_type & SRCH_NO_REGEX)) 2075a5f0fb15SPaul Saab { 2076a5f0fb15SPaul Saab error("Please re-enter search pattern", NULL_PARG); 2077a5f0fb15SPaul Saab return -1; 2078a5f0fb15SPaul Saab } 2079a5f0fb15SPaul Saab #if HILITE_SEARCH 2080b2ea2440SXin LI if (hilite_search == OPT_ON || status_col) 2081a5f0fb15SPaul Saab { 2082a5f0fb15SPaul Saab /* 2083a5f0fb15SPaul Saab * Erase the highlights currently on screen. 2084a5f0fb15SPaul Saab * If the search fails, we'll redisplay them later. 2085a5f0fb15SPaul Saab */ 2086*c77c4889SXin LI repaint_hilite(FALSE); 2087a5f0fb15SPaul Saab } 2088a5f0fb15SPaul Saab if (hilite_search == OPT_ONPLUS && hide_hilite) 2089a5f0fb15SPaul Saab { 2090a5f0fb15SPaul Saab /* 2091a5f0fb15SPaul Saab * Highlight any matches currently on screen, 2092a5f0fb15SPaul Saab * before we actually start the search. 2093a5f0fb15SPaul Saab */ 2094*c77c4889SXin LI hide_hilite = FALSE; 2095a5f0fb15SPaul Saab hilite_screen(); 2096a5f0fb15SPaul Saab } 2097*c77c4889SXin LI hide_hilite = FALSE; 2098a5f0fb15SPaul Saab #endif 2099a5f0fb15SPaul Saab } else 2100a5f0fb15SPaul Saab { 2101a5f0fb15SPaul Saab /* 2102a5f0fb15SPaul Saab * Compile the pattern. 2103a5f0fb15SPaul Saab */ 21042235c7feSXin LI int show_error = !(search_type & SRCH_INCR); 21052235c7feSXin LI if (set_pattern(&search_info, pattern, search_type, show_error) < 0) 2106a5f0fb15SPaul Saab return (-1); 2107a5f0fb15SPaul Saab #if HILITE_SEARCH 2108b2ea2440SXin LI if (hilite_search || status_col) 2109a5f0fb15SPaul Saab { 2110a5f0fb15SPaul Saab /* 2111a5f0fb15SPaul Saab * Erase the highlights currently on screen. 2112a5f0fb15SPaul Saab * Also permanently delete them from the hilite list. 2113a5f0fb15SPaul Saab */ 2114*c77c4889SXin LI repaint_hilite(FALSE); 2115*c77c4889SXin LI hide_hilite = FALSE; 2116a5f0fb15SPaul Saab clr_hilite(); 2117a5f0fb15SPaul Saab } 2118b2ea2440SXin LI if (hilite_search == OPT_ONPLUS || status_col) 2119a5f0fb15SPaul Saab { 2120a5f0fb15SPaul Saab /* 2121a5f0fb15SPaul Saab * Highlight any matches currently on screen, 2122a5f0fb15SPaul Saab * before we actually start the search. 2123a5f0fb15SPaul Saab */ 2124a5f0fb15SPaul Saab hilite_screen(); 2125a5f0fb15SPaul Saab } 2126a5f0fb15SPaul Saab #endif 2127a5f0fb15SPaul Saab } 2128a5f0fb15SPaul Saab 2129a5f0fb15SPaul Saab /* 2130a5f0fb15SPaul Saab * Figure out where to start the search. 2131a5f0fb15SPaul Saab */ 2132a5f0fb15SPaul Saab pos = search_pos(search_type); 21332235c7feSXin LI opos = position(sindex_from_sline(jump_sline)); 2134a5f0fb15SPaul Saab if (pos == NULL_POSITION) 2135a5f0fb15SPaul Saab { 2136a5f0fb15SPaul Saab /* 2137a5f0fb15SPaul Saab * Can't find anyplace to start searching from. 2138a5f0fb15SPaul Saab */ 2139a5f0fb15SPaul Saab if (search_type & SRCH_PAST_EOF) 2140a5f0fb15SPaul Saab return (n); 21412235c7feSXin LI #if HILITE_SEARCH 2142b2ea2440SXin LI if (hilite_search == OPT_ON || status_col) 2143*c77c4889SXin LI repaint_hilite(TRUE); 21442235c7feSXin LI #endif 2145a5f0fb15SPaul Saab error("Nothing to search", NULL_PARG); 2146a5f0fb15SPaul Saab return (-1); 2147a5f0fb15SPaul Saab } 2148a5f0fb15SPaul Saab 2149a5f0fb15SPaul Saab n = search_range(pos, NULL_POSITION, search_type, n, -1, 21502235c7feSXin LI &pos, (POSITION*)NULL, &lastlinepos); 2151*c77c4889SXin LI /* 2152*c77c4889SXin LI * This ABORT_SIGS check ensures that if the user presses interrupt, 2153*c77c4889SXin LI * we don't continue and complete the search. 2154*c77c4889SXin LI * That is, we leave the display unchanged. 2155*c77c4889SXin LI * {{ Is this true? Do we always want to abort the search on interrupt? }} 2156*c77c4889SXin LI */ 2157*c77c4889SXin LI if (ABORT_SIGS()) 2158*c77c4889SXin LI return (-1); 2159a5f0fb15SPaul Saab if (n != 0) 2160a5f0fb15SPaul Saab { 2161a5f0fb15SPaul Saab /* 2162a5f0fb15SPaul Saab * Search was unsuccessful. 2163a5f0fb15SPaul Saab */ 2164a5f0fb15SPaul Saab #if HILITE_SEARCH 2165b2ea2440SXin LI if ((hilite_search == OPT_ON || status_col) && n > 0) 2166a5f0fb15SPaul Saab /* 2167a5f0fb15SPaul Saab * Redisplay old hilites. 2168a5f0fb15SPaul Saab */ 2169*c77c4889SXin LI repaint_hilite(TRUE); 2170a5f0fb15SPaul Saab #endif 2171a5f0fb15SPaul Saab return (n); 2172a5f0fb15SPaul Saab } 2173a5f0fb15SPaul Saab 2174a5f0fb15SPaul Saab if (!(search_type & SRCH_NO_MOVE)) 2175a5f0fb15SPaul Saab { 2176a5f0fb15SPaul Saab /* 2177a5f0fb15SPaul Saab * Go to the matching line. 2178a5f0fb15SPaul Saab */ 21792235c7feSXin LI if (lastlinepos != NULL_POSITION) 21802235c7feSXin LI jump_loc(lastlinepos, BOTTOM); 21812235c7feSXin LI else if (pos != opos) 2182a5f0fb15SPaul Saab jump_loc(pos, jump_sline); 2183a5f0fb15SPaul Saab } 2184a5f0fb15SPaul Saab 2185a5f0fb15SPaul Saab #if HILITE_SEARCH 2186b2ea2440SXin LI if (hilite_search == OPT_ON || status_col) 2187a5f0fb15SPaul Saab /* 2188a5f0fb15SPaul Saab * Display new hilites in the matching line. 2189a5f0fb15SPaul Saab */ 2190*c77c4889SXin LI repaint_hilite(TRUE); 2191a5f0fb15SPaul Saab #endif 2192a5f0fb15SPaul Saab return (0); 2193a5f0fb15SPaul Saab } 2194a5f0fb15SPaul Saab 2195a5f0fb15SPaul Saab #if HILITE_SEARCH 2196a5f0fb15SPaul Saab /* 2197a5f0fb15SPaul Saab * Prepare hilites in a given range of the file. 2198a5f0fb15SPaul Saab * 2199a5f0fb15SPaul Saab * The pair (prep_startpos,prep_endpos) delimits a contiguous region 2200a5f0fb15SPaul Saab * of the file that has been "prepared"; that is, scanned for matches for 2201a5f0fb15SPaul Saab * the current search pattern, and hilites have been created for such matches. 2202a5f0fb15SPaul Saab * If prep_startpos == NULL_POSITION, the prep region is empty. 2203a5f0fb15SPaul Saab * If prep_endpos == NULL_POSITION, the prep region extends to EOF. 2204a5f0fb15SPaul Saab * prep_hilite asks that the range (spos,epos) be covered by the prep region. 2205a5f0fb15SPaul Saab */ 2206d713e089SXin LI public void prep_hilite(POSITION spos, POSITION epos, int maxlines) 2207a5f0fb15SPaul Saab { 2208a5f0fb15SPaul Saab POSITION nprep_startpos = prep_startpos; 2209a5f0fb15SPaul Saab POSITION nprep_endpos = prep_endpos; 2210a5f0fb15SPaul Saab POSITION new_epos; 2211a5f0fb15SPaul Saab POSITION max_epos; 2212a5f0fb15SPaul Saab int result; 2213a5f0fb15SPaul Saab int i; 2214f0be0a1fSXin LI 2215a5f0fb15SPaul Saab /* 2216a5f0fb15SPaul Saab * Search beyond where we're asked to search, so the prep region covers 2217a5f0fb15SPaul Saab * more than we need. Do one big search instead of a bunch of small ones. 2218a5f0fb15SPaul Saab */ 2219*c77c4889SXin LI POSITION SEARCH_MORE = (POSITION) (3*size_linebuf); 2220a5f0fb15SPaul Saab 2221f0be0a1fSXin LI if (!prev_pattern(&search_info) && !is_filtering()) 2222a5f0fb15SPaul Saab return; 2223a5f0fb15SPaul Saab 2224a5f0fb15SPaul Saab /* 2225a15691bfSXin LI * Make sure our prep region always starts at the beginning of 2226a15691bfSXin LI * a line. (search_range takes care of the end boundary below.) 2227a15691bfSXin LI */ 2228*c77c4889SXin LI spos = back_raw_line(spos+1, NULL, NULL); 2229a15691bfSXin LI 2230a15691bfSXin LI /* 2231a5f0fb15SPaul Saab * If we're limited to a max number of lines, figure out the 2232a5f0fb15SPaul Saab * file position we should stop at. 2233a5f0fb15SPaul Saab */ 2234a5f0fb15SPaul Saab if (maxlines < 0) 2235a5f0fb15SPaul Saab max_epos = NULL_POSITION; 2236a5f0fb15SPaul Saab else 2237a5f0fb15SPaul Saab { 2238a5f0fb15SPaul Saab max_epos = spos; 2239a5f0fb15SPaul Saab for (i = 0; i < maxlines; i++) 2240*c77c4889SXin LI max_epos = forw_raw_line(max_epos, NULL, NULL); 2241a5f0fb15SPaul Saab } 2242a5f0fb15SPaul Saab 2243a5f0fb15SPaul Saab /* 2244a5f0fb15SPaul Saab * Find two ranges: 2245a5f0fb15SPaul Saab * The range that we need to search (spos,epos); and the range that 2246a5f0fb15SPaul Saab * the "prep" region will then cover (nprep_startpos,nprep_endpos). 2247a5f0fb15SPaul Saab */ 2248a5f0fb15SPaul Saab 2249a5f0fb15SPaul Saab if (prep_startpos == NULL_POSITION || 2250a5f0fb15SPaul Saab (epos != NULL_POSITION && epos < prep_startpos) || 2251a5f0fb15SPaul Saab spos > prep_endpos) 2252a5f0fb15SPaul Saab { 2253a5f0fb15SPaul Saab /* 2254a5f0fb15SPaul Saab * New range is not contiguous with old prep region. 2255a5f0fb15SPaul Saab * Discard the old prep region and start a new one. 2256a5f0fb15SPaul Saab */ 2257a5f0fb15SPaul Saab clr_hilite(); 22587374caaaSXin LI clr_filter(); 2259a5f0fb15SPaul Saab if (epos != NULL_POSITION) 2260a5f0fb15SPaul Saab epos += SEARCH_MORE; 2261a5f0fb15SPaul Saab nprep_startpos = spos; 2262a5f0fb15SPaul Saab } else 2263a5f0fb15SPaul Saab { 2264a5f0fb15SPaul Saab /* 2265a5f0fb15SPaul Saab * New range partially or completely overlaps old prep region. 2266a5f0fb15SPaul Saab */ 2267a5f0fb15SPaul Saab if (epos == NULL_POSITION) 2268a5f0fb15SPaul Saab { 2269a5f0fb15SPaul Saab /* 2270a5f0fb15SPaul Saab * New range goes to end of file. 2271a5f0fb15SPaul Saab */ 2272a5f0fb15SPaul Saab ; 2273a5f0fb15SPaul Saab } else if (epos > prep_endpos) 2274a5f0fb15SPaul Saab { 2275a5f0fb15SPaul Saab /* 2276a5f0fb15SPaul Saab * New range ends after old prep region. 2277a5f0fb15SPaul Saab * Extend prep region to end at end of new range. 2278a5f0fb15SPaul Saab */ 2279a5f0fb15SPaul Saab epos += SEARCH_MORE; 2280a5f0fb15SPaul Saab } else /* (epos <= prep_endpos) */ 2281a5f0fb15SPaul Saab { 2282a5f0fb15SPaul Saab /* 2283a5f0fb15SPaul Saab * New range ends within old prep region. 2284a5f0fb15SPaul Saab * Truncate search to end at start of old prep region. 2285a5f0fb15SPaul Saab */ 2286a5f0fb15SPaul Saab epos = prep_startpos; 2287a5f0fb15SPaul Saab } 2288a5f0fb15SPaul Saab 2289a5f0fb15SPaul Saab if (spos < prep_startpos) 2290a5f0fb15SPaul Saab { 2291a5f0fb15SPaul Saab /* 2292a5f0fb15SPaul Saab * New range starts before old prep region. 2293a5f0fb15SPaul Saab * Extend old prep region backwards to start at 2294a5f0fb15SPaul Saab * start of new range. 2295a5f0fb15SPaul Saab */ 2296a5f0fb15SPaul Saab if (spos < SEARCH_MORE) 2297a5f0fb15SPaul Saab spos = 0; 2298a5f0fb15SPaul Saab else 2299a5f0fb15SPaul Saab spos -= SEARCH_MORE; 2300a5f0fb15SPaul Saab nprep_startpos = spos; 2301a5f0fb15SPaul Saab } else /* (spos >= prep_startpos) */ 2302a5f0fb15SPaul Saab { 2303a5f0fb15SPaul Saab /* 2304a5f0fb15SPaul Saab * New range starts within or after old prep region. 2305a5f0fb15SPaul Saab * Trim search to start at end of old prep region. 2306a5f0fb15SPaul Saab */ 2307a5f0fb15SPaul Saab spos = prep_endpos; 2308a5f0fb15SPaul Saab } 2309a5f0fb15SPaul Saab } 2310a5f0fb15SPaul Saab 2311a5f0fb15SPaul Saab if (epos != NULL_POSITION && max_epos != NULL_POSITION && 2312a5f0fb15SPaul Saab epos > max_epos) 2313a5f0fb15SPaul Saab /* 2314a5f0fb15SPaul Saab * Don't go past the max position we're allowed. 2315a5f0fb15SPaul Saab */ 2316a5f0fb15SPaul Saab epos = max_epos; 2317a5f0fb15SPaul Saab 2318a5f0fb15SPaul Saab if (epos == NULL_POSITION || epos > spos) 2319a5f0fb15SPaul Saab { 2320f0be0a1fSXin LI int search_type = SRCH_FORW | SRCH_FIND_ALL; 2321d713e089SXin LI search_type |= (search_info.search_type & (SRCH_NO_REGEX|SRCH_SUBSEARCH_ALL)); 2322a15691bfSXin LI for (;;) 2323a15691bfSXin LI { 23242235c7feSXin LI result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos, (POSITION*)NULL); 2325a5f0fb15SPaul Saab if (result < 0) 2326a5f0fb15SPaul Saab return; 2327a5f0fb15SPaul Saab if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) 2328a5f0fb15SPaul Saab nprep_endpos = new_epos; 2329a15691bfSXin LI 2330a15691bfSXin LI /* 2331a15691bfSXin LI * Check both ends of the resulting prep region to 2332a15691bfSXin LI * make sure they're not filtered. If they are, 2333a15691bfSXin LI * keep going at least one more line until we find 2334a15691bfSXin LI * something that isn't filtered, or hit the end. 2335a15691bfSXin LI */ 2336a15691bfSXin LI if (prep_endpos == NULL_POSITION || nprep_endpos > prep_endpos) 2337a15691bfSXin LI { 2338a15691bfSXin LI if (new_epos >= nprep_endpos && is_filtered(new_epos-1)) 2339a15691bfSXin LI { 2340a15691bfSXin LI spos = nprep_endpos; 2341*c77c4889SXin LI epos = forw_raw_line(nprep_endpos, NULL, NULL); 2342a15691bfSXin LI if (epos == NULL_POSITION) 2343a15691bfSXin LI break; 2344a15691bfSXin LI maxlines = 1; 2345a15691bfSXin LI continue; 2346a15691bfSXin LI } 2347a15691bfSXin LI } 2348a15691bfSXin LI 2349a15691bfSXin LI if (prep_startpos == NULL_POSITION || nprep_startpos < prep_startpos) 2350a15691bfSXin LI { 2351a15691bfSXin LI if (nprep_startpos > 0 && is_filtered(nprep_startpos)) 2352a15691bfSXin LI { 2353a15691bfSXin LI epos = nprep_startpos; 2354*c77c4889SXin LI spos = back_raw_line(nprep_startpos, NULL, NULL); 2355a15691bfSXin LI if (spos == NULL_POSITION) 2356a15691bfSXin LI break; 2357a15691bfSXin LI nprep_startpos = spos; 2358a15691bfSXin LI maxlines = 1; 2359a15691bfSXin LI continue; 2360a15691bfSXin LI } 2361a15691bfSXin LI } 2362a15691bfSXin LI break; 2363a15691bfSXin LI } 2364a5f0fb15SPaul Saab } 2365a5f0fb15SPaul Saab prep_startpos = nprep_startpos; 2366a5f0fb15SPaul Saab prep_endpos = nprep_endpos; 2367a5f0fb15SPaul Saab } 23687374caaaSXin LI 23697374caaaSXin LI /* 23707374caaaSXin LI * Set the pattern to be used for line filtering. 23717374caaaSXin LI */ 2372*c77c4889SXin LI public void set_filter_pattern(constant char *pattern, int search_type) 23737374caaaSXin LI { 23742235c7feSXin LI struct pattern_info *filter; 23752235c7feSXin LI 23767374caaaSXin LI clr_filter(); 23777374caaaSXin LI if (pattern == NULL || *pattern == '\0') 23782235c7feSXin LI { 23792235c7feSXin LI /* Clear and free all filters. */ 23802235c7feSXin LI for (filter = filter_infos; filter != NULL; ) 23812235c7feSXin LI { 23822235c7feSXin LI struct pattern_info *next_filter = filter->next; 23832235c7feSXin LI clear_pattern(filter); 23842235c7feSXin LI free(filter); 23852235c7feSXin LI filter = next_filter; 23862235c7feSXin LI } 23872235c7feSXin LI filter_infos = NULL; 23882235c7feSXin LI } else 23892235c7feSXin LI { 23902235c7feSXin LI /* Create a new filter and add it to the filter_infos list. */ 23912235c7feSXin LI filter = ecalloc(1, sizeof(struct pattern_info)); 23922235c7feSXin LI init_pattern(filter); 239395270f73SXin LI if (set_pattern(filter, pattern, search_type, 1) < 0) 239495270f73SXin LI { 239595270f73SXin LI free(filter); 239695270f73SXin LI return; 239795270f73SXin LI } 23982235c7feSXin LI filter->next = filter_infos; 23992235c7feSXin LI filter_infos = filter; 24002235c7feSXin LI } 2401*c77c4889SXin LI screen_trashed(); 24027374caaaSXin LI } 24037374caaaSXin LI 24047374caaaSXin LI /* 24057374caaaSXin LI * Is there a line filter in effect? 24067374caaaSXin LI */ 2407*c77c4889SXin LI public lbool is_filtering(void) 24087374caaaSXin LI { 24097374caaaSXin LI if (ch_getflags() & CH_HELPFILE) 2410*c77c4889SXin LI return (FALSE); 24112235c7feSXin LI return (filter_infos != NULL); 24127374caaaSXin LI } 2413a5f0fb15SPaul Saab #endif 2414a5f0fb15SPaul Saab 2415a5f0fb15SPaul Saab #if HAVE_V8_REGCOMP 2416a5f0fb15SPaul Saab /* 2417a5f0fb15SPaul Saab * This function is called by the V8 regcomp to report 2418a5f0fb15SPaul Saab * errors in regular expressions. 2419a5f0fb15SPaul Saab */ 2420a15691bfSXin LI public int reg_show_error = 1; 2421a15691bfSXin LI 2422*c77c4889SXin LI void regerror(constant char *s) 2423a5f0fb15SPaul Saab { 2424a5f0fb15SPaul Saab PARG parg; 2425a5f0fb15SPaul Saab 2426a15691bfSXin LI if (!reg_show_error) 2427a15691bfSXin LI return; 2428a5f0fb15SPaul Saab parg.p_string = s; 2429a5f0fb15SPaul Saab error("%s", &parg); 2430a5f0fb15SPaul Saab } 2431a5f0fb15SPaul Saab #endif 2432a5f0fb15SPaul Saab 2433