1 /*
2 * Copyright (C) 1984-2002 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
19 #define MINPOS(a,b) (((a) < (b)) ? (a) : (b))
20 #define MAXPOS(a,b) (((a) > (b)) ? (a) : (b))
21
22 #if HAVE_POSIX_REGCOMP
23 #include <regex.h>
24 #ifdef REG_EXTENDED
25 #define REGCOMP_FLAG REG_EXTENDED
26 #else
27 #define REGCOMP_FLAG 0
28 #endif
29 #endif
30 #if HAVE_PCRE
31 #include <pcre.h>
32 #endif
33 #if HAVE_RE_COMP
34 char *re_comp();
35 int re_exec();
36 #endif
37 #if HAVE_REGCMP
38 char *regcmp();
39 char *regex();
40 extern char *__loc1;
41 #endif
42 #if HAVE_V8_REGCOMP
43 #include "regexp.h"
44 #endif
45
46 static int match();
47
48 extern int sigs;
49 extern int how_search;
50 extern int caseless;
51 extern int linenums;
52 extern int sc_height;
53 extern int jump_sline;
54 extern int bs_mode;
55 extern int ctldisp;
56 extern int status_col;
57 extern POSITION start_attnpos;
58 extern POSITION end_attnpos;
59 #if HILITE_SEARCH
60 extern int hilite_search;
61 extern int screen_trashed;
62 extern int size_linebuf;
63 extern int squished;
64 extern int can_goto_line;
65 static int hide_hilite;
66 static POSITION prep_startpos;
67 static POSITION prep_endpos;
68
69 struct hilite
70 {
71 struct hilite *hl_next;
72 POSITION hl_startpos;
73 POSITION hl_endpos;
74 };
75 static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
76 #define hl_first hl_next
77 #endif
78
79 /*
80 * These are the static variables that represent the "remembered"
81 * search pattern.
82 */
83 #if HAVE_POSIX_REGCOMP
84 static regex_t *regpattern = NULL;
85 #endif
86 #if HAVE_PCRE
87 pcre *regpattern = NULL;
88 #endif
89 #if HAVE_RE_COMP
90 int re_pattern = 0;
91 #endif
92 #if HAVE_REGCMP
93 static char *cpattern = NULL;
94 #endif
95 #if HAVE_V8_REGCOMP
96 static struct regexp *regpattern = NULL;
97 #endif
98
99 static int is_caseless;
100 static int is_ucase_pattern;
101 static int last_search_type;
102 static char *last_pattern = NULL;
103
104 /*
105 * Convert text. Perform one or more of these transformations:
106 */
107 #define CVT_TO_LC 01 /* Convert upper-case to lower-case */
108 #define CVT_BS 02 /* Do backspace processing */
109 #define CVT_CRLF 04 /* Remove CR after LF */
110 #define CVT_ANSI 010 /* Remove ANSI escape sequences */
111
112 static void
cvt_text(odst,osrc,ops)113 cvt_text(odst, osrc, ops)
114 char *odst;
115 char *osrc;
116 int ops;
117 {
118 register char *dst;
119 register char *src;
120
121 for (src = osrc, dst = odst; *src != '\0'; src++)
122 {
123 if ((ops & CVT_TO_LC) && isupper((unsigned char) *src))
124 /* Convert uppercase to lowercase. */
125 *dst++ = tolower((unsigned char) *src);
126 else if ((ops & CVT_BS) && *src == '\b' && dst > odst)
127 /* Delete BS and preceding char. */
128 dst--;
129 else if ((ops & CVT_ANSI) && *src == ESC)
130 {
131 /* Skip to end of ANSI escape sequence. */
132 while (src[1] != '\0')
133 if (is_ansi_end(*++src))
134 break;
135 } else
136 /* Just copy. */
137 *dst++ = *src;
138 }
139 if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
140 dst--;
141 *dst = '\0';
142 }
143
144 /*
145 * Determine which conversions to perform.
146 */
147 static int
get_cvt_ops()148 get_cvt_ops()
149 {
150 int ops = 0;
151 if (is_caseless || bs_mode == BS_SPECIAL)
152 {
153 if (is_caseless)
154 ops |= CVT_TO_LC;
155 if (bs_mode == BS_SPECIAL)
156 ops |= CVT_BS;
157 if (bs_mode != BS_CONTROL)
158 ops |= CVT_CRLF;
159 } else if (bs_mode != BS_CONTROL)
160 {
161 ops |= CVT_CRLF;
162 }
163 if (ctldisp == OPT_ONPLUS)
164 ops |= CVT_ANSI;
165 return (ops);
166 }
167
168 /*
169 * Are there any uppercase letters in this string?
170 */
171 static int
is_ucase(s)172 is_ucase(s)
173 char *s;
174 {
175 register char *p;
176
177 for (p = s; *p != '\0'; p++)
178 if (isupper((unsigned char) *p))
179 return (1);
180 return (0);
181 }
182
183 /*
184 * Is there a previous (remembered) search pattern?
185 */
186 static int
prev_pattern()187 prev_pattern()
188 {
189 if (last_search_type & SRCH_NO_REGEX)
190 return (last_pattern != NULL);
191 #if HAVE_POSIX_REGCOMP
192 return (regpattern != NULL);
193 #endif
194 #if HAVE_PCRE
195 return (regpattern != NULL);
196 #endif
197 #if HAVE_RE_COMP
198 return (re_pattern != 0);
199 #endif
200 #if HAVE_REGCMP
201 return (cpattern != NULL);
202 #endif
203 #if HAVE_V8_REGCOMP
204 return (regpattern != NULL);
205 #endif
206 #if NO_REGEX
207 return (last_pattern != NULL);
208 #endif
209 }
210
211 #if HILITE_SEARCH
212 /*
213 * Repaint the hilites currently displayed on the screen.
214 * Repaint each line which contains highlighted text.
215 * If on==0, force all hilites off.
216 */
217 public void
repaint_hilite(on)218 repaint_hilite(on)
219 int on;
220 {
221 int slinenum;
222 POSITION pos;
223 POSITION epos;
224 int save_hide_hilite;
225
226 if (squished)
227 repaint();
228
229 save_hide_hilite = hide_hilite;
230 if (!on)
231 {
232 if (hide_hilite)
233 return;
234 hide_hilite = 1;
235 }
236
237 if (!can_goto_line)
238 {
239 repaint();
240 hide_hilite = save_hide_hilite;
241 return;
242 }
243
244 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++)
245 {
246 pos = position(slinenum);
247 if (pos == NULL_POSITION)
248 continue;
249 epos = position(slinenum+1);
250 /*
251 * If any character in the line is highlighted,
252 * repaint the line.
253 */
254 if (is_hilited(pos, epos, 1))
255 {
256 (void) forw_line(pos);
257 goto_line(slinenum);
258 put_line();
259 }
260 }
261 hide_hilite = save_hide_hilite;
262 }
263
264 /*
265 * Clear the attn hilite.
266 */
267 public void
clear_attn()268 clear_attn()
269 {
270 int slinenum;
271 POSITION old_start_attnpos;
272 POSITION old_end_attnpos;
273 POSITION pos;
274 POSITION epos;
275
276 if (start_attnpos == NULL_POSITION)
277 return;
278 old_start_attnpos = start_attnpos;
279 old_end_attnpos = end_attnpos;
280 start_attnpos = end_attnpos = NULL_POSITION;
281
282 if (!can_goto_line)
283 {
284 repaint();
285 return;
286 }
287 if (squished)
288 repaint();
289
290 for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++)
291 {
292 pos = position(slinenum);
293 if (pos == NULL_POSITION)
294 continue;
295 epos = position(slinenum+1);
296 if (pos < old_end_attnpos &&
297 (epos == NULL_POSITION || epos > old_start_attnpos))
298 {
299 (void) forw_line(pos);
300 goto_line(slinenum);
301 put_line();
302 }
303 }
304 }
305 #endif
306
307 /*
308 * Hide search string highlighting.
309 */
310 public void
undo_search()311 undo_search()
312 {
313 if (!prev_pattern())
314 {
315 error("No previous regular expression", NULL_PARG);
316 return;
317 }
318 #if HILITE_SEARCH
319 hide_hilite = !hide_hilite;
320 repaint_hilite(1);
321 #endif
322 }
323
324 /*
325 * Compile a search pattern, for future use by match_pattern.
326 */
327 static int
compile_pattern(pattern,search_type)328 compile_pattern(pattern, search_type)
329 char *pattern;
330 int search_type;
331 {
332 if ((search_type & SRCH_NO_REGEX) == 0)
333 {
334 #if HAVE_POSIX_REGCOMP
335 regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t));
336 if (regcomp(s, pattern, REGCOMP_FLAG))
337 {
338 free(s);
339 error("Invalid pattern", NULL_PARG);
340 return (-1);
341 }
342 if (regpattern != NULL)
343 regfree(regpattern);
344 regpattern = s;
345 #endif
346 #if HAVE_PCRE
347 pcre *comp;
348 const char *errstring;
349 int erroffset;
350 PARG parg;
351 comp = pcre_compile(pattern, 0,
352 &errstring, &erroffset, NULL);
353 if (comp == NULL)
354 {
355 parg.p_string = (char *) errstring;
356 error("%s", &parg);
357 return (-1);
358 }
359 regpattern = comp;
360 #endif
361 #if HAVE_RE_COMP
362 PARG parg;
363 if ((parg.p_string = re_comp(pattern)) != NULL)
364 {
365 error("%s", &parg);
366 return (-1);
367 }
368 re_pattern = 1;
369 #endif
370 #if HAVE_REGCMP
371 char *s;
372 if ((s = regcmp(pattern, 0)) == NULL)
373 {
374 error("Invalid pattern", NULL_PARG);
375 return (-1);
376 }
377 if (cpattern != NULL)
378 free(cpattern);
379 cpattern = s;
380 #endif
381 #if HAVE_V8_REGCOMP
382 struct regexp *s;
383 if ((s = regcomp(pattern)) == NULL)
384 {
385 /*
386 * regcomp has already printed an error message
387 * via regerror().
388 */
389 return (-1);
390 }
391 if (regpattern != NULL)
392 free(regpattern);
393 regpattern = s;
394 #endif
395 }
396
397 if (last_pattern != NULL)
398 free(last_pattern);
399 last_pattern = save(pattern);
400
401 last_search_type = search_type;
402 return (0);
403 }
404
405 /*
406 * Forget that we have a compiled pattern.
407 */
408 static void
uncompile_pattern()409 uncompile_pattern()
410 {
411 #if HAVE_POSIX_REGCOMP
412 if (regpattern != NULL)
413 regfree(regpattern);
414 regpattern = NULL;
415 #endif
416 #if HAVE_PCRE
417 if (regpattern != NULL)
418 pcre_free(regpattern);
419 regpattern = NULL;
420 #endif
421 #if HAVE_RE_COMP
422 re_pattern = 0;
423 #endif
424 #if HAVE_REGCMP
425 if (cpattern != NULL)
426 free(cpattern);
427 cpattern = NULL;
428 #endif
429 #if HAVE_V8_REGCOMP
430 if (regpattern != NULL)
431 free(regpattern);
432 regpattern = NULL;
433 #endif
434 last_pattern = NULL;
435 }
436
437 /*
438 * Perform a pattern match with the previously compiled pattern.
439 * Set sp and ep to the start and end of the matched string.
440 */
441 static int
match_pattern(line,sp,ep,notbol)442 match_pattern(line, sp, ep, notbol)
443 char *line;
444 char **sp;
445 char **ep;
446 int notbol;
447 {
448 int matched;
449
450 if (last_search_type & SRCH_NO_REGEX)
451 return (match(last_pattern, line, sp, ep));
452
453 #if HAVE_POSIX_REGCOMP
454 {
455 regmatch_t rm;
456 int flags = (notbol) ? REG_NOTBOL : 0;
457 matched = !regexec(regpattern, line, 1, &rm, flags);
458 if (!matched)
459 return (0);
460 #ifndef __WATCOMC__
461 *sp = line + rm.rm_so;
462 *ep = line + rm.rm_eo;
463 #else
464 *sp = rm.rm_sp;
465 *ep = rm.rm_ep;
466 #endif
467 }
468 #endif
469 #if HAVE_PCRE
470 {
471 int flags = (notbol) ? PCRE_NOTBOL : 0;
472 int ovector[3];
473 matched = pcre_exec(regpattern, NULL, line, strlen(line),
474 0, flags, ovector, 3) >= 0;
475 if (!matched)
476 return (0);
477 *sp = line + ovector[0];
478 *ep = line + ovector[1];
479 }
480 #endif
481 #if HAVE_RE_COMP
482 matched = (re_exec(line) == 1);
483 /*
484 * re_exec doesn't seem to provide a way to get the matched string.
485 */
486 *sp = *ep = NULL;
487 #endif
488 #if HAVE_REGCMP
489 *ep = regex(cpattern, line);
490 matched = (*ep != NULL);
491 if (!matched)
492 return (0);
493 *sp = __loc1;
494 #endif
495 #if HAVE_V8_REGCOMP
496 #if HAVE_REGEXEC2
497 matched = regexec2(regpattern, line, notbol);
498 #else
499 matched = regexec(regpattern, line);
500 #endif
501 if (!matched)
502 return (0);
503 *sp = regpattern->startp[0];
504 *ep = regpattern->endp[0];
505 #endif
506 #if NO_REGEX
507 matched = match(last_pattern, line, sp, ep);
508 #endif
509 return (matched);
510 }
511
512 #if HILITE_SEARCH
513 /*
514 * Clear the hilite list.
515 */
516 public void
clr_hilite()517 clr_hilite()
518 {
519 struct hilite *hl;
520 struct hilite *nexthl;
521
522 for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl)
523 {
524 nexthl = hl->hl_next;
525 free((void*)hl);
526 }
527 hilite_anchor.hl_first = NULL;
528 prep_startpos = prep_endpos = NULL_POSITION;
529 }
530
531 /*
532 * Should any characters in a specified range be highlighted?
533 * If nohide is nonzero, don't consider hide_hilite.
534 */
535 public int
is_hilited(pos,epos,nohide)536 is_hilited(pos, epos, nohide)
537 POSITION pos;
538 POSITION epos;
539 int nohide;
540 {
541 struct hilite *hl;
542
543 if (!status_col &&
544 start_attnpos != NULL_POSITION &&
545 pos < end_attnpos &&
546 (epos == NULL_POSITION || epos > start_attnpos))
547 /*
548 * The attn line overlaps this range.
549 */
550 return (1);
551
552 if (hilite_search == 0)
553 /*
554 * Not doing highlighting.
555 */
556 return (0);
557
558 if (!nohide && hide_hilite)
559 /*
560 * Highlighting is hidden.
561 */
562 return (0);
563
564 /*
565 * Look at each highlight and see if any part of it falls in the range.
566 */
567 for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next)
568 {
569 if (hl->hl_endpos > pos &&
570 (epos == NULL_POSITION || epos > hl->hl_startpos))
571 return (1);
572 }
573 return (0);
574 }
575
576 /*
577 * Add a new hilite to a hilite list.
578 */
579 static void
add_hilite(anchor,hl)580 add_hilite(anchor, hl)
581 struct hilite *anchor;
582 struct hilite *hl;
583 {
584 struct hilite *ihl;
585
586 /*
587 * Hilites are sorted in the list; find where new one belongs.
588 * Insert new one after ihl.
589 */
590 for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next)
591 {
592 if (ihl->hl_next->hl_startpos > hl->hl_startpos)
593 break;
594 }
595
596 /*
597 * Truncate hilite so it doesn't overlap any existing ones
598 * above and below it.
599 */
600 if (ihl != anchor)
601 hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
602 if (ihl->hl_next != NULL)
603 hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
604 if (hl->hl_startpos >= hl->hl_endpos)
605 {
606 /*
607 * Hilite was truncated out of existence.
608 */
609 free(hl);
610 return;
611 }
612 hl->hl_next = ihl->hl_next;
613 ihl->hl_next = hl;
614 }
615
616 /*
617 * Adjust hl_startpos & hl_endpos to account for backspace processing.
618 */
619 static void
adj_hilite(anchor,linepos,cvt_ops)620 adj_hilite(anchor, linepos, cvt_ops)
621 struct hilite *anchor;
622 POSITION linepos;
623 int cvt_ops;
624 {
625 char *line;
626 struct hilite *hl;
627 int checkstart;
628 POSITION opos;
629 POSITION npos;
630
631 /*
632 * The line was already scanned and hilites were added (in hilite_line).
633 * But it was assumed that each char position in the line
634 * correponds to one char position in the file.
635 * This may not be true if there are backspaces in the line.
636 * Get the raw line again. Look at each character.
637 */
638 (void) forw_raw_line(linepos, &line);
639 opos = npos = linepos;
640 hl = anchor->hl_first;
641 checkstart = TRUE;
642 while (hl != NULL)
643 {
644 /*
645 * See if we need to adjust the current hl_startpos or
646 * hl_endpos. After adjusting startpos[i], move to endpos[i].
647 * After adjusting endpos[i], move to startpos[i+1].
648 * The hilite list must be sorted thus:
649 * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
650 */
651 if (checkstart && hl->hl_startpos == opos)
652 {
653 hl->hl_startpos = npos;
654 checkstart = FALSE;
655 continue; /* {{ not really necessary }} */
656 } else if (!checkstart && hl->hl_endpos == opos)
657 {
658 hl->hl_endpos = npos;
659 checkstart = TRUE;
660 hl = hl->hl_next;
661 continue; /* {{ necessary }} */
662 }
663 if (*line == '\0')
664 break;
665 if (cvt_ops & CVT_ANSI)
666 {
667 while (line[0] == ESC)
668 {
669 /*
670 * Found an ESC. The file position moves
671 * forward past the entire ANSI escape sequence.
672 */
673 line++;
674 npos++;
675 while (*line != '\0')
676 {
677 npos++;
678 if (is_ansi_end(*line++))
679 break;
680 }
681 }
682 }
683 opos++;
684 npos++;
685 line++;
686 if (cvt_ops & CVT_BS)
687 {
688 while (line[0] == '\b' && line[1] != '\0')
689 {
690 /*
691 * Found a backspace. The file position moves
692 * forward by 2 relative to the processed line
693 * which was searched in hilite_line.
694 */
695 npos += 2;
696 line += 2;
697 }
698 }
699 }
700 }
701
702 /*
703 * Make a hilite for each string in a physical line which matches
704 * the current pattern.
705 * sp,ep delimit the first match already found.
706 */
707 static void
hilite_line(linepos,line,sp,ep,cvt_ops)708 hilite_line(linepos, line, sp, ep, cvt_ops)
709 POSITION linepos;
710 char *line;
711 char *sp;
712 char *ep;
713 int cvt_ops;
714 {
715 char *searchp;
716 struct hilite *hl;
717 struct hilite hilites;
718
719 if (sp == NULL || ep == NULL)
720 return;
721 /*
722 * sp and ep delimit the first match in the line.
723 * Mark the corresponding file positions, then
724 * look for further matches and mark them.
725 * {{ This technique, of calling match_pattern on subsequent
726 * substrings of the line, may mark more than is correct
727 * if the pattern starts with "^". This bug is fixed
728 * for those regex functions that accept a notbol parameter
729 * (currently POSIX and V8-with-regexec2). }}
730 */
731 searchp = line;
732 /*
733 * Put the hilites into a temporary list until they're adjusted.
734 */
735 hilites.hl_first = NULL;
736 do {
737 if (ep > sp)
738 {
739 /*
740 * Assume that each char position in the "line"
741 * buffer corresponds to one char position in the file.
742 * This is not quite true; we need to adjust later.
743 */
744 hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
745 hl->hl_startpos = linepos + (sp-line);
746 hl->hl_endpos = linepos + (ep-line);
747 add_hilite(&hilites, hl);
748 }
749 /*
750 * If we matched more than zero characters,
751 * move to the first char after the string we matched.
752 * If we matched zero, just move to the next char.
753 */
754 if (ep > searchp)
755 searchp = ep;
756 else if (*searchp != '\0')
757 searchp++;
758 else /* end of line */
759 break;
760 } while (match_pattern(searchp, &sp, &ep, 1));
761
762 /*
763 * If there were backspaces in the original line, they
764 * were removed, and hl_startpos/hl_endpos are not correct.
765 * {{ This is very ugly. }}
766 */
767 adj_hilite(&hilites, linepos, cvt_ops);
768
769 /*
770 * Now put the hilites into the real list.
771 */
772 while ((hl = hilites.hl_next) != NULL)
773 {
774 hilites.hl_next = hl->hl_next;
775 add_hilite(&hilite_anchor, hl);
776 }
777 }
778 #endif
779
780 /*
781 * Change the caseless-ness of searches.
782 * Updates the internal search state to reflect a change in the -i flag.
783 */
784 public void
chg_caseless()785 chg_caseless()
786 {
787 if (!is_ucase_pattern)
788 /*
789 * Pattern did not have uppercase.
790 * Just set the search caselessness to the global caselessness.
791 */
792 is_caseless = caseless;
793 else
794 /*
795 * Pattern did have uppercase.
796 * Discard the pattern; we can't change search caselessness now.
797 */
798 uncompile_pattern();
799 }
800
801 #if HILITE_SEARCH
802 /*
803 * Find matching text which is currently on screen and highlight it.
804 */
805 static void
hilite_screen()806 hilite_screen()
807 {
808 struct scrpos scrpos;
809
810 get_scrpos(&scrpos);
811 if (scrpos.pos == NULL_POSITION)
812 return;
813 prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
814 repaint_hilite(1);
815 }
816
817 /*
818 * Change highlighting parameters.
819 */
820 public void
chg_hilite()821 chg_hilite()
822 {
823 /*
824 * Erase any highlights currently on screen.
825 */
826 clr_hilite();
827 hide_hilite = 0;
828
829 if (hilite_search == OPT_ONPLUS)
830 /*
831 * Display highlights.
832 */
833 hilite_screen();
834 }
835 #endif
836
837 /*
838 * Figure out where to start a search.
839 */
840 static POSITION
search_pos(search_type)841 search_pos(search_type)
842 int search_type;
843 {
844 POSITION pos;
845 int linenum;
846
847 if (empty_screen())
848 {
849 /*
850 * Start at the beginning (or end) of the file.
851 * The empty_screen() case is mainly for
852 * command line initiated searches;
853 * for example, "+/xyz" on the command line.
854 * Also for multi-file (SRCH_PAST_EOF) searches.
855 */
856 if (search_type & SRCH_FORW)
857 {
858 return (ch_zero());
859 } else
860 {
861 pos = ch_length();
862 if (pos == NULL_POSITION)
863 {
864 (void) ch_end_seek();
865 pos = ch_length();
866 }
867 return (pos);
868 }
869 }
870 if (how_search)
871 {
872 /*
873 * Search does not include current screen.
874 */
875 if (search_type & SRCH_FORW)
876 linenum = BOTTOM_PLUS_ONE;
877 else
878 linenum = TOP;
879 pos = position(linenum);
880 } else
881 {
882 /*
883 * Search includes current screen.
884 * It starts at the jump target (if searching backwards),
885 * or at the jump target plus one (if forwards).
886 */
887 linenum = adjsline(jump_sline);
888 pos = position(linenum);
889 if (search_type & SRCH_FORW)
890 {
891 pos = forw_raw_line(pos, (char **)NULL);
892 while (pos == NULL_POSITION)
893 {
894 if (++linenum >= sc_height)
895 break;
896 pos = position(linenum);
897 }
898 } else
899 {
900 while (pos == NULL_POSITION)
901 {
902 if (--linenum < 0)
903 break;
904 pos = position(linenum);
905 }
906 }
907 }
908 return (pos);
909 }
910
911 /*
912 * Search a subset of the file, specified by start/end position.
913 */
914 static int
search_range(pos,endpos,search_type,matches,maxlines,plinepos,pendpos)915 search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
916 POSITION pos;
917 POSITION endpos;
918 int search_type;
919 int matches;
920 int maxlines;
921 POSITION *plinepos;
922 POSITION *pendpos;
923 {
924 char *line;
925 LINENUM linenum;
926 char *sp, *ep;
927 int line_match;
928 int cvt_ops;
929 POSITION linepos, oldpos;
930
931 linenum = find_linenum(pos);
932 oldpos = pos;
933 for (;;)
934 {
935 /*
936 * Get lines until we find a matching one or until
937 * we hit end-of-file (or beginning-of-file if we're
938 * going backwards), or until we hit the end position.
939 */
940 if (ABORT_SIGS())
941 {
942 /*
943 * A signal aborts the search.
944 */
945 return (-1);
946 }
947
948 if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
949 {
950 /*
951 * Reached end position without a match.
952 */
953 if (pendpos != NULL)
954 *pendpos = pos;
955 return (matches);
956 }
957 if (maxlines > 0)
958 maxlines--;
959
960 if (search_type & SRCH_FORW)
961 {
962 /*
963 * Read the next line, and save the
964 * starting position of that line in linepos.
965 */
966 linepos = pos;
967 pos = forw_raw_line(pos, &line);
968 if (linenum != 0)
969 linenum++;
970 } else
971 {
972 /*
973 * Read the previous line and save the
974 * starting position of that line in linepos.
975 */
976 pos = back_raw_line(pos, &line);
977 linepos = pos;
978 if (linenum != 0)
979 linenum--;
980 }
981
982 if (pos == NULL_POSITION)
983 {
984 /*
985 * Reached EOF/BOF without a match.
986 */
987 if (pendpos != NULL)
988 *pendpos = oldpos;
989 return (matches);
990 }
991
992 /*
993 * If we're using line numbers, we might as well
994 * remember the information we have now (the position
995 * and line number of the current line).
996 * Don't do it for every line because it slows down
997 * the search. Remember the line number only if
998 * we're "far" from the last place we remembered it.
999 */
1000 if (linenums && abs((int)(pos - oldpos)) > 1024)
1001 add_lnum(linenum, pos);
1002 oldpos = pos;
1003
1004 /*
1005 * If it's a caseless search, convert the line to lowercase.
1006 * If we're doing backspace processing, delete backspaces.
1007 */
1008 cvt_ops = get_cvt_ops();
1009 cvt_text(line, line, cvt_ops);
1010
1011 /*
1012 * Test the next line to see if we have a match.
1013 * We are successful if we either want a match and got one,
1014 * or if we want a non-match and got one.
1015 */
1016 line_match = match_pattern(line, &sp, &ep, 0);
1017 line_match = (!(search_type & SRCH_NO_MATCH) && line_match) ||
1018 ((search_type & SRCH_NO_MATCH) && !line_match);
1019 if (!line_match)
1020 continue;
1021 /*
1022 * Got a match.
1023 */
1024 if (search_type & SRCH_FIND_ALL)
1025 {
1026 #if HILITE_SEARCH
1027 /*
1028 * We are supposed to find all matches in the range.
1029 * Just add the matches in this line to the
1030 * hilite list and keep searching.
1031 */
1032 if (line_match)
1033 hilite_line(linepos, line, sp, ep, cvt_ops);
1034 #endif
1035 } else if (--matches <= 0)
1036 {
1037 /*
1038 * Found the one match we're looking for.
1039 * Return it.
1040 */
1041 #if HILITE_SEARCH
1042 if (hilite_search == 1)
1043 {
1044 /*
1045 * Clear the hilite list and add only
1046 * the matches in this one line.
1047 */
1048 clr_hilite();
1049 if (line_match)
1050 hilite_line(linepos, line, sp, ep, cvt_ops);
1051 }
1052 #endif
1053 if (plinepos != NULL)
1054 *plinepos = linepos;
1055 return (0);
1056 }
1057 }
1058 }
1059
1060 /*
1061 * Search for the n-th occurrence of a specified pattern,
1062 * either forward or backward.
1063 * Return the number of matches not yet found in this file
1064 * (that is, n minus the number of matches found).
1065 * Return -1 if the search should be aborted.
1066 * Caller may continue the search in another file
1067 * if less than n matches are found in this file.
1068 */
1069 public int
search(search_type,pattern,n)1070 search(search_type, pattern, n)
1071 int search_type;
1072 char *pattern;
1073 int n;
1074 {
1075 POSITION pos;
1076 int ucase;
1077
1078 if (pattern == NULL || *pattern == '\0')
1079 {
1080 /*
1081 * A null pattern means use the previously compiled pattern.
1082 */
1083 if (!prev_pattern())
1084 {
1085 error("No previous regular expression", NULL_PARG);
1086 return (-1);
1087 }
1088 if ((search_type & SRCH_NO_REGEX) !=
1089 (last_search_type & SRCH_NO_REGEX))
1090 {
1091 error("Please re-enter search pattern", NULL_PARG);
1092 return -1;
1093 }
1094 #if HILITE_SEARCH
1095 if (hilite_search == OPT_ON)
1096 {
1097 /*
1098 * Erase the highlights currently on screen.
1099 * If the search fails, we'll redisplay them later.
1100 */
1101 repaint_hilite(0);
1102 }
1103 if (hilite_search == OPT_ONPLUS && hide_hilite)
1104 {
1105 /*
1106 * Highlight any matches currently on screen,
1107 * before we actually start the search.
1108 */
1109 hide_hilite = 0;
1110 hilite_screen();
1111 }
1112 hide_hilite = 0;
1113 #endif
1114 } else
1115 {
1116 /*
1117 * Compile the pattern.
1118 */
1119 ucase = is_ucase(pattern);
1120 if (caseless == OPT_ONPLUS)
1121 cvt_text(pattern, pattern, CVT_TO_LC);
1122 if (compile_pattern(pattern, search_type) < 0)
1123 return (-1);
1124 /*
1125 * Ignore case if -I is set OR
1126 * -i is set AND the pattern is all lowercase.
1127 */
1128 is_ucase_pattern = ucase;
1129 if (is_ucase_pattern && caseless != OPT_ONPLUS)
1130 is_caseless = 0;
1131 else
1132 is_caseless = caseless;
1133 #if HILITE_SEARCH
1134 if (hilite_search)
1135 {
1136 /*
1137 * Erase the highlights currently on screen.
1138 * Also permanently delete them from the hilite list.
1139 */
1140 repaint_hilite(0);
1141 hide_hilite = 0;
1142 clr_hilite();
1143 }
1144 if (hilite_search == OPT_ONPLUS)
1145 {
1146 /*
1147 * Highlight any matches currently on screen,
1148 * before we actually start the search.
1149 */
1150 hilite_screen();
1151 }
1152 #endif
1153 }
1154
1155 /*
1156 * Figure out where to start the search.
1157 */
1158 pos = search_pos(search_type);
1159 if (pos == NULL_POSITION)
1160 {
1161 /*
1162 * Can't find anyplace to start searching from.
1163 */
1164 if (search_type & SRCH_PAST_EOF)
1165 return (n);
1166 /* repaint(); -- why was this here? */
1167 error("Nothing to search", NULL_PARG);
1168 return (-1);
1169 }
1170
1171 n = search_range(pos, NULL_POSITION, search_type, n, -1,
1172 &pos, (POSITION*)NULL);
1173 if (n != 0)
1174 {
1175 /*
1176 * Search was unsuccessful.
1177 */
1178 #if HILITE_SEARCH
1179 if (hilite_search == OPT_ON && n > 0)
1180 /*
1181 * Redisplay old hilites.
1182 */
1183 repaint_hilite(1);
1184 #endif
1185 return (n);
1186 }
1187
1188 if (!(search_type & SRCH_NO_MOVE))
1189 {
1190 /*
1191 * Go to the matching line.
1192 */
1193 jump_loc(pos, jump_sline);
1194 }
1195
1196 #if HILITE_SEARCH
1197 if (hilite_search == OPT_ON)
1198 /*
1199 * Display new hilites in the matching line.
1200 */
1201 repaint_hilite(1);
1202 #endif
1203 return (0);
1204 }
1205
1206
1207 #if HILITE_SEARCH
1208 /*
1209 * Prepare hilites in a given range of the file.
1210 *
1211 * The pair (prep_startpos,prep_endpos) delimits a contiguous region
1212 * of the file that has been "prepared"; that is, scanned for matches for
1213 * the current search pattern, and hilites have been created for such matches.
1214 * If prep_startpos == NULL_POSITION, the prep region is empty.
1215 * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
1216 * prep_hilite asks that the range (spos,epos) be covered by the prep region.
1217 */
1218 public void
prep_hilite(spos,epos,maxlines)1219 prep_hilite(spos, epos, maxlines)
1220 POSITION spos;
1221 POSITION epos;
1222 int maxlines;
1223 {
1224 POSITION nprep_startpos = prep_startpos;
1225 POSITION nprep_endpos = prep_endpos;
1226 POSITION new_epos;
1227 POSITION max_epos;
1228 int result;
1229 int i;
1230 /*
1231 * Search beyond where we're asked to search, so the prep region covers
1232 * more than we need. Do one big search instead of a bunch of small ones.
1233 */
1234 #define SEARCH_MORE (3*size_linebuf)
1235
1236 if (!prev_pattern())
1237 return;
1238
1239 /*
1240 * If we're limited to a max number of lines, figure out the
1241 * file position we should stop at.
1242 */
1243 if (maxlines < 0)
1244 max_epos = NULL_POSITION;
1245 else
1246 {
1247 max_epos = spos;
1248 for (i = 0; i < maxlines; i++)
1249 max_epos = forw_raw_line(max_epos, (char **)NULL);
1250 }
1251
1252 /*
1253 * Find two ranges:
1254 * The range that we need to search (spos,epos); and the range that
1255 * the "prep" region will then cover (nprep_startpos,nprep_endpos).
1256 */
1257
1258 if (prep_startpos == NULL_POSITION ||
1259 (epos != NULL_POSITION && epos < prep_startpos) ||
1260 spos > prep_endpos)
1261 {
1262 /*
1263 * New range is not contiguous with old prep region.
1264 * Discard the old prep region and start a new one.
1265 */
1266 clr_hilite();
1267 if (epos != NULL_POSITION)
1268 epos += SEARCH_MORE;
1269 nprep_startpos = spos;
1270 } else
1271 {
1272 /*
1273 * New range partially or completely overlaps old prep region.
1274 */
1275 if (epos == NULL_POSITION)
1276 {
1277 /*
1278 * New range goes to end of file.
1279 */
1280 ;
1281 } else if (epos > prep_endpos)
1282 {
1283 /*
1284 * New range ends after old prep region.
1285 * Extend prep region to end at end of new range.
1286 */
1287 epos += SEARCH_MORE;
1288 } else /* (epos <= prep_endpos) */
1289 {
1290 /*
1291 * New range ends within old prep region.
1292 * Truncate search to end at start of old prep region.
1293 */
1294 epos = prep_startpos;
1295 }
1296
1297 if (spos < prep_startpos)
1298 {
1299 /*
1300 * New range starts before old prep region.
1301 * Extend old prep region backwards to start at
1302 * start of new range.
1303 */
1304 if (spos < SEARCH_MORE)
1305 spos = 0;
1306 else
1307 spos -= SEARCH_MORE;
1308 nprep_startpos = spos;
1309 } else /* (spos >= prep_startpos) */
1310 {
1311 /*
1312 * New range starts within or after old prep region.
1313 * Trim search to start at end of old prep region.
1314 */
1315 spos = prep_endpos;
1316 }
1317 }
1318
1319 if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
1320 epos > max_epos)
1321 /*
1322 * Don't go past the max position we're allowed.
1323 */
1324 epos = max_epos;
1325
1326 if (epos == NULL_POSITION || epos > spos)
1327 {
1328 result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
1329 maxlines, (POSITION*)NULL, &new_epos);
1330 if (result < 0)
1331 return;
1332 if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
1333 nprep_endpos = new_epos;
1334 }
1335 prep_startpos = nprep_startpos;
1336 prep_endpos = nprep_endpos;
1337 }
1338 #endif
1339
1340 /*
1341 * Simple pattern matching function.
1342 * It supports no metacharacters like *, etc.
1343 */
1344 static int
match(pattern,buf,pfound,pend)1345 match(pattern, buf, pfound, pend)
1346 char *pattern, *buf;
1347 char **pfound, **pend;
1348 {
1349 register char *pp, *lp;
1350
1351 for ( ; *buf != '\0'; buf++)
1352 {
1353 for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
1354 if (*pp == '\0' || *lp == '\0')
1355 break;
1356 if (*pp == '\0')
1357 {
1358 if (pfound != NULL)
1359 *pfound = buf;
1360 if (pend != NULL)
1361 *pend = lp;
1362 return (1);
1363 }
1364 }
1365 return (0);
1366 }
1367
1368 #if HAVE_V8_REGCOMP
1369 /*
1370 * This function is called by the V8 regcomp to report
1371 * errors in regular expressions.
1372 */
1373 void
regerror(s)1374 regerror(s)
1375 char *s;
1376 {
1377 PARG parg;
1378
1379 parg.p_string = s;
1380 error("%s", &parg);
1381 }
1382 #endif
1383
1384