"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