]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/less/search.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / less / search.c
1 /* $FreeBSD$ */
2 /*
3  * Copyright (C) 1984-2011  Mark Nudelman
4  *
5  * You may distribute under the terms of either the GNU General Public
6  * License or the Less License, as specified in the README file.
7  *
8  * For more information about less, or for information on how to 
9  * contact the author, see the README file.
10  */
11
12
13 /*
14  * Routines to search a file for a pattern.
15  */
16
17 #include "less.h"
18 #include "pattern.h"
19 #include "position.h"
20 #include "charset.h"
21
22 #define MINPOS(a,b)     (((a) < (b)) ? (a) : (b))
23 #define MAXPOS(a,b)     (((a) > (b)) ? (a) : (b))
24
25 extern int sigs;
26 extern int how_search;
27 extern int caseless;
28 extern int linenums;
29 extern int sc_height;
30 extern int jump_sline;
31 extern int bs_mode;
32 extern int less_is_more;
33 extern int ctldisp;
34 extern int status_col;
35 extern void * constant ml_search;
36 extern POSITION start_attnpos;
37 extern POSITION end_attnpos;
38 extern int utf_mode;
39 extern int screen_trashed;
40 #if HILITE_SEARCH
41 extern int hilite_search;
42 extern int size_linebuf;
43 extern int squished;
44 extern int can_goto_line;
45 static int hide_hilite;
46 static POSITION prep_startpos;
47 static POSITION prep_endpos;
48 static int is_caseless;
49 static int is_ucase_pattern;
50
51 struct hilite
52 {
53         struct hilite *hl_next;
54         POSITION hl_startpos;
55         POSITION hl_endpos;
56 };
57 static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
58 static struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION };
59 #define hl_first        hl_next
60 #endif
61
62 /*
63  * These are the static variables that represent the "remembered"
64  * search pattern and filter pattern.
65  */
66 struct pattern_info {
67         DEFINE_PATTERN(compiled);
68         char* text;
69         int search_type;
70 };
71         
72 static struct pattern_info search_info;
73 static struct pattern_info filter_info;
74
75 /*
76  * Are there any uppercase letters in this string?
77  */
78         static int
79 is_ucase(str)
80         char *str;
81 {
82         char *str_end = str + strlen(str);
83         LWCHAR ch;
84
85         while (str < str_end)
86         {
87                 ch = step_char(&str, +1, str_end);
88                 if (IS_UPPER(ch))
89                         return (1);
90         }
91         return (0);
92 }
93
94 /*
95  * Compile and save a search pattern.
96  */
97         static int
98 set_pattern(info, pattern, search_type)
99         struct pattern_info *info;
100         char *pattern;
101         int search_type;
102 {
103         if (pattern == NULL)
104                 CLEAR_PATTERN(search_info.compiled);
105         else if (compile_pattern(pattern, search_type, &info->compiled) < 0)
106                 return -1;
107         /* Pattern compiled successfully; save the text too. */
108         if (info->text != NULL)
109                 free(info->text);
110         info->text = NULL;
111         if (pattern != NULL)
112         {
113                 info->text = (char *) ecalloc(1, strlen(pattern)+1);
114                 strcpy(info->text, pattern);
115         }
116         info->search_type = search_type;
117
118         /*
119          * Ignore case if -I is set OR
120          * -i is set AND the pattern is all lowercase.
121          */
122         is_ucase_pattern = is_ucase(pattern);
123         if (is_ucase_pattern && caseless != OPT_ONPLUS)
124                 is_caseless = 0;
125         else
126                 is_caseless = caseless;
127         return 0;
128 }
129
130 /*
131  * Discard a saved pattern.
132  */
133         static void
134 clear_pattern(info)
135         struct pattern_info *info;
136 {
137         if (info->text != NULL)
138                 free(info->text);
139         info->text = NULL;
140         uncompile_pattern(&info->compiled);
141 }
142
143 /*
144  * Initialize saved pattern to nothing.
145  */
146         static void
147 init_pattern(info)
148         struct pattern_info *info;
149 {
150         CLEAR_PATTERN(info->compiled);
151         info->text = NULL;
152         info->search_type = 0;
153 }
154
155 /*
156  * Initialize search variables.
157  */
158         public void
159 init_search()
160 {
161         init_pattern(&search_info);
162         init_pattern(&filter_info);
163 }
164
165 /*
166  * Determine which text conversions to perform before pattern matching.
167  */
168         static int
169 get_cvt_ops()
170 {
171         int ops = 0;
172         if (is_caseless || bs_mode == BS_SPECIAL)
173         {
174                 if (is_caseless) 
175                         ops |= CVT_TO_LC;
176                 if (bs_mode == BS_SPECIAL)
177                         ops |= CVT_BS;
178                 if (bs_mode != BS_CONTROL)
179                         ops |= CVT_CRLF;
180         } else if (bs_mode != BS_CONTROL)
181         {
182                 ops |= CVT_CRLF;
183         }
184         if (ctldisp == OPT_ONPLUS)
185                 ops |= CVT_ANSI;
186         return (ops);
187 }
188
189 /*
190  * Is there a previous (remembered) search pattern?
191  */
192         static int
193 prev_pattern(info)
194         struct pattern_info *info;
195 {
196         if (info->search_type & SRCH_NO_REGEX)
197                 return (info->text != NULL);
198         return (!is_null_pattern(info->compiled));
199 }
200
201 #if HILITE_SEARCH
202 /*
203  * Repaint the hilites currently displayed on the screen.
204  * Repaint each line which contains highlighted text.
205  * If on==0, force all hilites off.
206  */
207         public void
208 repaint_hilite(on)
209         int on;
210 {
211         int slinenum;
212         POSITION pos;
213         POSITION epos;
214         int save_hide_hilite;
215
216         if (squished)
217                 repaint();
218
219         save_hide_hilite = hide_hilite;
220         if (!on)
221         {
222                 if (hide_hilite)
223                         return;
224                 hide_hilite = 1;
225         }
226
227         if (!can_goto_line)
228         {
229                 repaint();
230                 hide_hilite = save_hide_hilite;
231                 return;
232         }
233
234         for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
235         {
236                 pos = position(slinenum);
237                 if (pos == NULL_POSITION)
238                         continue;
239                 epos = position(slinenum+1);
240                 (void) forw_line(pos);
241                 goto_line(slinenum);
242                 put_line();
243         }
244         lower_left();
245         hide_hilite = save_hide_hilite;
246 }
247
248 /*
249  * Clear the attn hilite.
250  */
251         public void
252 clear_attn()
253 {
254         int slinenum;
255         POSITION old_start_attnpos;
256         POSITION old_end_attnpos;
257         POSITION pos;
258         POSITION epos;
259         int moved = 0;
260
261         if (start_attnpos == NULL_POSITION)
262                 return;
263         old_start_attnpos = start_attnpos;
264         old_end_attnpos = end_attnpos;
265         start_attnpos = end_attnpos = NULL_POSITION;
266
267         if (!can_goto_line)
268         {
269                 repaint();
270                 return;
271         }
272         if (squished)
273                 repaint();
274
275         for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
276         {
277                 pos = position(slinenum);
278                 if (pos == NULL_POSITION)
279                         continue;
280                 epos = position(slinenum+1);
281                 if (pos < old_end_attnpos &&
282                      (epos == NULL_POSITION || epos > old_start_attnpos))
283                 {
284                         (void) forw_line(pos);
285                         goto_line(slinenum);
286                         put_line();
287                         moved = 1;
288                 }
289         }
290         if (moved)
291                 lower_left();
292 }
293 #endif
294
295 /*
296  * Hide search string highlighting.
297  */
298         public void
299 undo_search()
300 {
301         if (!prev_pattern(&search_info))
302         {
303                 error("No previous regular expression", NULL_PARG);
304                 return;
305         }
306 #if HILITE_SEARCH
307         hide_hilite = !hide_hilite;
308         repaint_hilite(1);
309 #endif
310 }
311
312 #if HILITE_SEARCH
313 /*
314  * Clear the hilite list.
315  */
316         public void
317 clr_hlist(anchor)
318         struct hilite *anchor;
319 {
320         struct hilite *hl;
321         struct hilite *nexthl;
322
323         for (hl = anchor->hl_first;  hl != NULL;  hl = nexthl)
324         {
325                 nexthl = hl->hl_next;
326                 free((void*)hl);
327         }
328         anchor->hl_first = NULL;
329         prep_startpos = prep_endpos = NULL_POSITION;
330 }
331
332         public void
333 clr_hilite()
334 {
335         clr_hlist(&hilite_anchor);
336 }
337
338         public void
339 clr_filter()
340 {
341         clr_hlist(&filter_anchor);
342 }
343
344 /*
345  * Should any characters in a specified range be highlighted?
346  */
347         static int
348 is_hilited_range(pos, epos)
349         POSITION pos;
350         POSITION epos;
351 {
352         struct hilite *hl;
353
354         /*
355          * Look at each highlight and see if any part of it falls in the range.
356          */
357         for (hl = hilite_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
358         {
359                 if (hl->hl_endpos > pos &&
360                     (epos == NULL_POSITION || epos > hl->hl_startpos))
361                         return (1);
362         }
363         return (0);
364 }
365
366 /* 
367  * Is a line "filtered" -- that is, should it be hidden?
368  */
369         public int
370 is_filtered(pos)
371         POSITION pos;
372 {
373         struct hilite *hl;
374
375         if (ch_getflags() & CH_HELPFILE)
376                 return (0);
377
378         /*
379          * Look at each filter and see if the start position
380          * equals the start position of the line.
381          */
382         for (hl = filter_anchor.hl_first;  hl != NULL;  hl = hl->hl_next)
383         {
384                 if (hl->hl_startpos == pos)
385                         return (1);
386         }
387         return (0);
388 }
389
390 /*
391  * Should any characters in a specified range be highlighted?
392  * If nohide is nonzero, don't consider hide_hilite.
393  */
394         public int
395 is_hilited(pos, epos, nohide, p_matches)
396         POSITION pos;
397         POSITION epos;
398         int nohide;
399         int *p_matches;
400 {
401         int match;
402
403         if (p_matches != NULL)
404                 *p_matches = 0;
405
406         if (!status_col &&
407             start_attnpos != NULL_POSITION && 
408             pos < end_attnpos &&
409              (epos == NULL_POSITION || epos > start_attnpos))
410                 /*
411                  * The attn line overlaps this range.
412                  */
413                 return (1);
414
415         match = is_hilited_range(pos, epos);
416         if (!match)
417                 return (0);
418
419         if (p_matches != NULL)
420                 /*
421                  * Report matches, even if we're hiding highlights.
422                  */
423                 *p_matches = 1;
424
425         if (hilite_search == 0)
426                 /*
427                  * Not doing highlighting.
428                  */
429                 return (0);
430
431         if (!nohide && hide_hilite)
432                 /*
433                  * Highlighting is hidden.
434                  */
435                 return (0);
436
437         return (1);
438 }
439
440 /*
441  * Add a new hilite to a hilite list.
442  */
443         static void
444 add_hilite(anchor, hl)
445         struct hilite *anchor;
446         struct hilite *hl;
447 {
448         struct hilite *ihl;
449
450         /*
451          * Hilites are sorted in the list; find where new one belongs.
452          * Insert new one after ihl.
453          */
454         for (ihl = anchor;  ihl->hl_next != NULL;  ihl = ihl->hl_next)
455         {
456                 if (ihl->hl_next->hl_startpos > hl->hl_startpos)
457                         break;
458         }
459
460         /*
461          * Truncate hilite so it doesn't overlap any existing ones
462          * above and below it.
463          */
464         if (ihl != anchor)
465                 hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
466         if (ihl->hl_next != NULL)
467                 hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
468         if (hl->hl_startpos >= hl->hl_endpos)
469         {
470                 /*
471                  * Hilite was truncated out of existence.
472                  */
473                 free(hl);
474                 return;
475         }
476         hl->hl_next = ihl->hl_next;
477         ihl->hl_next = hl;
478 }
479
480 /*
481  * Make a hilite for each string in a physical line which matches 
482  * the current pattern.
483  * sp,ep delimit the first match already found.
484  */
485         static void
486 hilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops)
487         POSITION linepos;
488         char *line;
489         int line_len;
490         int *chpos;
491         char *sp;
492         char *ep;
493         int cvt_ops;
494 {
495         char *searchp;
496         char *line_end = line + line_len;
497         struct hilite *hl;
498
499         if (sp == NULL || ep == NULL)
500                 return;
501         /*
502          * sp and ep delimit the first match in the line.
503          * Mark the corresponding file positions, then
504          * look for further matches and mark them.
505          * {{ This technique, of calling match_pattern on subsequent
506          *    substrings of the line, may mark more than is correct
507          *    if the pattern starts with "^".  This bug is fixed
508          *    for those regex functions that accept a notbol parameter
509          *    (currently POSIX, PCRE and V8-with-regexec2). }}
510          */
511         searchp = line;
512         do {
513                 if (ep > sp)
514                 {
515                         hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
516                         hl->hl_startpos = linepos + chpos[sp-line];
517                         hl->hl_endpos = linepos + chpos[ep-line];
518                         add_hilite(&hilite_anchor, hl);
519                 }
520                 /*
521                  * If we matched more than zero characters,
522                  * move to the first char after the string we matched.
523                  * If we matched zero, just move to the next char.
524                  */
525                 if (ep > searchp)
526                         searchp = ep;
527                 else if (searchp != line_end)
528                         searchp++;
529                 else /* end of line */
530                         break;
531         } while (match_pattern(search_info.compiled, search_info.text,
532                         searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type));
533 }
534 #endif
535
536 /*
537  * Change the caseless-ness of searches.  
538  * Updates the internal search state to reflect a change in the -i flag.
539  */
540         public void
541 chg_caseless()
542 {
543         if (!is_ucase_pattern)
544                 /*
545                  * Pattern did not have uppercase.
546                  * Just set the search caselessness to the global caselessness.
547                  */
548                 is_caseless = caseless;
549         else
550                 /*
551                  * Pattern did have uppercase.
552                  * Discard the pattern; we can't change search caselessness now.
553                  */
554                 clear_pattern(&search_info);
555 }
556
557 #if HILITE_SEARCH
558 /*
559  * Find matching text which is currently on screen and highlight it.
560  */
561         static void
562 hilite_screen()
563 {
564         struct scrpos scrpos;
565
566         get_scrpos(&scrpos);
567         if (scrpos.pos == NULL_POSITION)
568                 return;
569         prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
570         repaint_hilite(1);
571 }
572
573 /*
574  * Change highlighting parameters.
575  */
576         public void
577 chg_hilite()
578 {
579         /*
580          * Erase any highlights currently on screen.
581          */
582         clr_hilite();
583         hide_hilite = 0;
584
585         if (hilite_search == OPT_ONPLUS)
586                 /*
587                  * Display highlights.
588                  */
589                 hilite_screen();
590 }
591 #endif
592
593 /*
594  * Figure out where to start a search.
595  */
596         static POSITION
597 search_pos(search_type)
598         int search_type;
599 {
600         POSITION pos;
601         int linenum;
602
603         if (empty_screen())
604         {
605                 /*
606                  * Start at the beginning (or end) of the file.
607                  * The empty_screen() case is mainly for 
608                  * command line initiated searches;
609                  * for example, "+/xyz" on the command line.
610                  * Also for multi-file (SRCH_PAST_EOF) searches.
611                  */
612                 if (search_type & SRCH_FORW)
613                 {
614                         pos = ch_zero();
615                 } else
616                 {
617                         pos = ch_length();
618                         if (pos == NULL_POSITION)
619                         {
620                                 (void) ch_end_seek();
621                                 pos = ch_length();
622                         }
623                 }
624                 linenum = 0;
625         } else 
626         {
627                 int add_one = 0;
628
629                 if (how_search == OPT_ON)
630                 {
631                         /*
632                          * Search does not include current screen.
633                          */
634                         if (search_type & SRCH_FORW)
635                                 linenum = BOTTOM_PLUS_ONE;
636                         else
637                                 linenum = TOP;
638                 } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET))
639                 {
640                         /*
641                          * Search includes all of displayed screen.
642                          */
643                         if (search_type & SRCH_FORW)
644                                 linenum = TOP;
645                         else
646                                 linenum = BOTTOM_PLUS_ONE;
647                 } else 
648                 {
649                         /*
650                          * Search includes the part of current screen beyond the jump target.
651                          * It starts at the jump target (if searching backwards),
652                          * or at the jump target plus one (if forwards).
653                          */
654                         linenum = jump_sline;
655                         if (search_type & SRCH_FORW) 
656                             add_one = 1;
657                 }
658                 linenum = adjsline(linenum);
659                 pos = position(linenum);
660                 if (add_one)
661                         pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
662         }
663
664         /*
665          * If the line is empty, look around for a plausible starting place.
666          */
667         if (search_type & SRCH_FORW) 
668         {
669             while (pos == NULL_POSITION)
670             {
671                 if (++linenum >= sc_height)
672                     break;
673                 pos = position(linenum);
674             }
675         } else 
676         {
677             while (pos == NULL_POSITION)
678             {
679                 if (--linenum < 0)
680                     break;
681                 pos = position(linenum);
682             }
683         }
684         return (pos);
685 }
686
687 /*
688  * Search a subset of the file, specified by start/end position.
689  */
690         static int
691 search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
692         POSITION pos;
693         POSITION endpos;
694         int search_type;
695         int matches;
696         int maxlines;
697         POSITION *plinepos;
698         POSITION *pendpos;
699 {
700         char *line;
701         char *cline;
702         int line_len;
703         LINENUM linenum;
704         char *sp, *ep;
705         int line_match;
706         int cvt_ops;
707         int cvt_len;
708         int *chpos;
709         POSITION linepos, oldpos;
710
711         linenum = find_linenum(pos);
712         oldpos = pos;
713         for (;;)
714         {
715                 /*
716                  * Get lines until we find a matching one or until
717                  * we hit end-of-file (or beginning-of-file if we're 
718                  * going backwards), or until we hit the end position.
719                  */
720                 if (ABORT_SIGS())
721                 {
722                         /*
723                          * A signal aborts the search.
724                          */
725                         return (-1);
726                 }
727
728                 if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
729                 {
730                         /*
731                          * Reached end position without a match.
732                          */
733                         if (pendpos != NULL)
734                                 *pendpos = pos;
735                         return (matches);
736                 }
737                 if (maxlines > 0)
738                         maxlines--;
739
740                 if (search_type & SRCH_FORW)
741                 {
742                         /*
743                          * Read the next line, and save the 
744                          * starting position of that line in linepos.
745                          */
746                         linepos = pos;
747                         pos = forw_raw_line(pos, &line, &line_len);
748                         if (linenum != 0)
749                                 linenum++;
750                 } else
751                 {
752                         /*
753                          * Read the previous line and save the
754                          * starting position of that line in linepos.
755                          */
756                         pos = back_raw_line(pos, &line, &line_len);
757                         linepos = pos;
758                         if (linenum != 0)
759                                 linenum--;
760                 }
761
762                 if (pos == NULL_POSITION)
763                 {
764                         /*
765                          * Reached EOF/BOF without a match.
766                          */
767                         if (pendpos != NULL)
768                                 *pendpos = oldpos;
769                         return (matches);
770                 }
771
772                 /*
773                  * If we're using line numbers, we might as well
774                  * remember the information we have now (the position
775                  * and line number of the current line).
776                  * Don't do it for every line because it slows down
777                  * the search.  Remember the line number only if
778                  * we're "far" from the last place we remembered it.
779                  */
780                 if (linenums && abs((int)(pos - oldpos)) > 2048)
781                         add_lnum(linenum, pos);
782                 oldpos = pos;
783
784                 if (is_filtered(linepos))
785                         continue;
786
787                 /*
788                  * If it's a caseless search, convert the line to lowercase.
789                  * If we're doing backspace processing, delete backspaces.
790                  */
791                 cvt_ops = get_cvt_ops();
792                 cvt_len = cvt_length(line_len, cvt_ops);
793                 cline = (char *) ecalloc(1, cvt_len);
794                 chpos = cvt_alloc_chpos(cvt_len);
795                 cvt_text(cline, line, chpos, &line_len, cvt_ops);
796
797 #if HILITE_SEARCH
798                 /*
799                  * Check to see if the line matches the filter pattern.
800                  * If so, add an entry to the filter list.
801                  */
802                 if ((search_type & SRCH_FIND_ALL) && prev_pattern(&filter_info)) {
803                         int line_filter = match_pattern(filter_info.compiled, filter_info.text,
804                                 cline, line_len, &sp, &ep, 0, filter_info.search_type);
805                         if (line_filter)
806                         {
807                                 struct hilite *hl = (struct hilite *)
808                                         ecalloc(1, sizeof(struct hilite));
809                                 hl->hl_startpos = linepos;
810                                 hl->hl_endpos = pos;
811                                 add_hilite(&filter_anchor, hl);
812                         }
813                 }
814 #endif
815
816                 /*
817                  * Test the next line to see if we have a match.
818                  * We are successful if we either want a match and got one,
819                  * or if we want a non-match and got one.
820                  */
821                 if (prev_pattern(&search_info))
822                 {
823                         line_match = match_pattern(search_info.compiled, search_info.text,
824                                 cline, line_len, &sp, &ep, 0, search_type);
825                         if (line_match)
826                         {
827                                 /*
828                                  * Got a match.
829                                  */
830                                 if (search_type & SRCH_FIND_ALL)
831                                 {
832 #if HILITE_SEARCH
833                                         /*
834                                          * We are supposed to find all matches in the range.
835                                          * Just add the matches in this line to the 
836                                          * hilite list and keep searching.
837                                          */
838                                         hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
839 #endif
840                                 } else if (--matches <= 0)
841                                 {
842                                         /*
843                                          * Found the one match we're looking for.
844                                          * Return it.
845                                          */
846 #if HILITE_SEARCH
847                                         if (hilite_search == OPT_ON)
848                                         {
849                                                 /*
850                                                  * Clear the hilite list and add only
851                                                  * the matches in this one line.
852                                                  */
853                                                 clr_hilite();
854                                                 hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
855                                         }
856 #endif
857                                         free(cline);
858                                         free(chpos);
859                                         if (plinepos != NULL)
860                                                 *plinepos = linepos;
861                                         return (0);
862                                 }
863                         }
864                 }
865                 free(cline);
866                 free(chpos);
867         }
868 }
869
870 /*
871  * search for a pattern in history. If found, compile that pattern.
872  */
873         static int 
874 hist_pattern(search_type) 
875         int search_type;
876 {
877 #if CMD_HISTORY
878         char *pattern;
879
880         set_mlist(ml_search, 0);
881         pattern = cmd_lastpattern();
882         if (pattern == NULL)
883                 return (0);
884
885         if (set_pattern(&search_info, pattern, search_type) < 0)
886                 return (0);
887
888 #if HILITE_SEARCH
889         if (hilite_search == OPT_ONPLUS && !hide_hilite)
890                 hilite_screen();
891 #endif
892
893         return (1);
894 #else /* CMD_HISTORY */
895         return (0);
896 #endif /* CMD_HISTORY */
897 }
898
899 /*
900  * Search for the n-th occurrence of a specified pattern, 
901  * either forward or backward.
902  * Return the number of matches not yet found in this file
903  * (that is, n minus the number of matches found).
904  * Return -1 if the search should be aborted.
905  * Caller may continue the search in another file 
906  * if less than n matches are found in this file.
907  */
908         public int
909 search(search_type, pattern, n)
910         int search_type;
911         char *pattern;
912         int n;
913 {
914         POSITION pos;
915
916         if (pattern == NULL || *pattern == '\0')
917         {
918                 /*
919                  * A null pattern means use the previously compiled pattern.
920                  */
921                 search_type |= SRCH_AFTER_TARGET;
922                 if (!prev_pattern(&search_info) && !hist_pattern(search_type))
923                 {
924                         error("No previous regular expression", NULL_PARG);
925                         return (-1);
926                 }
927                 if ((search_type & SRCH_NO_REGEX) != 
928                       (search_info.search_type & SRCH_NO_REGEX))
929                 {
930                         error("Please re-enter search pattern", NULL_PARG);
931                         return -1;
932                 }
933 #if HILITE_SEARCH
934                 if (hilite_search == OPT_ON)
935                 {
936                         /*
937                          * Erase the highlights currently on screen.
938                          * If the search fails, we'll redisplay them later.
939                          */
940                         repaint_hilite(0);
941                 }
942                 if (hilite_search == OPT_ONPLUS && hide_hilite)
943                 {
944                         /*
945                          * Highlight any matches currently on screen,
946                          * before we actually start the search.
947                          */
948                         hide_hilite = 0;
949                         hilite_screen();
950                 }
951                 hide_hilite = 0;
952 #endif
953         } else
954         {
955                 /*
956                  * Compile the pattern.
957                  */
958                 if (set_pattern(&search_info, pattern, search_type) < 0)
959                         return (-1);
960 #if HILITE_SEARCH
961                 if (hilite_search)
962                 {
963                         /*
964                          * Erase the highlights currently on screen.
965                          * Also permanently delete them from the hilite list.
966                          */
967                         repaint_hilite(0);
968                         hide_hilite = 0;
969                         clr_hilite();
970                 }
971                 if (hilite_search == OPT_ONPLUS)
972                 {
973                         /*
974                          * Highlight any matches currently on screen,
975                          * before we actually start the search.
976                          */
977                         hilite_screen();
978                 }
979 #endif
980         }
981
982         /*
983          * Figure out where to start the search.
984          */
985         pos = search_pos(search_type);
986         if (pos == NULL_POSITION)
987         {
988                 /*
989                  * Can't find anyplace to start searching from.
990                  */
991                 if (search_type & SRCH_PAST_EOF)
992                         return (n);
993                 /* repaint(); -- why was this here? */
994                 error("Nothing to search", NULL_PARG);
995                 return (-1);
996         }
997
998         n = search_range(pos, NULL_POSITION, search_type, n, -1,
999                         &pos, (POSITION*)NULL);
1000         if (n != 0)
1001         {
1002                 /*
1003                  * Search was unsuccessful.
1004                  */
1005 #if HILITE_SEARCH
1006                 if (hilite_search == OPT_ON && n > 0)
1007                         /*
1008                          * Redisplay old hilites.
1009                          */
1010                         repaint_hilite(1);
1011 #endif
1012                 return (n);
1013         }
1014
1015         if (!(search_type & SRCH_NO_MOVE))
1016         {
1017                 /*
1018                  * Go to the matching line.
1019                  */
1020                 jump_loc(pos, jump_sline);
1021         }
1022
1023 #if HILITE_SEARCH
1024         if (hilite_search == OPT_ON)
1025                 /*
1026                  * Display new hilites in the matching line.
1027                  */
1028                 repaint_hilite(1);
1029 #endif
1030         return (0);
1031 }
1032
1033
1034 #if HILITE_SEARCH
1035 /*
1036  * Prepare hilites in a given range of the file.
1037  *
1038  * The pair (prep_startpos,prep_endpos) delimits a contiguous region
1039  * of the file that has been "prepared"; that is, scanned for matches for
1040  * the current search pattern, and hilites have been created for such matches.
1041  * If prep_startpos == NULL_POSITION, the prep region is empty.
1042  * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
1043  * prep_hilite asks that the range (spos,epos) be covered by the prep region.
1044  */
1045         public void
1046 prep_hilite(spos, epos, maxlines)
1047         POSITION spos;
1048         POSITION epos;
1049         int maxlines;
1050 {
1051         POSITION nprep_startpos = prep_startpos;
1052         POSITION nprep_endpos = prep_endpos;
1053         POSITION new_epos;
1054         POSITION max_epos;
1055         int result;
1056         int i;
1057
1058 /*
1059  * Search beyond where we're asked to search, so the prep region covers
1060  * more than we need.  Do one big search instead of a bunch of small ones.
1061  */
1062 #define SEARCH_MORE (3*size_linebuf)
1063
1064         if (!prev_pattern(&search_info) && !is_filtering())
1065                 return;
1066
1067         /*
1068          * If we're limited to a max number of lines, figure out the
1069          * file position we should stop at.
1070          */
1071         if (maxlines < 0)
1072                 max_epos = NULL_POSITION;
1073         else
1074         {
1075                 max_epos = spos;
1076                 for (i = 0;  i < maxlines;  i++)
1077                         max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL);
1078         }
1079
1080         /*
1081          * Find two ranges:
1082          * The range that we need to search (spos,epos); and the range that
1083          * the "prep" region will then cover (nprep_startpos,nprep_endpos).
1084          */
1085
1086         if (prep_startpos == NULL_POSITION ||
1087             (epos != NULL_POSITION && epos < prep_startpos) ||
1088             spos > prep_endpos)
1089         {
1090                 /*
1091                  * New range is not contiguous with old prep region.
1092                  * Discard the old prep region and start a new one.
1093                  */
1094                 clr_hilite();
1095                 clr_filter();
1096                 if (epos != NULL_POSITION)
1097                         epos += SEARCH_MORE;
1098                 nprep_startpos = spos;
1099         } else
1100         {
1101                 /*
1102                  * New range partially or completely overlaps old prep region.
1103                  */
1104                 if (epos == NULL_POSITION)
1105                 {
1106                         /*
1107                          * New range goes to end of file.
1108                          */
1109                         ;
1110                 } else if (epos > prep_endpos)
1111                 {
1112                         /*
1113                          * New range ends after old prep region.
1114                          * Extend prep region to end at end of new range.
1115                          */
1116                         epos += SEARCH_MORE;
1117                 } else /* (epos <= prep_endpos) */
1118                 {
1119                         /*
1120                          * New range ends within old prep region.
1121                          * Truncate search to end at start of old prep region.
1122                          */
1123                         epos = prep_startpos;
1124                 }
1125
1126                 if (spos < prep_startpos)
1127                 {
1128                         /*
1129                          * New range starts before old prep region.
1130                          * Extend old prep region backwards to start at 
1131                          * start of new range.
1132                          */
1133                         if (spos < SEARCH_MORE)
1134                                 spos = 0;
1135                         else
1136                                 spos -= SEARCH_MORE;
1137                         nprep_startpos = spos;
1138                 } else /* (spos >= prep_startpos) */
1139                 {
1140                         /*
1141                          * New range starts within or after old prep region.
1142                          * Trim search to start at end of old prep region.
1143                          */
1144                         spos = prep_endpos;
1145                 }
1146         }
1147
1148         if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
1149             epos > max_epos)
1150                 /*
1151                  * Don't go past the max position we're allowed.
1152                  */
1153                 epos = max_epos;
1154
1155         if (epos == NULL_POSITION || epos > spos)
1156         {
1157                 int search_type = SRCH_FORW | SRCH_FIND_ALL;
1158                 search_type |= (search_info.search_type & SRCH_NO_REGEX);
1159                 result = search_range(spos, epos, search_type, 0,
1160                                 maxlines, (POSITION*)NULL, &new_epos);
1161                 if (result < 0)
1162                         return;
1163                 if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
1164                         nprep_endpos = new_epos;
1165         }
1166         prep_startpos = nprep_startpos;
1167         prep_endpos = nprep_endpos;
1168 }
1169
1170 /*
1171  * Set the pattern to be used for line filtering.
1172  */
1173         public void
1174 set_filter_pattern(pattern, search_type)
1175         char *pattern;
1176         int search_type;
1177 {
1178         clr_filter();
1179         if (pattern == NULL || *pattern == '\0')
1180                 clear_pattern(&filter_info);
1181         else
1182                 set_pattern(&filter_info, pattern, search_type);
1183         screen_trashed = 1;
1184 }
1185
1186 /*
1187  * Is there a line filter in effect?
1188  */
1189         public int
1190 is_filtering()
1191 {
1192         if (ch_getflags() & CH_HELPFILE)
1193                 return (0);
1194         return prev_pattern(&filter_info);
1195 }
1196 #endif
1197
1198 #if HAVE_V8_REGCOMP
1199 /*
1200  * This function is called by the V8 regcomp to report 
1201  * errors in regular expressions.
1202  */
1203         void 
1204 regerror(s) 
1205         char *s; 
1206 {
1207         PARG parg;
1208
1209         parg.p_string = s;
1210         error("%s", &parg);
1211 }
1212 #endif
1213