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