2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
9 * For more information, see the README file.
13 * Routines to search a file for a pattern.
21 #define MINPOS(a, b) (((a) < (b)) ? (a) : (b))
22 #define MAXPOS(a, b) (((a) > (b)) ? (a) : (b))
24 extern volatile sig_atomic_t sigs
;
25 extern int how_search
;
29 extern int jump_sline
;
32 extern int status_col
;
33 extern void *const ml_search
;
34 extern off_t start_attnpos
;
35 extern off_t end_attnpos
;
37 extern int screen_trashed
;
38 extern int hilite_search
;
39 extern int size_linebuf
;
41 extern int can_goto_line
;
42 static int hide_hilite
;
43 static off_t prep_startpos
;
44 static off_t prep_endpos
;
45 static int is_caseless
;
46 static int is_ucase_pattern
;
49 struct hilite
*hl_next
;
53 static struct hilite hilite_anchor
= { NULL
, -1, -1 };
54 static struct hilite filter_anchor
= { NULL
, -1, -1 };
55 #define hl_first hl_next
58 * These are the static variables that represent the "remembered"
59 * search pattern and filter pattern.
67 #define info_compiled(info) ((info)->compiled)
69 static struct pattern_info search_info
;
70 static struct pattern_info filter_info
;
73 * Are there any uppercase letters in this string?
78 char *str_end
= str
+ strlen(str
);
81 while (str
< str_end
) {
82 ch
= step_char(&str
, +1, str_end
);
90 * Compile and save a search pattern.
93 set_pattern(struct pattern_info
*info
, char *pattern
, int search_type
)
96 info
->compiled
= NULL
;
97 else if (compile_pattern(pattern
, search_type
, &info
->compiled
) < 0)
99 /* Pattern compiled successfully; save the text too. */
103 info
->text
= estrdup(pattern
);
104 info
->search_type
= search_type
;
107 * Ignore case if -I is set OR
108 * -i is set AND the pattern is all lowercase.
110 is_ucase_pattern
= is_ucase(pattern
);
111 if (is_ucase_pattern
&& caseless
!= OPT_ONPLUS
)
114 is_caseless
= caseless
;
119 * Discard a saved pattern.
122 clear_pattern(struct pattern_info
*info
)
126 uncompile_pattern(&info
->compiled
);
130 * Initialize saved pattern to nothing.
133 init_pattern(struct pattern_info
*info
)
135 info
->compiled
= NULL
;
137 info
->search_type
= 0;
141 * Initialize search variables.
146 init_pattern(&search_info
);
147 init_pattern(&filter_info
);
151 * Determine which text conversions to perform before pattern matching.
157 if (is_caseless
|| bs_mode
== BS_SPECIAL
) {
160 if (bs_mode
== BS_SPECIAL
)
162 if (bs_mode
!= BS_CONTROL
)
164 } else if (bs_mode
!= BS_CONTROL
) {
167 if (ctldisp
== OPT_ONPLUS
)
173 * Is there a previous (remembered) search pattern?
176 prev_pattern(struct pattern_info
*info
)
178 if ((info
->search_type
& SRCH_NO_REGEX
) == 0)
179 return (info
->compiled
!= NULL
);
180 return (info
->text
!= NULL
);
184 * Repaint the hilites currently displayed on the screen.
185 * Repaint each line which contains highlighted text.
186 * If on==0, force all hilites off.
189 repaint_hilite(int on
)
193 int save_hide_hilite
;
198 save_hide_hilite
= hide_hilite
;
205 if (!can_goto_line
) {
207 hide_hilite
= save_hide_hilite
;
211 for (slinenum
= TOP
; slinenum
< TOP
+ sc_height
-1; slinenum
++) {
212 pos
= position(slinenum
);
215 (void) forw_line(pos
);
220 hide_hilite
= save_hide_hilite
;
224 * Clear the attn hilite.
230 off_t old_start_attnpos
;
231 off_t old_end_attnpos
;
236 if (start_attnpos
== -1)
238 old_start_attnpos
= start_attnpos
;
239 old_end_attnpos
= end_attnpos
;
240 start_attnpos
= end_attnpos
= -1;
242 if (!can_goto_line
) {
249 for (slinenum
= TOP
; slinenum
< TOP
+ sc_height
-1; slinenum
++) {
250 pos
= position(slinenum
);
253 epos
= position(slinenum
+1);
254 if (pos
< old_end_attnpos
&&
255 (epos
== -1 || epos
> old_start_attnpos
)) {
256 (void) forw_line(pos
);
267 * Hide search string highlighting.
272 if (!prev_pattern(&search_info
)) {
273 error("No previous regular expression", NULL
);
276 hide_hilite
= !hide_hilite
;
281 * Clear the hilite list.
284 clr_hlist(struct hilite
*anchor
)
287 struct hilite
*nexthl
;
289 for (hl
= anchor
->hl_first
; hl
!= NULL
; hl
= nexthl
) {
290 nexthl
= hl
->hl_next
;
293 anchor
->hl_first
= NULL
;
294 prep_startpos
= prep_endpos
= -1;
300 clr_hlist(&hilite_anchor
);
306 clr_hlist(&filter_anchor
);
310 * Should any characters in a specified range be highlighted?
313 is_hilited_range(off_t pos
, off_t epos
)
318 * Look at each highlight and see if any part of it falls in the range.
320 for (hl
= hilite_anchor
.hl_first
; hl
!= NULL
; hl
= hl
->hl_next
) {
321 if (hl
->hl_endpos
> pos
&&
322 (epos
== -1 || epos
> hl
->hl_startpos
))
329 * Is a line "filtered" -- that is, should it be hidden?
332 is_filtered(off_t pos
)
336 if (ch_getflags() & CH_HELPFILE
)
340 * Look at each filter and see if the start position
341 * equals the start position of the line.
343 for (hl
= filter_anchor
.hl_first
; hl
!= NULL
; hl
= hl
->hl_next
) {
344 if (hl
->hl_startpos
== pos
)
351 * Should any characters in a specified range be highlighted?
352 * If nohide is nonzero, don't consider hide_hilite.
355 is_hilited(off_t pos
, off_t epos
, int nohide
, int *p_matches
)
359 if (p_matches
!= NULL
)
363 start_attnpos
!= -1 &&
365 (epos
== -1 || epos
> start_attnpos
))
367 * The attn line overlaps this range.
371 match
= is_hilited_range(pos
, epos
);
375 if (p_matches
!= NULL
)
377 * Report matches, even if we're hiding highlights.
381 if (hilite_search
== 0)
383 * Not doing highlighting.
387 if (!nohide
&& hide_hilite
)
389 * Highlighting is hidden.
397 * Add a new hilite to a hilite list.
400 add_hilite(struct hilite
*anchor
, struct hilite
*hl
)
405 * Hilites are sorted in the list; find where new one belongs.
406 * Insert new one after ihl.
408 for (ihl
= anchor
; ihl
->hl_next
!= NULL
; ihl
= ihl
->hl_next
)
410 if (ihl
->hl_next
->hl_startpos
> hl
->hl_startpos
)
415 * Truncate hilite so it doesn't overlap any existing ones
416 * above and below it.
419 hl
->hl_startpos
= MAXPOS(hl
->hl_startpos
, ihl
->hl_endpos
);
420 if (ihl
->hl_next
!= NULL
)
421 hl
->hl_endpos
= MINPOS(hl
->hl_endpos
,
422 ihl
->hl_next
->hl_startpos
);
423 if (hl
->hl_startpos
>= hl
->hl_endpos
) {
425 * Hilite was truncated out of existence.
430 hl
->hl_next
= ihl
->hl_next
;
435 * Hilight every character in a range of displayed characters.
438 create_hilites(off_t linepos
, int start_index
, int end_index
, int *chpos
)
443 /* Start the first hilite. */
444 hl
= ecalloc(1, sizeof (struct hilite
));
445 hl
->hl_startpos
= linepos
+ chpos
[start_index
];
448 * Step through the displayed chars.
449 * If the source position (before cvt) of the char is one more
450 * than the source pos of the previous char (the usual case),
451 * just increase the size of the current hilite by one.
452 * Otherwise (there are backspaces or something involved),
453 * finish the current hilite and start a new one.
455 for (i
= start_index
+1; i
<= end_index
; i
++) {
456 if (chpos
[i
] != chpos
[i
-1] + 1 || i
== end_index
) {
457 hl
->hl_endpos
= linepos
+ chpos
[i
-1] + 1;
458 add_hilite(&hilite_anchor
, hl
);
459 /* Start new hilite unless this is the last char. */
461 hl
= ecalloc(1, sizeof (struct hilite
));
462 hl
->hl_startpos
= linepos
+ chpos
[i
];
469 * Make a hilite for each string in a physical line which matches
470 * the current pattern.
471 * sp,ep delimit the first match already found.
474 hilite_line(off_t linepos
, char *line
, int line_len
, int *chpos
,
478 char *line_end
= line
+ line_len
;
481 * sp and ep delimit the first match in the line.
482 * Mark the corresponding file positions, then
483 * look for further matches and mark them.
484 * {{ This technique, of calling match_pattern on subsequent
485 * substrings of the line, may mark more than is correct
486 * if the pattern starts with "^". This bug is fixed
487 * for those regex functions that accept a notbol parameter
488 * (currently POSIX, PCRE and V8-with-regexec2). }}
492 if (sp
== NULL
|| ep
== NULL
)
495 create_hilites(linepos
, (intptr_t)sp
- (intptr_t)line
,
496 (intptr_t)ep
- (intptr_t)line
, chpos
);
498 * If we matched more than zero characters,
499 * move to the first char after the string we matched.
500 * If we matched zero, just move to the next char.
504 else if (searchp
!= line_end
)
506 else /* end of line */
508 } while (match_pattern(info_compiled(&search_info
), search_info
.text
,
509 searchp
, (intptr_t)line_end
- (intptr_t)searchp
, &sp
, &ep
, 1,
510 search_info
.search_type
));
514 * Change the caseless-ness of searches.
515 * Updates the internal search state to reflect a change in the -i flag.
520 if (!is_ucase_pattern
)
522 * Pattern did not have uppercase.
523 * Just set the search caselessness to the global caselessness.
525 is_caseless
= caseless
;
528 * Pattern did have uppercase.
529 * Discard the pattern; we can't change search caselessness now.
531 clear_pattern(&search_info
);
535 * Find matching text which is currently on screen and highlight it.
540 struct scrpos scrpos
;
543 if (scrpos
.pos
== -1)
545 prep_hilite(scrpos
.pos
, position(BOTTOM_PLUS_ONE
), -1);
550 * Change highlighting parameters.
556 * Erase any highlights currently on screen.
561 if (hilite_search
== OPT_ONPLUS
)
563 * Display highlights.
569 * Figure out where to start a search.
572 search_pos(int search_type
)
577 if (empty_screen()) {
579 * Start at the beginning (or end) of the file.
580 * The empty_screen() case is mainly for
581 * command line initiated searches;
582 * for example, "+/xyz" on the command line.
583 * Also for multi-file (SRCH_PAST_EOF) searches.
585 if (search_type
& SRCH_FORW
) {
590 (void) ch_end_seek();
598 if (how_search
== OPT_ON
) {
600 * Search does not include current screen.
602 if (search_type
& SRCH_FORW
)
603 linenum
= BOTTOM_PLUS_ONE
;
606 } else if (how_search
== OPT_ONPLUS
&&
607 !(search_type
& SRCH_AFTER_TARGET
)) {
609 * Search includes all of displayed screen.
611 if (search_type
& SRCH_FORW
)
614 linenum
= BOTTOM_PLUS_ONE
;
617 * Search includes the part of current screen beyond
619 * It starts at the jump target (if searching
620 * backwards), or at the jump target plus one
623 linenum
= jump_sline
;
624 if (search_type
& SRCH_FORW
)
627 linenum
= adjsline(linenum
);
628 pos
= position(linenum
);
630 pos
= forw_raw_line(pos
, NULL
, NULL
);
634 * If the line is empty, look around for a plausible starting place.
636 if (search_type
& SRCH_FORW
) {
638 if (++linenum
>= sc_height
)
640 pos
= position(linenum
);
646 pos
= position(linenum
);
653 * Search a subset of the file, specified by start/end position.
656 search_range(off_t pos
, off_t endpos
, int search_type
, int matches
,
657 int maxlines
, off_t
*plinepos
, off_t
*pendpos
)
668 off_t linepos
, oldpos
;
670 linenum
= find_linenum(pos
);
674 * Get lines until we find a matching one or until
675 * we hit end-of-file (or beginning-of-file if we're
676 * going backwards), or until we hit the end position.
680 * A signal aborts the search.
685 if ((endpos
!= -1 && pos
>= endpos
) ||
688 * Reached end position without a match.
697 if (search_type
& SRCH_FORW
) {
699 * Read the next line, and save the
700 * starting position of that line in linepos.
703 pos
= forw_raw_line(pos
, &line
, &line_len
);
708 * Read the previous line and save the
709 * starting position of that line in linepos.
711 pos
= back_raw_line(pos
, &line
, &line_len
);
719 * Reached EOF/BOF without a match.
727 * If we're using line numbers, we might as well
728 * remember the information we have now (the position
729 * and line number of the current line).
730 * Don't do it for every line because it slows down
731 * the search. Remember the line number only if
732 * we're "far" from the last place we remembered it.
734 if (linenums
&& abs((int)(pos
- oldpos
)) > 2048)
735 add_lnum(linenum
, pos
);
738 if (is_filtered(linepos
))
742 * If it's a caseless search, convert the line to lowercase.
743 * If we're doing backspace processing, delete backspaces.
745 cvt_ops
= get_cvt_ops();
746 cvt_len
= cvt_length(line_len
);
747 cline
= ecalloc(1, cvt_len
);
748 chpos
= cvt_alloc_chpos(cvt_len
);
749 cvt_text(cline
, line
, chpos
, &line_len
, cvt_ops
);
752 * Check to see if the line matches the filter pattern.
753 * If so, add an entry to the filter list.
755 if ((search_type
& SRCH_FIND_ALL
) &&
756 prev_pattern(&filter_info
)) {
758 match_pattern(info_compiled(&filter_info
),
759 filter_info
.text
, cline
, line_len
, &sp
, &ep
, 0,
760 filter_info
.search_type
);
763 ecalloc(1, sizeof (struct hilite
));
764 hl
->hl_startpos
= linepos
;
766 add_hilite(&filter_anchor
, hl
);
771 * Test the next line to see if we have a match.
772 * We are successful if we either want a match and got one,
773 * or if we want a non-match and got one.
775 if (prev_pattern(&search_info
)) {
776 line_match
= match_pattern(info_compiled(&search_info
),
777 search_info
.text
, cline
, line_len
, &sp
, &ep
, 0,
783 if (search_type
& SRCH_FIND_ALL
) {
785 * We are supposed to find all matches
787 * Just add the matches in this line
788 * to the hilite list and keep
791 hilite_line(linepos
, cline
, line_len
,
793 } else if (--matches
<= 0) {
795 * Found the one match we're looking
798 if (hilite_search
== OPT_ON
) {
800 * Clear the hilite list and
802 * the matches in this one line.
805 hilite_line(linepos
, cline
,
806 line_len
, chpos
, sp
, ep
);
810 if (plinepos
!= NULL
)
822 * search for a pattern in history. If found, compile that pattern.
825 hist_pattern(int search_type
)
829 set_mlist(ml_search
, 0);
830 pattern
= cmd_lastpattern();
834 if (set_pattern(&search_info
, pattern
, search_type
) < 0)
837 if (hilite_search
== OPT_ONPLUS
&& !hide_hilite
)
844 * Search for the n-th occurrence of a specified pattern,
845 * either forward or backward.
846 * Return the number of matches not yet found in this file
847 * (that is, n minus the number of matches found).
848 * Return -1 if the search should be aborted.
849 * Caller may continue the search in another file
850 * if less than n matches are found in this file.
853 search(int search_type
, char *pattern
, int n
)
857 if (pattern
== NULL
|| *pattern
== '\0') {
859 * A null pattern means use the previously compiled pattern.
861 search_type
|= SRCH_AFTER_TARGET
;
862 if (!prev_pattern(&search_info
) && !hist_pattern(search_type
)) {
863 error("No previous regular expression", NULL
);
866 if ((search_type
& SRCH_NO_REGEX
) !=
867 (search_info
.search_type
& SRCH_NO_REGEX
)) {
868 error("Please re-enter search pattern", NULL
);
871 if (hilite_search
== OPT_ON
) {
873 * Erase the highlights currently on screen.
874 * If the search fails, we'll redisplay them later.
878 if (hilite_search
== OPT_ONPLUS
&& hide_hilite
) {
880 * Highlight any matches currently on screen,
881 * before we actually start the search.
889 * Compile the pattern.
891 if (set_pattern(&search_info
, pattern
, search_type
) < 0)
895 * Erase the highlights currently on screen.
896 * Also permanently delete them from the hilite list.
902 if (hilite_search
== OPT_ONPLUS
) {
904 * Highlight any matches currently on screen,
905 * before we actually start the search.
912 * Figure out where to start the search.
914 pos
= search_pos(search_type
);
917 * Can't find anyplace to start searching from.
919 if (search_type
& SRCH_PAST_EOF
)
921 /* repaint(); -- why was this here? */
922 error("Nothing to search", NULL
);
926 n
= search_range(pos
, -1, search_type
, n
, -1, &pos
, NULL
);
929 * Search was unsuccessful.
931 if (hilite_search
== OPT_ON
&& n
> 0)
933 * Redisplay old hilites.
939 if (!(search_type
& SRCH_NO_MOVE
)) {
941 * Go to the matching line.
943 jump_loc(pos
, jump_sline
);
946 if (hilite_search
== OPT_ON
)
948 * Display new hilites in the matching line.
956 * Prepare hilites in a given range of the file.
958 * The pair (prep_startpos,prep_endpos) delimits a contiguous region
959 * of the file that has been "prepared"; that is, scanned for matches for
960 * the current search pattern, and hilites have been created for such matches.
961 * If prep_startpos == -1, the prep region is empty.
962 * If prep_endpos == -1, the prep region extends to EOF.
963 * prep_hilite asks that the range (spos,epos) be covered by the prep region.
966 prep_hilite(off_t spos
, off_t epos
, int maxlines
)
968 off_t nprep_startpos
= prep_startpos
;
969 off_t nprep_endpos
= prep_endpos
;
976 * Search beyond where we're asked to search, so the prep region covers
977 * more than we need. Do one big search instead of a bunch of small ones.
979 #define SEARCH_MORE (3*size_linebuf)
981 if (!prev_pattern(&search_info
) && !is_filtering())
985 * If we're limited to a max number of lines, figure out the
986 * file position we should stop at.
992 for (i
= 0; i
< maxlines
; i
++)
993 max_epos
= forw_raw_line(max_epos
, NULL
, NULL
);
998 * The range that we need to search (spos,epos); and the range that
999 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
1002 if (prep_startpos
== -1 ||
1003 (epos
!= -1 && epos
< prep_startpos
) ||
1004 spos
> prep_endpos
) {
1006 * New range is not contiguous with old prep region.
1007 * Discard the old prep region and start a new one.
1012 epos
+= SEARCH_MORE
;
1013 nprep_startpos
= spos
;
1016 * New range partially or completely overlaps old prep region.
1019 if (epos
> prep_endpos
) {
1021 * New range ends after old prep region.
1022 * Extend prep region to end at end of new
1025 epos
+= SEARCH_MORE
;
1029 * New range ends within old prep region.
1030 * Truncate search to end at start of old prep
1033 epos
= prep_startpos
;
1037 if (spos
< prep_startpos
) {
1039 * New range starts before old prep region.
1040 * Extend old prep region backwards to start at
1041 * start of new range.
1043 if (spos
< SEARCH_MORE
)
1046 spos
-= SEARCH_MORE
;
1047 nprep_startpos
= spos
;
1048 } else { /* (spos >= prep_startpos) */
1050 * New range starts within or after old prep region.
1051 * Trim search to start at end of old prep region.
1057 if (epos
!= -1 && max_epos
!= -1 &&
1060 * Don't go past the max position we're allowed.
1064 if (epos
== -1 || epos
> spos
) {
1065 int search_type
= SRCH_FORW
| SRCH_FIND_ALL
;
1066 search_type
|= (search_info
.search_type
& SRCH_NO_REGEX
);
1067 result
= search_range(spos
, epos
, search_type
, 0,
1068 maxlines
, NULL
, &new_epos
);
1071 if (prep_endpos
== -1 || new_epos
> prep_endpos
)
1072 nprep_endpos
= new_epos
;
1074 prep_startpos
= nprep_startpos
;
1075 prep_endpos
= nprep_endpos
;
1079 * Set the pattern to be used for line filtering.
1082 set_filter_pattern(char *pattern
, int search_type
)
1085 if (pattern
== NULL
|| *pattern
== '\0')
1086 clear_pattern(&filter_info
);
1088 (void) set_pattern(&filter_info
, pattern
, search_type
);
1093 * Is there a line filter in effect?
1098 if (ch_getflags() & CH_HELPFILE
)
1100 return (prev_pattern(&filter_info
));