"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/search.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  * Routines to search a file for a pattern.
   14  */
   15 
   16 #include "less.h"
   17 #include "position.h"
   18 #include "charset.h"
   19 
   20 #define	MINPOS(a,b)	(((a) < (b)) ? (a) : (b))
   21 #define	MAXPOS(a,b)	(((a) > (b)) ? (a) : (b))
   22 
   23 #if HAVE_POSIX_REGCOMP
   24 #include <regex.h>
   25 #ifdef REG_EXTENDED
   26 #define	REGCOMP_FLAG	REG_EXTENDED
   27 #else
   28 #define	REGCOMP_FLAG	0
   29 #endif
   30 #endif
   31 #if HAVE_PCRE
   32 #include <pcre.h>
   33 #endif
   34 #if HAVE_RE_COMP
   35 char *re_comp();
   36 int re_exec();
   37 #endif
   38 #if HAVE_REGCMP
   39 char *regcmp();
   40 char *regex();
   41 extern char *__loc1;
   42 #endif
   43 #if HAVE_V8_REGCOMP
   44 #include "regexp.h"
   45 #endif
   46 
   47 static int match();
   48 
   49 extern int sigs;
   50 extern int how_search;
   51 extern int caseless;
   52 extern int linenums;
   53 extern int sc_height;
   54 extern int jump_sline;
   55 extern int bs_mode;
   56 extern int ctldisp;
   57 extern int status_col;
   58 extern void * constant ml_search;
   59 extern POSITION start_attnpos;
   60 extern POSITION end_attnpos;
   61 extern int utf_mode;
   62 extern int screen_trashed;
   63 #if HILITE_SEARCH
   64 extern int hilite_search;
   65 extern int size_linebuf;
   66 extern int squished;
   67 extern int can_goto_line;
   68 static int hide_hilite;
   69 static int oldbot;
   70 static POSITION prep_startpos;
   71 static POSITION prep_endpos;
   72 
   73 struct hilite
   74 {
   75 	struct hilite *hl_next;
   76 	POSITION hl_startpos;
   77 	POSITION hl_endpos;
   78 };
   79 static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
   80 static struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION };
   81 #define	hl_first	hl_next
   82 #endif
   83 
   84 /*
   85  * These are the static variables that represent the "remembered"
   86  * search pattern.
   87  */
   88 #if HAVE_POSIX_REGCOMP
   89 #define DEFINE_PATTERN(name)  static regex_t *name = NULL
   90 #endif
   91 #if HAVE_PCRE
   92 #define DEFINE_PATTERN(name)  pcre *name = NULL;
   93 #endif
   94 #if HAVE_RE_COMP
   95 #define DEFINE_PATTERN(name)  int name = 0;
   96 #endif
   97 #if HAVE_REGCMP
   98 #define DEFINE_PATTERN(name)  static char *name = NULL;
   99 #endif
  100 #if HAVE_V8_REGCOMP
  101 #define DEFINE_PATTERN(name)  static struct regexp *name = NULL;
  102 #endif
  103 
  104 DEFINE_PATTERN(search_pattern);
  105 DEFINE_PATTERN(filter_pattern);
  106 
  107 static int is_caseless;
  108 static int is_ucase_pattern;
  109 static int last_search_type;
  110 static int last_filter_type;
  111 static char *last_pattern = NULL;
  112 
  113 #define	CVT_TO_LC	01	/* Convert upper-case to lower-case */
  114 #define	CVT_BS		02	/* Do backspace processing */
  115 #define	CVT_CRLF	04	/* Remove CR after LF */
  116 #define	CVT_ANSI	010	/* Remove ANSI escape sequences */
  117 
  118 /*
  119  * Get the length of a buffer needed to convert a string.
  120  */
  121 	static int
  122 cvt_length(len, ops)
  123 	int len;
  124 	int ops;
  125 {
  126 	if (utf_mode)
  127 		/*
  128 		 * Just copying a string in UTF-8 mode can cause it to grow
  129 		 * in length.
  130 		 * Six output bytes for one input byte is the worst case
  131 		 * (and unfortunately is far more than is needed in any
  132 		 * non-pathological situation, so this is very wasteful).
  133 		 */
  134 		len *= 6;
  135 	return len + 1;
  136 }
  137 
  138 /*
  139  * Convert text.  Perform the transformations specified by ops.
  140  */
  141 	static void
  142 cvt_text(odst, osrc, lenp, ops)
  143 	char *odst;
  144 	char *osrc;
  145 	int *lenp;
  146 	int ops;
  147 {
  148 	char *dst;
  149 	char *src;
  150 	register char *src_end;
  151 	LWCHAR ch;
  152 
  153 	if (lenp != NULL)
  154 		src_end = osrc + *lenp;
  155 	else
  156 		src_end = osrc + strlen(osrc);
  157 
  158 	for (src = osrc, dst = odst;  src < src_end;  )
  159 	{
  160 		ch = step_char(&src, +1, src_end);
  161 		if ((ops & CVT_TO_LC) && IS_UPPER(ch))
  162 		{
  163 			/* Convert uppercase to lowercase. */
  164 			put_wchar(&dst, TO_LOWER(ch));
  165 		} else if ((ops & CVT_BS) && ch == '\b' && dst > odst)
  166 		{
  167 			/* Delete backspace and preceding char. */
  168 			do {
  169 				dst--;
  170 			} while (dst > odst &&
  171 				!IS_ASCII_OCTET(*dst) && !IS_UTF8_LEAD(*dst));
  172 		} else if ((ops & CVT_ANSI) && IS_CSI_START(ch))
  173 		{
  174 			/* Skip to end of ANSI escape sequence. */
  175 			src++;  /* skip the CSI start char */
  176 			while (src < src_end)
  177 				if (!is_ansi_middle(*src++))
  178 					break;
  179 		} else
  180 			/* Just copy. */
  181 			put_wchar(&dst, ch);
  182 	}
  183 	if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
  184 		dst--;
  185 	*dst = '\0';
  186 	if (lenp != NULL)
  187 		*lenp = dst - odst;
  188 }
  189 
  190 /*
  191  * Determine which conversions to perform.
  192  */
  193 	static int
  194 get_cvt_ops()
  195 {
  196 	int ops = 0;
  197 	if (is_caseless || bs_mode == BS_SPECIAL)
  198 	{
  199 		if (is_caseless)
  200 			ops |= CVT_TO_LC;
  201 		if (bs_mode == BS_SPECIAL)
  202 			ops |= CVT_BS;
  203 		if (bs_mode != BS_CONTROL)
  204 			ops |= CVT_CRLF;
  205 	} else if (bs_mode != BS_CONTROL)
  206 	{
  207 		ops |= CVT_CRLF;
  208 	}
  209 	if (ctldisp == OPT_ONPLUS)
  210 		ops |= CVT_ANSI;
  211 	return (ops);
  212 }
  213 
  214 /*
  215  * Are there any uppercase letters in this string?
  216  */
  217 	static int
  218 is_ucase(str)
  219 	char *str;
  220 {
  221 	char *str_end = str + strlen(str);
  222 	LWCHAR ch;
  223 
  224 	while (str < str_end)
  225 	{
  226 		ch = step_char(&str, +1, str_end);
  227 		if (IS_UPPER(ch))
  228 			return (1);
  229 	}
  230 	return (0);
  231 }
  232 
  233 /*
  234  * Is there a previous (remembered) search pattern?
  235  */
  236 	static int
  237 prev_pattern()
  238 {
  239 	if (last_search_type & SRCH_NO_REGEX)
  240 		return (last_pattern != NULL);
  241 #if HAVE_POSIX_REGCOMP
  242 	return (search_pattern != NULL);
  243 #endif
  244 #if HAVE_PCRE
  245 	return (search_pattern != NULL);
  246 #endif
  247 #if HAVE_RE_COMP
  248 	return (search_pattern != 0);
  249 #endif
  250 #if HAVE_REGCMP
  251 	return (search_pattern != NULL);
  252 #endif
  253 #if HAVE_V8_REGCOMP
  254 	return (search_pattern != NULL);
  255 #endif
  256 #if NO_REGEX
  257 	return (search_pattern != NULL);
  258 #endif
  259 }
  260 
  261 #if HILITE_SEARCH
  262 /*
  263  * Repaint the hilites currently displayed on the screen.
  264  * Repaint each line which contains highlighted text.
  265  * If on==0, force all hilites off.
  266  */
  267 	public void
  268 repaint_hilite(on)
  269 	int on;
  270 {
  271 	int slinenum;
  272 	POSITION pos;
  273 	POSITION epos;
  274 	int save_hide_hilite;
  275 
  276 	if (squished)
  277 		repaint();
  278 
  279 	save_hide_hilite = hide_hilite;
  280 	if (!on)
  281 	{
  282 		if (hide_hilite)
  283 			return;
  284 		hide_hilite = 1;
  285 	}
  286 
  287 	if (!can_goto_line)
  288 	{
  289 		repaint();
  290 		hide_hilite = save_hide_hilite;
  291 		return;
  292 	}
  293 
  294 	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
  295 	{
  296 		pos = position(slinenum);
  297 		if (pos == NULL_POSITION)
  298 			continue;
  299 		epos = position(slinenum+1);
  300 #if 0
  301 		/*
  302 		 * If any character in the line is highlighted,
  303 		 * repaint the line.
  304 		 *
  305 		 * {{ This doesn't work -- if line is drawn with highlights
  306 		 * which should be erased (e.g. toggle -i with status column),
  307 		 * we must redraw the line even if it has no highlights.
  308 		 * For now, just repaint every line. }}
  309 		 */
  310 		if (is_hilited(pos, epos, 1, NULL))
  311 #endif
  312 		{
  313 			(void) forw_line(pos);
  314 			goto_line(slinenum);
  315 			put_line();
  316 		}
  317 	}
  318 	if (!oldbot)
  319 		lower_left();
  320 	hide_hilite = save_hide_hilite;
  321 }
  322 
  323 /*
  324  * Clear the attn hilite.
  325  */
  326 	public void
  327 clear_attn()
  328 {
  329 	int slinenum;
  330 	POSITION old_start_attnpos;
  331 	POSITION old_end_attnpos;
  332 	POSITION pos;
  333 	POSITION epos;
  334 	int moved = 0;
  335 
  336 	if (start_attnpos == NULL_POSITION)
  337 		return;
  338 	old_start_attnpos = start_attnpos;
  339 	old_end_attnpos = end_attnpos;
  340 	start_attnpos = end_attnpos = NULL_POSITION;
  341 
  342 	if (!can_goto_line)
  343 	{
  344 		repaint();
  345 		return;
  346 	}
  347 	if (squished)
  348 		repaint();
  349 
  350 	for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
  351 	{
  352 		pos = position(slinenum);
  353 		if (pos == NULL_POSITION)
  354 			continue;
  355 		epos = position(slinenum+1);
  356 		if (pos < old_end_attnpos &&
  357 		     (epos == NULL_POSITION || epos > old_start_attnpos))
  358 		{
  359 			(void) forw_line(pos);
  360 			goto_line(slinenum);
  361 			put_line();
  362 			moved = 1;
  363 		}
  364 	}
  365 	if (moved)
  366 		lower_left();
  367 }
  368 #endif
  369 
  370 /*
  371  * Hide search string highlighting.
  372  */
  373 	public void
  374 undo_search()
  375 {
  376 	if (!prev_pattern())
  377 	{
  378 		error("No previous regular expression", NULL_PARG);
  379 		return;
  380 	}
  381 #if HILITE_SEARCH
  382 	hide_hilite = !hide_hilite;
  383 	repaint_hilite(1);
  384 #endif
  385 }
  386 
  387 /*
  388  * Compile a search pattern, for future use by match_pattern.
  389  */
  390 	static int
  391 compile_pattern2(pattern, search_type, comp_pattern)
  392 	char *pattern;
  393 	int search_type;
  394 	void **comp_pattern;
  395 {
  396 	if ((search_type & SRCH_NO_REGEX) == 0)
  397 	{
  398 #if HAVE_POSIX_REGCOMP
  399 		regex_t *comp = (regex_t *) ecalloc(1, sizeof(regex_t));
  400 		regex_t **pcomp = (regex_t **) comp_pattern;
  401 		if (regcomp(comp, pattern, REGCOMP_FLAG))
  402 		{
  403 			free(comp);
  404 			error("Invalid pattern", NULL_PARG);
  405 			return (-1);
  406 		}
  407 		if (*pcomp != NULL)
  408 			regfree(*pcomp);
  409 		*pcomp = comp;
  410 #endif
  411 #if HAVE_PCRE
  412 		pcre *comp;
  413 		pcre **pcomp = (pcre **) comp_pattern;
  414 		const char *errstring;
  415 		int erroffset;
  416 		PARG parg;
  417 		comp = pcre_compile(pattern, 0,
  418 				&errstring, &erroffset, NULL);
  419 		if (comp == NULL)
  420 		{
  421 			parg.p_string = (char *) errstring;
  422 			error("%s", &parg);
  423 			return (-1);
  424 		}
  425 		*pcomp = comp;
  426 #endif
  427 #if HAVE_RE_COMP
  428 		PARG parg;
  429 		int *pcomp = (int *) comp_pattern;
  430 		if ((parg.p_string = re_comp(pattern)) != NULL)
  431 		{
  432 			error("%s", &parg);
  433 			return (-1);
  434 		}
  435 		*pcomp = 1;
  436 #endif
  437 #if HAVE_REGCMP
  438 		char *comp;
  439 		char **pcomp = (char **) comp_pattern;
  440 		if ((comp = regcmp(pattern, 0)) == NULL)
  441 		{
  442 			error("Invalid pattern", NULL_PARG);
  443 			return (-1);
  444 		}
  445 		if (pcomp != NULL)
  446 			free(*pcomp);
  447 		*pcomp = comp;
  448 #endif
  449 #if HAVE_V8_REGCOMP
  450 		struct regexp *comp;
  451 		struct regexp **pcomp = (struct regexp **) comp_pattern;
  452 		if ((comp = regcomp(pattern)) == NULL)
  453 		{
  454 			/*
  455 			 * regcomp has already printed an error message
  456 			 * via regerror().
  457 			 */
  458 			return (-1);
  459 		}
  460 		if (*pcomp != NULL)
  461 			free(*pcomp);
  462 		*pcomp = comp;
  463 #endif
  464 	}
  465 
  466 	if (comp_pattern == (void **) &search_pattern)
  467 	{
  468 		if (last_pattern != NULL)
  469 			free(last_pattern);
  470 		last_pattern = (char *) calloc(1, strlen(pattern)+1);
  471 		if (last_pattern != NULL)
  472 			strcpy(last_pattern, pattern);
  473 		last_search_type = search_type;
  474 	} else
  475 	{
  476 		last_filter_type = search_type;
  477 	}
  478 	return (0);
  479 }
  480 
  481 /*
  482  * Like compile_pattern2, but convert the pattern to lowercase if necessary.
  483  */
  484 	static int
  485 compile_pattern(pattern, search_type, comp_pattern)
  486 	char *pattern;
  487 	int search_type;
  488 	void **comp_pattern;
  489 {
  490 	char *cvt_pattern;
  491 	int result;
  492 
  493 	if (caseless != OPT_ONPLUS)
  494 		cvt_pattern = pattern;
  495 	else
  496 	{
  497 		cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
  498 		cvt_text(cvt_pattern, pattern, (int *)NULL, CVT_TO_LC);
  499 	}
  500 	result = compile_pattern2(cvt_pattern, search_type, comp_pattern);
  501 	if (cvt_pattern != pattern)
  502 		free(cvt_pattern);
  503 	return (result);
  504 }
  505 
  506 /*
  507  * Forget that we have a compiled pattern.
  508  */
  509 	static void
  510 uncompile_pattern(pattern)
  511 	void **pattern;
  512 {
  513 #if HAVE_POSIX_REGCOMP
  514 	regex_t **pcomp = (regex_t **) pattern;
  515 	if (*pcomp != NULL)
  516 		regfree(*pcomp);
  517 	*pcomp = NULL;
  518 #endif
  519 #if HAVE_PCRE
  520 	pcre **pcomp = (pcre **) pattern;
  521 	if (*pcomp != NULL)
  522 		pcre_free(*pcomp);
  523 	*pcomp = NULL;
  524 #endif
  525 #if HAVE_RE_COMP
  526 	int *pcomp = (int *) pattern;
  527 	*pcomp = 0;
  528 #endif
  529 #if HAVE_REGCMP
  530 	char **pcomp = (char **) pattern;
  531 	if (*pcomp != NULL)
  532 		free(*pcomp);
  533 	*pcomp = NULL;
  534 #endif
  535 #if HAVE_V8_REGCOMP
  536 	struct regexp **pcomp = (struct regexp **) pattern;
  537 	if (*pcomp != NULL)
  538 		free(*pcomp);
  539 	*pcomp = NULL;
  540 #endif
  541 }
  542 
  543 	static void
  544 uncompile_search_pattern()
  545 {
  546 	uncompile_pattern(&search_pattern);
  547 	last_pattern = NULL;
  548 }
  549 
  550 	static void
  551 uncompile_filter_pattern()
  552 {
  553 	uncompile_pattern(&filter_pattern);
  554 }
  555 
  556 /*
  557  * Is a compiled pattern null?
  558  */
  559 	static int
  560 is_null_pattern(pattern)
  561 	void *pattern;
  562 {
  563 #if HAVE_POSIX_REGCOMP
  564 	return (pattern == NULL);
  565 #endif
  566 #if HAVE_PCRE
  567 	return (pattern == NULL);
  568 #endif
  569 #if HAVE_RE_COMP
  570 	return (pattern == 0);
  571 #endif
  572 #if HAVE_REGCMP
  573 	return (pattern == NULL);
  574 #endif
  575 #if HAVE_V8_REGCOMP
  576 	return (pattern == NULL);
  577 #endif
  578 }
  579 
  580 /*
  581  * Perform a pattern match with the previously compiled pattern.
  582  * Set sp and ep to the start and end of the matched string.
  583  */
  584 	static int
  585 match_pattern(pattern, line, line_len, sp, ep, notbol, search_type)
  586 	void *pattern;
  587 	char *line;
  588 	int line_len;
  589 	char **sp;
  590 	char **ep;
  591 	int notbol;
  592 	int search_type;
  593 {
  594 	int matched;
  595 #if HAVE_POSIX_REGCOMP
  596 	regex_t *spattern = (regex_t *) pattern;
  597 #endif
  598 #if HAVE_PCRE
  599 	pcre *spattern = (pcre *) pattern;
  600 #endif
  601 #if HAVE_RE_COMP
  602 	int spattern = (int) pattern;
  603 #endif
  604 #if HAVE_REGCMP
  605 	char *spattern = (char *) pattern;
  606 #endif
  607 #if HAVE_V8_REGCOMP
  608 	struct regexp *spattern = (struct regexp *) pattern;
  609 #endif
  610 
  611 	if (search_type & SRCH_NO_REGEX)
  612 		return (match(last_pattern, strlen(last_pattern), line, line_len, sp, ep));
  613 
  614 #if HAVE_POSIX_REGCOMP
  615 	{
  616 		regmatch_t rm;
  617 		int flags = (notbol) ? REG_NOTBOL : 0;
  618 		matched = !regexec(spattern, line, 1, &rm, flags);
  619 		if (matched)
  620 		{
  621 #ifndef __WATCOMC__
  622 			*sp = line + rm.rm_so;
  623 			*ep = line + rm.rm_eo;
  624 #else
  625 			*sp = rm.rm_sp;
  626 			*ep = rm.rm_ep;
  627 #endif
  628 		}
  629 	}
  630 #endif
  631 #if HAVE_PCRE
  632 	{
  633 		int flags = (notbol) ? PCRE_NOTBOL : 0;
  634 		int ovector[3];
  635 		matched = pcre_exec(spattern, NULL, line, line_len,
  636 			0, flags, ovector, 3) >= 0;
  637 		if (matched)
  638 		{
  639 			*sp = line + ovector[0];
  640 			*ep = line + ovector[1];
  641 		}
  642 	}
  643 #endif
  644 #if HAVE_RE_COMP
  645 	matched = (re_exec(line) == 1);
  646 	/*
  647 	 * re_exec doesn't seem to provide a way to get the matched string.
  648 	 */
  649 	*sp = *ep = NULL;
  650 #endif
  651 #if HAVE_REGCMP
  652 	*ep = regex(spattern, line);
  653 	matched = (*ep != NULL);
  654 	if (matched)
  655 		*sp = __loc1;
  656 #endif
  657 #if HAVE_V8_REGCOMP
  658 #if HAVE_REGEXEC2
  659 	matched = regexec2(spattern, line, notbol);
  660 #else
  661 	matched = regexec(spattern, line);
  662 #endif
  663 	if (matched)
  664 	{
  665 		*sp = spattern->startp[0];
  666 		*ep = spattern->endp[0];
  667 	}
  668 #endif
  669 #if NO_REGEX
  670 	matched = match(last_pattern, strlen(last_pattern), line, line_len, sp, ep);
  671 #endif
  672 	matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
  673 			((search_type & SRCH_NO_MATCH) && !matched);
  674 	return (matched);
  675 }
  676 
  677 #if HILITE_SEARCH
  678 /*
  679  * Clear the hilite list.
  680  */
  681 	public void
  682 clr_hlist(anchor)
  683 	struct hilite *anchor;
  684 {
  685 	struct hilite *hl;
  686 	struct hilite *nexthl;
  687 
  688 	for (hl = anchor->hl_first;  hl != NULL;  hl = nexthl)
  689 	{
  690 		nexthl = hl->hl_next;
  691 		free((void*)hl);
  692 	}
  693 	anchor->hl_first = NULL;
  694 	prep_startpos = prep_endpos = NULL_POSITION;
  695 }
  696 
  697 	public void
  698 clr_hilite()
  699 {
  700 	clr_hlist(&hilite_anchor);
  701 }
  702 
  703 	public void
  704 clr_filter()
  705 {
  706 	clr_hlist(&filter_anchor);
  707 }
  708 
  709 /*
  710  * Should any characters in a specified range be highlighted?
  711  */
  712 	static int
  713 is_hilited_range(pos, epos)
  714 	POSITION pos;
  715 	POSITION epos;
  716 {
  717 	struct hilite *hl;
  718 
  719 	/*
  720 	 * Look at each highlight and see if any part of it falls in the range.
  721 	 */
  722 	for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
  723 	{
  724 		if (hl->hl_endpos > pos &&
  725 		    (epos == NULL_POSITION || epos > hl->hl_startpos))
  726 			return (1);
  727 	}
  728 	return (0);
  729 }
  730 
  731 /*
  732  * Is a line "filtered" -- that is, should it be hidden?
  733  */
  734 	public int
  735 is_filtered(pos)
  736 	POSITION pos;
  737 {
  738 	struct hilite *hl;
  739 
  740 	if (ch_getflags() & CH_HELPFILE)
  741 		return (0);
  742 
  743 	/*
  744 	 * Look at each filter and see if the start position
  745 	 * equals the start position of the line.
  746 	 */
  747 	for (hl = filter_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
  748 	{
  749 		if (hl->hl_startpos == pos)
  750 			return (1);
  751 	}
  752 	return (0);
  753 }
  754 
  755 /*
  756  * Should any characters in a specified range be highlighted?
  757  * If nohide is nonzero, don't consider hide_hilite.
  758  */
  759 	public int
  760 is_hilited(pos, epos, nohide, p_matches)
  761 	POSITION pos;
  762 	POSITION epos;
  763 	int nohide;
  764 	int *p_matches;
  765 {
  766 	int match;
  767 
  768 	if (p_matches != NULL)
  769 		*p_matches = 0;
  770 
  771 	if (!status_col &&
  772 	    start_attnpos != NULL_POSITION &&
  773 	    pos < end_attnpos &&
  774 	     (epos == NULL_POSITION || epos > start_attnpos))
  775 		/*
  776 		 * The attn line overlaps this range.
  777 		 */
  778 		return (1);
  779 
  780 	match = is_hilited_range(pos, epos);
  781 	if (!match)
  782 		return (0);
  783 
  784 	if (p_matches != NULL)
  785 		/*
  786 		 * Report matches, even if we're hiding highlights.
  787 		 */
  788 		*p_matches = 1;
  789 
  790 	if (hilite_search == 0)
  791 		/*
  792 		 * Not doing highlighting.
  793 		 */
  794 		return (0);
  795 
  796 	if (!nohide && hide_hilite)
  797 		/*
  798 		 * Highlighting is hidden.
  799 		 */
  800 		return (0);
  801 
  802 	return (1);
  803 }
  804 
  805 /*
  806  * Add a new hilite to a hilite list.
  807  */
  808 	static void
  809 add_hilite(anchor, hl)
  810 	struct hilite *anchor;
  811 	struct hilite *hl;
  812 {
  813 	struct hilite *ihl;
  814 
  815 	/*
  816 	 * Hilites are sorted in the list; find where new one belongs.
  817 	 * Insert new one after ihl.
  818 	 */
  819 	for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
  820 	{
  821 		if (ihl->hl_next->hl_startpos > hl->hl_startpos)
  822 			break;
  823 	}
  824 
  825 	/*
  826 	 * Truncate hilite so it doesn't overlap any existing ones
  827 	 * above and below it.
  828 	 */
  829 	if (ihl != anchor)
  830 		hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
  831 	if (ihl->hl_next != NULL)
  832 		hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
  833 	if (hl->hl_startpos >= hl->hl_endpos)
  834 	{
  835 		/*
  836 		 * Hilite was truncated out of existence.
  837 		 */
  838 		free(hl);
  839 		return;
  840 	}
  841 	hl->hl_next = ihl->hl_next;
  842 	ihl->hl_next = hl;
  843 }
  844 
  845 /*
  846  * Adjust hl_startpos & hl_endpos to account for processing by cvt_text.
  847  */
  848 	static void
  849 adj_hilite(anchor, linepos, cvt_ops)
  850 	struct hilite *anchor;
  851 	POSITION linepos;
  852 	int cvt_ops;
  853 {
  854 	char *line;
  855 	char *oline;
  856 	int line_len;
  857 	char *line_end;
  858 	struct hilite *hl;
  859 	int checkstart;
  860 	POSITION opos;
  861 	POSITION npos;
  862 	LWCHAR ch;
  863 	int ncwidth;
  864 
  865 	/*
  866 	 * The line was already scanned and hilites were added (in hilite_line).
  867 	 * But it was assumed that each char position in the line
  868 	 * correponds to one char position in the file.
  869 	 * This may not be true if cvt_text modified the line.
  870 	 * Get the raw line again.  Look at each character.
  871 	 */
  872 	(void) forw_raw_line(linepos, &line, &line_len);
  873 	line_end = line + line_len;
  874 	opos = npos = linepos;
  875 	hl = anchor->hl_first;
  876 	checkstart = TRUE;
  877 	while (hl != NULL)
  878 	{
  879 		/*
  880 		 * See if we need to adjust the current hl_startpos or
  881 		 * hl_endpos.  After adjusting startpos[i], move to endpos[i].
  882 		 * After adjusting endpos[i], move to startpos[i+1].
  883 		 * The hilite list must be sorted thus:
  884 		 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
  885 		 */
  886 		if (checkstart && hl->hl_startpos == opos)
  887 		{
  888 			hl->hl_startpos = npos;
  889 			checkstart = FALSE;
  890 			continue; /* {{ not really necessary }} */
  891 		} else if (!checkstart && hl->hl_endpos == opos)
  892 		{
  893 			hl->hl_endpos = npos;
  894 			checkstart = TRUE;
  895 			hl = hl->hl_next;
  896 			continue; /* {{ necessary }} */
  897 		}
  898 		if (line == line_end)
  899 			break;
  900 
  901 		/* Get the next char from the line. */
  902 		oline = line;
  903 		ch = step_char(&line, +1, line_end);
  904 		ncwidth = line - oline;
  905 		npos += ncwidth;
  906 
  907 		/* Figure out how this char was processed by cvt_text. */
  908 		if ((cvt_ops & CVT_BS) && ch == '\b')
  909 		{
  910 			/* Skip the backspace and the following char. */
  911 			oline = line;
  912 			ch = step_char(&line, +1, line_end);
  913 			ncwidth = line - oline;
  914 			npos += ncwidth;
  915 		} else if ((cvt_ops & CVT_TO_LC) && IS_UPPER(ch))
  916 		{
  917 			/* Converted uppercase to lower.
  918 			 * Note that this may have changed the number of bytes
  919 			 * that the character occupies. */
  920 			char dbuf[6];
  921 			char *dst = dbuf;
  922 			put_wchar(&dst, TO_LOWER(ch));
  923 			opos += dst - dbuf;
  924 		} else if ((cvt_ops & CVT_ANSI) && IS_CSI_START(ch))
  925 		{
  926 			/* Skip to end of ANSI escape sequence. */
  927 			line++;  /* skip the CSI start char */
  928 			npos++;
  929 			while (line < line_end)
  930 			{
  931 				npos++;
  932 				if (!is_ansi_middle(*line++))
  933 					break;
  934 			}
  935 		} else
  936 		{
  937 			/* Ordinary unprocessed character. */
  938 			opos += ncwidth;
  939 		}
  940 	}
  941 }
  942 
  943 /*
  944  * Make a hilite for each string in a physical line which matches
  945  * the current pattern.
  946  * sp,ep delimit the first match already found.
  947  */
  948 	static void
  949 hilite_line(linepos, line, line_len, sp, ep, cvt_ops)
  950 	POSITION linepos;
  951 	char *line;
  952 	int line_len;
  953 	char *sp;
  954 	char *ep;
  955 	int cvt_ops;
  956 {
  957 	char *searchp;
  958 	char *line_end = line + line_len;
  959 	struct hilite *hl;
  960 	struct hilite hilites;
  961 
  962 	if (sp == NULL || ep == NULL)
  963 		return;
  964 	/*
  965 	 * sp and ep delimit the first match in the line.
  966 	 * Mark the corresponding file positions, then
  967 	 * look for further matches and mark them.
  968 	 * {{ This technique, of calling match_pattern on subsequent
  969 	 *    substrings of the line, may mark more than is correct
  970 	 *    if the pattern starts with "^".  This bug is fixed
  971 	 *    for those regex functions that accept a notbol parameter
  972 	 *    (currently POSIX, PCRE and V8-with-regexec2). }}
  973 	 */
  974 	searchp = line;
  975 	/*
  976 	 * Put the hilites into a temporary list until they're adjusted.
  977 	 */
  978 	hilites.hl_first = NULL;
  979 	do {
  980 		if (ep > sp)
  981 		{
  982 			/*
  983 			 * Assume that each char position in the "line"
  984 			 * buffer corresponds to one char position in the file.
  985 			 * This is not quite true; we need to adjust later.
  986 			 */
  987 			hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
  988 			hl->hl_startpos = linepos + (sp-line);
  989 			hl->hl_endpos = linepos + (ep-line);
  990 			add_hilite(&hilites, hl);
  991 		}
  992 		/*
  993 		 * If we matched more than zero characters,
  994 		 * move to the first char after the string we matched.
  995 		 * If we matched zero, just move to the next char.
  996 		 */
  997 		if (ep > searchp)
  998 			searchp = ep;
  999 		else if (searchp != line_end)
 1000 			searchp++;
 1001 		else /* end of line */
 1002 			break;
 1003 	} while (match_pattern(search_pattern, searchp, line_end - searchp, &sp, &ep, 1, last_search_type));
 1004 
 1005 	/*
 1006 	 * If there were backspaces in the original line, they
 1007 	 * were removed, and hl_startpos/hl_endpos are not correct.
 1008 	 * {{ This is very ugly. }}
 1009 	 */
 1010 	adj_hilite(&hilites, linepos, cvt_ops);
 1011 
 1012 	/*
 1013 	 * Now put the hilites into the real list.
 1014 	 */
 1015 	while ((hl = hilites.hl_next) != NULL)
 1016 	{
 1017 		hilites.hl_next = hl->hl_next;
 1018 		add_hilite(&hilite_anchor, hl);
 1019 	}
 1020 }
 1021 #endif
 1022 
 1023 /*
 1024  * Change the caseless-ness of searches.
 1025  * Updates the internal search state to reflect a change in the -i flag.
 1026  */
 1027 	public void
 1028 chg_caseless()
 1029 {
 1030 	if (!is_ucase_pattern)
 1031 		/*
 1032 		 * Pattern did not have uppercase.
 1033 		 * Just set the search caselessness to the global caselessness.
 1034 		 */
 1035 		is_caseless = caseless;
 1036 	else
 1037 		/*
 1038 		 * Pattern did have uppercase.
 1039 		 * Discard the pattern; we can't change search caselessness now.
 1040 		 */
 1041 		uncompile_search_pattern();
 1042 }
 1043 
 1044 #if HILITE_SEARCH
 1045 /*
 1046  * Find matching text which is currently on screen and highlight it.
 1047  */
 1048 	static void
 1049 hilite_screen()
 1050 {
 1051 	struct scrpos scrpos;
 1052 
 1053 	get_scrpos(&scrpos);
 1054 	if (scrpos.pos == NULL_POSITION)
 1055 		return;
 1056 	prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
 1057 	repaint_hilite(1);
 1058 }
 1059 
 1060 /*
 1061  * Change highlighting parameters.
 1062  */
 1063 	public void
 1064 chg_hilite()
 1065 {
 1066 	/*
 1067 	 * Erase any highlights currently on screen.
 1068 	 */
 1069 	clr_hilite();
 1070 	hide_hilite = 0;
 1071 
 1072 	if (hilite_search == OPT_ONPLUS)
 1073 		/*
 1074 		 * Display highlights.
 1075 		 */
 1076 		hilite_screen();
 1077 }
 1078 #endif
 1079 
 1080 /*
 1081  * Figure out where to start a search.
 1082  */
 1083 	static POSITION
 1084 search_pos(search_type)
 1085 	int search_type;
 1086 {
 1087 	POSITION pos;
 1088 	int linenum;
 1089 
 1090 	if (empty_screen())
 1091 	{
 1092 		/*
 1093 		 * Start at the beginning (or end) of the file.
 1094 		 * The empty_screen() case is mainly for
 1095 		 * command line initiated searches;
 1096 		 * for example, "+/xyz" on the command line.
 1097 		 * Also for multi-file (SRCH_PAST_EOF) searches.
 1098 		 */
 1099 		if (search_type