]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/less/search.c
MFV 316905
[FreeBSD/FreeBSD.git] / contrib / less / search.c
1 /*
2  * Copyright (C) 1984-2015  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, see the README file.
8  */
9
10
11 /*
12  * Routines to search a file for a pattern.
13  */
14
15 #include "less.h"
16 #include "pattern.h"
17 #include "position.h"
18 #include "charset.h"
19
20 #define MINPOS(a,b)     (((a) < (b)) ? (a) : (b))
21 #define MAXPOS(a,b)     (((a) > (b)) ? (a) : (b))
22
23 extern int sigs;
24 extern int how_search;
25 extern int caseless;
26 extern int linenums;
27 extern int sc_height;
28 extern int jump_sline;
29 extern int bs_mode;
30 extern int ctldisp;
31 extern int status_col;
32 extern void * constant ml_search;
33 extern POSITION start_attnpos;
34 extern POSITION end_attnpos;
35 extern int utf_mode;
36 extern int screen_trashed;
37 #if HILITE_SEARCH
38 extern int hilite_search;
39 extern int size_linebuf;
40 extern int squished;
41 extern int can_goto_line;
42 static int hide_hilite;
43 static POSITION prep_startpos;
44 static POSITION prep_endpos;
45 static int is_caseless;
46 static int is_ucase_pattern;
47
48 /*
49  * Structures for maintaining a set of ranges for hilites and filtered-out
50  * lines. Each range is stored as a node within a red-black tree, and we
51  * try to extend existing ranges (without creating overlaps) rather than
52  * create new nodes if possible. We remember the last node found by a
53  * search for constant-time lookup if the next search is near enough to
54  * the previous. To aid that, we overlay a secondary doubly-linked list
55  * on top of the red-black tree so we can find the preceding/succeeding
56  * nodes also in constant time.
57  *
58  * Each node is allocated from a series of pools, each pool double the size
59  * of the previous (for amortised constant time allocation). Since our only
60  * tree operations are clear and node insertion, not node removal, we don't
61  * need to maintain a usage bitmap or freelist and can just return nodes
62  * from the pool in-order until capacity is reached.
63  */
64 struct hilite
65 {
66         POSITION hl_startpos;
67         POSITION hl_endpos;
68 };
69 struct hilite_node
70 {
71         struct hilite_node *parent;
72         struct hilite_node *left;
73         struct hilite_node *right;
74         struct hilite_node *prev;
75         struct hilite_node *next;
76         int red;
77         struct hilite r;
78 };
79 struct hilite_storage
80 {
81         int capacity;
82         int used;
83         struct hilite_storage *next;
84         struct hilite_node *nodes;
85 };
86 struct hilite_tree
87 {
88         struct hilite_storage *first;
89         struct hilite_storage *current;
90         struct hilite_node *root;
91         struct hilite_node *lookaside;
92 };
93 #define HILITE_INITIALIZER() { NULL, NULL, NULL, NULL }
94 #define HILITE_LOOKASIDE_STEPS 2
95
96 static struct hilite_tree hilite_anchor = HILITE_INITIALIZER();
97 static struct hilite_tree filter_anchor = HILITE_INITIALIZER();
98
99 #endif
100
101 /*
102  * These are the static variables that represent the "remembered"
103  * search pattern and filter pattern.
104  */
105 struct pattern_info {
106         DEFINE_PATTERN(compiled);
107         char* text;
108         int search_type;
109 };
110
111 #if NO_REGEX
112 #define info_compiled(info) ((void*)0)
113 #else
114 #define info_compiled(info) ((info)->compiled)
115 #endif
116         
117 static struct pattern_info search_info;
118 static struct pattern_info filter_info;
119
120 /*
121  * Are there any uppercase letters in this string?
122  */
123         static int
124 is_ucase(constant char *str)
125 {
126         constant char *str_end = str + strlen(str);
127         LWCHAR ch;
128
129         while (str < str_end)
130         {
131                 ch = step_char(&str, +1, str_end);
132                 if (IS_UPPER(ch))
133                         return (1);
134         }
135         return (0);
136 }
137
138 /*
139  * Compile and save a search pattern.
140  */
141         static int
142 set_pattern(struct pattern_info *info, char *pattern, int search_type)
143 {
144 #if !NO_REGEX
145         if (pattern == NULL)
146                 CLEAR_PATTERN(info->compiled);
147         else if (compile_pattern(pattern, search_type,
148             (void **)&info->compiled) < 0)
149                 return -1;
150 #endif
151         /* Pattern compiled successfully; save the text too. */
152         if (info->text != NULL)
153                 free(info->text);
154         info->text = NULL;
155         if (pattern != NULL)
156         {
157                 info->text = (char *) ecalloc(1, strlen(pattern)+1);
158                 strcpy(info->text, pattern);
159         }
160         info->search_type = search_type;
161
162         /*
163          * Ignore case if -I is set OR
164          * -i is set AND the pattern is all lowercase.
165          */
166         is_ucase_pattern = is_ucase(pattern);
167         if (is_ucase_pattern && caseless != OPT_ONPLUS)
168                 is_caseless = 0;
169         else
170                 is_caseless = caseless;
171         return 0;
172 }
173
174 /*
175  * Discard a saved pattern.
176  */
177         static void
178 clear_pattern(struct pattern_info *info)
179 {
180         if (info->text != NULL)
181                 free(info->text);
182         info->text = NULL;
183 #if !NO_REGEX
184         uncompile_pattern((void **)&info->compiled);
185 #endif
186 }
187
188 /*
189  * Initialize saved pattern to nothing.
190  */
191         static void
192 init_pattern(struct pattern_info *info)
193 {
194         CLEAR_PATTERN(info->compiled);
195         info->text = NULL;
196         info->search_type = 0;
197 }
198
199 /*
200  * Initialize search variables.
201  */
202         public void
203 init_search(void)
204 {
205         init_pattern(&search_info);
206         init_pattern(&filter_info);
207 }
208
209 /*
210  * Determine which text conversions to perform before pattern matching.
211  */
212         static int
213 get_cvt_ops(void)
214 {
215         int ops = 0;
216         if (is_caseless || bs_mode == BS_SPECIAL)
217         {
218                 if (is_caseless) 
219                         ops |= CVT_TO_LC;
220                 if (bs_mode == BS_SPECIAL)
221                         ops |= CVT_BS;
222                 if (bs_mode != BS_CONTROL)
223                         ops |= CVT_CRLF;
224         } else if (bs_mode != BS_CONTROL)
225         {
226                 ops |= CVT_CRLF;
227         }
228         if (ctldisp == OPT_ONPLUS)
229                 ops |= CVT_ANSI;
230         return (ops);
231 }
232
233 /*
234  * Is there a previous (remembered) search pattern?
235  */
236         static int
237 prev_pattern(struct pattern_info *info)
238 {
239 #if !NO_REGEX
240         if ((info->search_type & SRCH_NO_REGEX) == 0)
241                 return (!is_null_pattern(info->compiled));
242 #endif
243         return (info->text != NULL);
244 }
245
246 #if HILITE_SEARCH
247 /*
248  * Repaint the hilites currently displayed on the screen.
249  * Repaint each line which contains highlighted text.
250  * If on==0, force all hilites off.
251  */
252         public void
253 repaint_hilite(int on)
254 {
255         int slinenum;
256         POSITION pos;
257         int save_hide_hilite;
258
259         if (squished)
260                 repaint();
261
262         save_hide_hilite = hide_hilite;
263         if (!on)
264         {
265                 if (hide_hilite)
266                         return;
267                 hide_hilite = 1;
268         }
269
270         if (!can_goto_line)
271         {
272                 repaint();
273                 hide_hilite = save_hide_hilite;
274                 return;
275         }
276
277         for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
278         {
279                 pos = position(slinenum);
280                 if (pos == NULL_POSITION)
281                         continue;
282                 (void) forw_line(pos);
283                 goto_line(slinenum);
284                 put_line();
285         }
286         lower_left();
287         hide_hilite = save_hide_hilite;
288 }
289
290 /*
291  * Clear the attn hilite.
292  */
293         public void
294 clear_attn(void)
295 {
296         int slinenum;
297         POSITION old_start_attnpos;
298         POSITION old_end_attnpos;
299         POSITION pos;
300         POSITION epos;
301         int moved = 0;
302
303         if (start_attnpos == NULL_POSITION)
304                 return;
305         old_start_attnpos = start_attnpos;
306         old_end_attnpos = end_attnpos;
307         start_attnpos = end_attnpos = NULL_POSITION;
308
309         if (!can_goto_line)
310         {
311                 repaint();
312                 return;
313         }
314         if (squished)
315                 repaint();
316
317         for (slinenum = TOP;  slinenum < TOP + sc_height-1;  slinenum++)
318         {
319                 pos = position(slinenum);
320                 if (pos == NULL_POSITION)
321                         continue;
322                 epos = position(slinenum+1);
323                 if (pos < old_end_attnpos &&
324                      (epos == NULL_POSITION || epos > old_start_attnpos))
325                 {
326                         (void) forw_line(pos);
327                         goto_line(slinenum);
328                         put_line();
329                         moved = 1;
330                 }
331         }
332         if (moved)
333                 lower_left();
334 }
335 #endif
336
337 /*
338  * Hide search string highlighting.
339  */
340         public void
341 undo_search(void)
342 {
343         if (!prev_pattern(&search_info))
344         {
345                 error("No previous regular expression", NULL_PARG);
346                 return;
347         }
348 #if HILITE_SEARCH
349         hide_hilite = !hide_hilite;
350         repaint_hilite(1);
351 #endif
352 }
353
354 #if HILITE_SEARCH
355 /*
356  * Clear the hilite list.
357  */
358         public void
359 clr_hlist(struct hilite_tree *anchor)
360 {
361         struct hilite_storage *hls;
362         struct hilite_storage *nexthls;
363
364         for (hls = anchor->first;  hls != NULL;  hls = nexthls)
365         {
366                 nexthls = hls->next;
367                 free((void*)hls->nodes);
368                 free((void*)hls);
369         }
370         anchor->first = NULL;
371         anchor->current = NULL;
372         anchor->root = NULL;
373
374         anchor->lookaside = NULL;
375
376         prep_startpos = prep_endpos = NULL_POSITION;
377 }
378
379         public void
380 clr_hilite(void)
381 {
382         clr_hlist(&hilite_anchor);
383 }
384
385         public void
386 clr_filter(void)
387 {
388         clr_hlist(&filter_anchor);
389 }
390
391         struct hilite_node*
392 hlist_last(anchor)
393         struct hilite_tree *anchor;
394 {
395         struct hilite_node *n = anchor->root;
396         while (n != NULL && n->right != NULL)
397                 n = n->right;
398         return n;
399 }
400
401         struct hilite_node*
402 hlist_next(n)
403         struct hilite_node *n;
404 {
405         return n->next;
406 }
407
408         struct hilite_node*
409 hlist_prev(n)
410         struct hilite_node *n;
411 {
412         return n->prev;
413 }
414
415 /*
416  * Find the node covering pos, or the node after it if no node covers it,
417  * or return NULL if pos is after the last range. Remember the found node,
418  * to speed up subsequent searches for the same or similar positions (if
419  * we return NULL, remember the last node.)
420  */
421         struct hilite_node*
422 hlist_find(anchor, pos)
423         struct hilite_tree *anchor;
424         POSITION pos;
425 {
426         struct hilite_node *n, *m;
427
428         if (anchor->lookaside)
429         {
430                 int steps = 0;
431                 int hit = 0;
432
433                 n = anchor->lookaside;
434
435                 for (;;)
436                 {
437                         if (pos < n->r.hl_endpos)
438                         {
439                                 if (n->prev == NULL || pos >= n->prev->r.hl_endpos)
440                                 {
441                                         hit = 1;
442                                         break;
443                                 }
444                         } else if (n->next == NULL)
445                         {
446                                 n = NULL;
447                                 hit = 1;
448                                 break;
449                         }
450
451                         /*
452                          * If we don't find the right node within a small
453                          * distance, don't keep doing a linear search!
454                          */
455                         if (steps >= HILITE_LOOKASIDE_STEPS)
456                                 break;
457                         steps++;
458
459                         if (pos < n->r.hl_endpos)
460                                 anchor->lookaside = n = n->prev;
461                         else
462                                 anchor->lookaside = n = n->next;
463                 }
464
465                 if (hit)
466                         return n;
467         }
468
469         n = anchor->root;
470         m = NULL;
471
472         while (n != NULL)
473         {
474                 if (pos < n->r.hl_startpos)
475                 {
476                         if (n->left != NULL)
477                         {
478                                 m = n;
479                                 n = n->left;
480                                 continue;
481                         }
482                         break;
483                 }
484                 if (pos >= n->r.hl_endpos)
485                 {
486                         if (n->right != NULL)
487                         {
488                                 n = n->right;
489                                 continue;
490                         }
491                         if (m != NULL)
492                         {
493                                 n = m;
494                         } else
495                         {
496                                 m = n;
497                                 n = NULL;
498                         }
499                 }
500                 break;
501         }
502
503         if (n != NULL)
504                 anchor->lookaside = n;
505         else if (m != NULL)
506                 anchor->lookaside = m;
507
508         return n;
509 }
510
511 /*
512  * Should any characters in a specified range be highlighted?
513  */
514         static int
515 is_hilited_range(POSITION pos, POSITION epos)
516 {
517         struct hilite_node *n = hlist_find(&hilite_anchor, pos);
518         return (n != NULL && (epos == NULL_POSITION || epos > n->r.hl_startpos));
519 }
520
521 /* 
522  * Is a line "filtered" -- that is, should it be hidden?
523  */
524         public int
525 is_filtered(POSITION pos)
526 {
527         struct hilite_node *n;
528
529         if (ch_getflags() & CH_HELPFILE)
530                 return (0);
531
532         n = hlist_find(&filter_anchor, pos);
533         return (n != NULL && pos >= n->r.hl_startpos);
534 }
535
536 /*
537  * If pos is hidden, return the next position which isn't, otherwise
538  * just return pos.
539  */
540         public POSITION
541 next_unfiltered(POSITION pos)
542 {
543         struct hilite_node *n;
544
545         if (ch_getflags() & CH_HELPFILE)
546                 return (pos);
547
548         n = hlist_find(&filter_anchor, pos);
549         while (n != NULL && pos >= n->r.hl_startpos)
550         {
551                 pos = n->r.hl_endpos;
552                 n = n->next;
553         }
554         return (pos);
555 }
556
557 /*
558  * If pos is hidden, return the previous position which isn't or 0 if
559  * we're filtered right to the beginning, otherwise just return pos.
560  */
561         public POSITION
562 prev_unfiltered(POSITION pos)
563 {
564         struct hilite_node *n;
565
566         if (ch_getflags() & CH_HELPFILE)
567                 return (pos);
568
569         n = hlist_find(&filter_anchor, pos);
570         while (n != NULL && pos >= n->r.hl_startpos)
571         {
572                 pos = n->r.hl_startpos;
573                 if (pos == 0)
574                         break;
575                 pos--;
576                 n = n->prev;
577         }
578         return (pos);
579 }
580
581
582 /*
583  * Should any characters in a specified range be highlighted?
584  * If nohide is nonzero, don't consider hide_hilite.
585  */
586         public int
587 is_hilited(POSITION pos, POSITION epos, int nohide, int *p_matches)
588 {
589         int match;
590
591         if (p_matches != NULL)
592                 *p_matches = 0;
593
594         if (!status_col &&
595             start_attnpos != NULL_POSITION && 
596             pos < end_attnpos &&
597              (epos == NULL_POSITION || epos > start_attnpos))
598                 /*
599                  * The attn line overlaps this range.
600                  */
601                 return (1);
602
603         match = is_hilited_range(pos, epos);
604         if (!match)
605                 return (0);
606
607         if (p_matches != NULL)
608                 /*
609                  * Report matches, even if we're hiding highlights.
610                  */
611                 *p_matches = 1;
612
613         if (hilite_search == 0)
614                 /*
615                  * Not doing highlighting.
616                  */
617                 return (0);
618
619         if (!nohide && hide_hilite)
620                 /*
621                  * Highlighting is hidden.
622                  */
623                 return (0);
624
625         return (1);
626 }
627
628 /*
629  * Tree node storage: get the current block of nodes if it has spare
630  * capacity, or create a new one if not.
631  */
632         static struct hilite_storage*
633 hlist_getstorage(struct hilite_tree *anchor)
634 {
635         int capacity = 1;
636         struct hilite_storage *s;
637
638         if (anchor->current)
639         {
640                 if (anchor->current->used < anchor->current->capacity)
641                         return anchor->current;
642                 capacity = anchor->current->capacity * 2;
643         }
644
645         s = (struct hilite_storage *) ecalloc(1, sizeof(struct hilite_storage));
646         s->nodes = (struct hilite_node *) ecalloc(capacity, sizeof(struct hilite_node));
647         s->capacity = capacity;
648         s->used = 0;
649         s->next = NULL;
650         if (anchor->current)
651                 anchor->current->next = s;
652         else
653                 anchor->first = s;
654         anchor->current = s;
655         return s;
656 }
657
658 /*
659  * Tree node storage: retrieve a new empty node to be inserted into the
660  * tree.
661  */
662         static struct hilite_node*
663 hlist_getnode(struct hilite_tree *anchor)
664 {
665         struct hilite_storage *s = hlist_getstorage(anchor);
666         return &s->nodes[s->used++];
667 }
668
669 /*
670  * Rotate the tree left around a pivot node.
671  */
672         static void
673 hlist_rotate_left(struct hilite_tree *anchor, struct hilite_node *n)
674 {
675         struct hilite_node *np = n->parent;
676         struct hilite_node *nr = n->right;
677         struct hilite_node *nrl = n->right->left;
678
679         if (np != NULL)
680         {
681                 if (n == np->left)
682                         np->left = nr;
683                 else
684                         np->right = nr;
685         } else
686         {
687                 anchor->root = nr;
688         }
689         nr->left = n;
690         n->right = nrl;
691
692         nr->parent = np;
693         n->parent = nr;
694         if (nrl != NULL)
695                 nrl->parent = n;
696 }
697
698 /*
699  * Rotate the tree right around a pivot node.
700  */
701         static void
702 hlist_rotate_right(struct hilite_tree *anchor, struct hilite_node *n)
703 {
704         struct hilite_node *np = n->parent;
705         struct hilite_node *nl = n->left;
706         struct hilite_node *nlr = n->left->right;
707
708         if (np != NULL)
709         {
710                 if (n == np->right)
711                         np->right = nl;
712                 else
713                         np->left = nl;
714         } else
715         {
716                 anchor->root = nl;
717         }
718         nl->right = n;
719         n->left = nlr;
720
721         nl->parent = np;
722         n->parent = nl;
723         if (nlr != NULL)
724                 nlr->parent = n;
725 }
726
727
728 /*
729  * Add a new hilite to a hilite list.
730  */
731         static void
732 add_hilite(struct hilite_tree *anchor, struct hilite *hl)
733 {
734         struct hilite_node *p, *n, *u;
735
736         /* Ignore empty ranges. */
737         if (hl->hl_startpos >= hl->hl_endpos)
738                 return;
739
740         p = anchor->root;
741
742         /* Inserting the very first node is trivial. */
743         if (p == NULL)
744         {
745                 n = hlist_getnode(anchor);
746                 n->r = *hl;
747                 anchor->root = n;
748                 anchor->lookaside = n;
749                 return;
750         }
751
752         /*
753          * Find our insertion point. If we come across any overlapping
754          * or adjoining existing ranges, shrink our range and discard
755          * if it become empty.
756          */
757         for (;;)
758         {
759                 if (hl->hl_startpos < p->r.hl_startpos)
760                 {
761                         if (hl->hl_endpos > p->r.hl_startpos)
762                                 hl->hl_endpos = p->r.hl_startpos;
763                         if (p->left != NULL)
764                         {
765                                 p = p->left;
766                                 continue;
767                         }
768                         break;
769                 }
770                 if (hl->hl_startpos < p->r.hl_endpos) {
771                         hl->hl_startpos = p->r.hl_endpos;
772                         if (hl->hl_startpos >= hl->hl_endpos)
773                                 return;
774                 }
775                 if (p->right != NULL)
776                 {
777                         p = p->right;
778                         continue;
779                 }
780                 break;
781         }
782
783         /*
784          * Now we're at the right leaf, again check for contiguous ranges
785          * and extend the existing node if possible to avoid the
786          * insertion. Otherwise insert a new node at the leaf.
787          */
788         if (hl->hl_startpos < p->r.hl_startpos) {
789                 if (hl->hl_endpos == p->r.hl_startpos)
790                 {
791                         p->r.hl_startpos = hl->hl_startpos;
792                         return;
793                 }
794                 if (p->prev != NULL && p->prev->r.hl_endpos == hl->hl_startpos)
795                 {
796                         p->prev->r.hl_endpos = hl->hl_endpos;
797                         return;
798                 }
799
800                 p->left = n = hlist_getnode(anchor);
801                 n->next = p;
802                 if (p->prev != NULL)
803                 {
804                         n->prev = p->prev;
805                         p->prev->next = n;
806                 }
807                 p->prev = n;
808         } else {
809                 if (p->r.hl_endpos == hl->hl_startpos)
810                 {
811                         p->r.hl_endpos = hl->hl_endpos;
812                         return;
813                 }
814                 if (p->next != NULL && hl->hl_endpos == p->next->r.hl_startpos) {
815                         p->next->r.hl_startpos = hl->hl_startpos;
816                         return;
817                 }
818
819                 p->right = n = hlist_getnode(anchor);
820                 n->prev = p;
821                 if (p->next != NULL)
822                 {
823                         n->next = p->next;
824                         p->next->prev = n;
825                 }
826                 p->next = n;
827         }
828         n->parent = p;
829         n->red = 1;
830         n->r = *hl;
831
832         /*
833          * The tree is in the correct order and covers the right ranges
834          * now, but may have become unbalanced. Rebalance it using the
835          * standard red-black tree constraints and operations.
836          */
837         for (;;)
838         {
839                 /* case 1 - current is root, root is always black */
840                 if (n->parent == NULL)
841                 {
842                         n->red = 0;
843                         break;
844                 }
845
846                 /* case 2 - parent is black, we can always be red */
847                 if (!n->parent->red)
848                         break;
849
850                 /*
851                  * constraint: because the root must be black, if our
852                  * parent is red it cannot be the root therefore we must
853                  * have a grandparent
854                  */
855
856                 /*
857                  * case 3 - parent and uncle are red, repaint them black,
858                  * the grandparent red, and start again at the grandparent.
859                  */
860                 u = n->parent->parent->left;
861                 if (n->parent == u) 
862                         u = n->parent->parent->right;
863                 if (u != NULL && u->red)
864                 {
865                         n->parent->red = 0;
866                         u->red = 0;
867                         n = n->parent->parent;
868                         n->red = 1;
869                         continue;
870                 }
871
872                 /*
873                  * case 4 - parent is red but uncle is black, parent and
874                  * grandparent on opposite sides. We need to start
875                  * changing the structure now. This and case 5 will shorten
876                  * our branch and lengthen the sibling, between them
877                  * restoring balance.
878                  */
879                 if (n == n->parent->right &&
880                     n->parent == n->parent->parent->left)
881                 {
882                         hlist_rotate_left(anchor, n->parent);
883                         n = n->left;
884                 } else if (n == n->parent->left &&
885                            n->parent == n->parent->parent->right)
886                 {
887                         hlist_rotate_right(anchor, n->parent);
888                         n = n->right;
889                 }
890
891                 /*
892                  * case 5 - parent is red but uncle is black, parent and
893                  * grandparent on same side
894                  */
895                 n->parent->red = 0;
896                 n->parent->parent->red = 1;
897                 if (n == n->parent->left)
898                         hlist_rotate_right(anchor, n->parent->parent);
899                 else
900                         hlist_rotate_left(anchor, n->parent->parent);
901                 break;
902         }
903 }
904
905 /*
906  * Hilight every character in a range of displayed characters.
907  */
908         static void
909 create_hilites(POSITION linepos, int start_index, int end_index, int *chpos)
910 {
911         struct hilite hl;
912         int i;
913
914         /* Start the first hilite. */
915         hl.hl_startpos = linepos + chpos[start_index];
916
917         /*
918          * Step through the displayed chars.
919          * If the source position (before cvt) of the char is one more
920          * than the source pos of the previous char (the usual case),
921          * just increase the size of the current hilite by one.
922          * Otherwise (there are backspaces or something involved),
923          * finish the current hilite and start a new one.
924          */
925         for (i = start_index+1;  i <= end_index;  i++)
926         {
927                 if (chpos[i] != chpos[i-1] + 1 || i == end_index)
928                 {
929                         hl.hl_endpos = linepos + chpos[i-1] + 1;
930                         add_hilite(&hilite_anchor, &hl);
931                         /* Start new hilite unless this is the last char. */
932                         if (i < end_index)
933                         {
934                                 hl.hl_startpos = linepos + chpos[i];
935                         }
936                 }
937         }
938 }
939
940 /*
941  * Make a hilite for each string in a physical line which matches 
942  * the current pattern.
943  * sp,ep delimit the first match already found.
944  */
945         static void
946 hilite_line(POSITION linepos, char *line, int line_len, int *chpos, char *sp,
947     char *ep, int cvt_ops)
948 {
949         char *searchp;
950         char *line_end = line + line_len;
951
952         /*
953          * sp and ep delimit the first match in the line.
954          * Mark the corresponding file positions, then
955          * look for further matches and mark them.
956          * {{ This technique, of calling match_pattern on subsequent
957          *    substrings of the line, may mark more than is correct
958          *    if the pattern starts with "^".  This bug is fixed
959          *    for those regex functions that accept a notbol parameter
960          *    (currently POSIX, PCRE and V8-with-regexec2). }}
961          */
962         searchp = line;
963         do {
964                 if (sp == NULL || ep == NULL)
965                         return;
966                 create_hilites(linepos, sp-line, ep-line, chpos);
967                 /*
968                  * If we matched more than zero characters,
969                  * move to the first char after the string we matched.
970                  * If we matched zero, just move to the next char.
971                  */
972                 if (ep > searchp)
973                         searchp = ep;
974                 else if (searchp != line_end)
975                         searchp++;
976                 else /* end of line */
977                         break;
978         } while (match_pattern(info_compiled(&search_info), search_info.text,
979                         searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type));
980 }
981 #endif
982
983 /*
984  * Change the caseless-ness of searches.  
985  * Updates the internal search state to reflect a change in the -i flag.
986  */
987         public void
988 chg_caseless(void)
989 {
990         if (!is_ucase_pattern)
991                 /*
992                  * Pattern did not have uppercase.
993                  * Just set the search caselessness to the global caselessness.
994                  */
995                 is_caseless = caseless;
996         else
997                 /*
998                  * Pattern did have uppercase.
999                  * Discard the pattern; we can't change search caselessness now.
1000                  */
1001                 clear_pattern(&search_info);
1002 }
1003
1004 #if HILITE_SEARCH
1005 /*
1006  * Find matching text which is currently on screen and highlight it.
1007  */
1008         static void
1009 hilite_screen(void)
1010 {
1011         struct scrpos scrpos;
1012
1013         get_scrpos(&scrpos);
1014         if (scrpos.pos == NULL_POSITION)
1015                 return;
1016         prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
1017         repaint_hilite(1);
1018 }
1019
1020 /*
1021  * Change highlighting parameters.
1022  */
1023         public void
1024 chg_hilite(void)
1025 {
1026         /*
1027          * Erase any highlights currently on screen.
1028          */
1029         clr_hilite();
1030         hide_hilite = 0;
1031
1032         if (hilite_search == OPT_ONPLUS)
1033                 /*
1034                  * Display highlights.
1035                  */
1036                 hilite_screen();
1037 }
1038 #endif
1039
1040 /*
1041  * Figure out where to start a search.
1042  */
1043         static POSITION
1044 search_pos(int search_type)
1045 {
1046         POSITION pos;
1047         int linenum;
1048
1049         if (empty_screen())
1050         {
1051                 /*
1052                  * Start at the beginning (or end) of the file.
1053                  * The empty_screen() case is mainly for 
1054                  * command line initiated searches;
1055                  * for example, "+/xyz" on the command line.
1056                  * Also for multi-file (SRCH_PAST_EOF) searches.
1057                  */
1058                 if (search_type & SRCH_FORW)
1059                 {
1060                         pos = ch_zero();
1061                 } else
1062                 {
1063                         pos = ch_length();
1064                         if (pos == NULL_POSITION)
1065                         {
1066                                 (void) ch_end_seek();
1067                                 pos = ch_length();
1068                         }
1069                 }
1070                 linenum = 0;
1071         } else 
1072         {
1073                 int add_one = 0;
1074
1075                 if (how_search == OPT_ON)
1076                 {
1077                         /*
1078                          * Search does not include current screen.
1079                          */
1080                         if (search_type & SRCH_FORW)
1081                                 linenum = BOTTOM_PLUS_ONE;
1082                         else
1083                                 linenum = TOP;
1084                 } else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET))
1085                 {
1086                         /*
1087                          * Search includes all of displayed screen.
1088                          */
1089                         if (search_type & SRCH_FORW)
1090                                 linenum = TOP;
1091                         else
1092                                 linenum = BOTTOM_PLUS_ONE;
1093                 } else 
1094                 {
1095                         /*
1096                          * Search includes the part of current screen beyond the jump target.
1097                          * It starts at the jump target (if searching backwards),
1098                          * or at the jump target plus one (if forwards).
1099                          */
1100                         linenum = adjsline(jump_sline);
1101                         if (search_type & SRCH_FORW) 
1102                                 add_one = 1;
1103                 }
1104                 pos = position(linenum);
1105                 if (add_one)
1106                         pos = forw_raw_line(pos, (char **)NULL, (int *)NULL);
1107         }
1108
1109         /*
1110          * If the line is empty, look around for a plausible starting place.
1111          */
1112         if (search_type & SRCH_FORW) 
1113         {
1114                 while (pos == NULL_POSITION)
1115                 {
1116                         if (++linenum >= sc_height)
1117                                 break;
1118                         pos = position(linenum);
1119                 }
1120         } else 
1121         {
1122                 while (pos == NULL_POSITION)
1123                 {
1124                         if (--linenum < 0)
1125                                 break;
1126                         pos = position(linenum);
1127                 }
1128         }
1129         return (pos);
1130 }
1131
1132 /*
1133  * Search a subset of the file, specified by start/end position.
1134  */
1135         static int
1136 search_range(POSITION pos, POSITION endpos, int search_type, int matches,
1137     int maxlines, POSITION *plinepos, POSITION *pendpos)
1138 {
1139         char *line;
1140         char *cline;
1141         int line_len;
1142         LINENUM linenum;
1143         char *sp, *ep;
1144         int line_match;
1145         int cvt_ops;
1146         int cvt_len;
1147         int *chpos;
1148         POSITION linepos, oldpos;
1149
1150         linenum = find_linenum(pos);
1151         oldpos = pos;
1152         for (;;)
1153         {
1154                 /*
1155                  * Get lines until we find a matching one or until
1156                  * we hit end-of-file (or beginning-of-file if we're 
1157                  * going backwards), or until we hit the end position.
1158                  */
1159                 if (ABORT_SIGS())
1160                 {
1161                         /*
1162                          * A signal aborts the search.
1163                          */
1164                         return (-1);
1165                 }
1166
1167                 if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
1168                 {
1169                         /*
1170                          * Reached end position without a match.
1171                          */
1172                         if (pendpos != NULL)
1173                                 *pendpos = pos;
1174                         return (matches);
1175                 }
1176                 if (maxlines > 0)
1177                         maxlines--;
1178
1179                 if (search_type & SRCH_FORW)
1180                 {
1181                         /*
1182                          * Read the next line, and save the 
1183                          * starting position of that line in linepos.
1184                          */
1185                         linepos = pos;
1186                         pos = forw_raw_line(pos, &line, &line_len);
1187                         if (linenum != 0)
1188                                 linenum++;
1189                 } else
1190                 {
1191                         /*
1192                          * Read the previous line and save the
1193                          * starting position of that line in linepos.
1194                          */
1195                         pos = back_raw_line(pos, &line, &line_len);
1196                         linepos = pos;
1197                         if (linenum != 0)
1198                                 linenum--;
1199                 }
1200
1201                 if (pos == NULL_POSITION)
1202                 {
1203                         /*
1204                          * Reached EOF/BOF without a match.
1205                          */
1206                         if (pendpos != NULL)
1207                                 *pendpos = oldpos;
1208                         return (matches);
1209                 }
1210
1211                 /*
1212                  * If we're using line numbers, we might as well
1213                  * remember the information we have now (the position
1214                  * and line number of the current line).
1215                  * Don't do it for every line because it slows down
1216                  * the search.  Remember the line number only if
1217                  * we're "far" from the last place we remembered it.
1218                  */
1219                 if (linenums && abs((int)(pos - oldpos)) > 2048)
1220                         add_lnum(linenum, pos);
1221                 oldpos = pos;
1222
1223                 if (is_filtered(linepos))
1224                         continue;
1225
1226                 /*
1227                  * If it's a caseless search, convert the line to lowercase.
1228                  * If we're doing backspace processing, delete backspaces.
1229                  */
1230                 cvt_ops = get_cvt_ops();
1231                 cvt_len = cvt_length(line_len, cvt_ops);
1232                 cline = (char *) ecalloc(1, cvt_len);
1233                 chpos = cvt_alloc_chpos(cvt_len);
1234                 cvt_text(cline, line, chpos, &line_len, cvt_ops);
1235
1236 #if HILITE_SEARCH
1237                 /*
1238                  * Check to see if the line matches the filter pattern.
1239                  * If so, add an entry to the filter list.
1240                  */
1241                 if (((search_type & SRCH_FIND_ALL) ||
1242                      prep_startpos == NULL_POSITION ||
1243                      linepos < prep_startpos || linepos >= prep_endpos) &&
1244                     prev_pattern(&filter_info)) {
1245                         int line_filter = match_pattern(info_compiled(&filter_info), filter_info.text,
1246                                 cline, line_len, &sp, &ep, 0, filter_info.search_type);
1247                         if (line_filter)
1248                         {
1249                                 struct hilite hl;
1250                                 hl.hl_startpos = linepos;
1251                                 hl.hl_endpos = pos;
1252                                 add_hilite(&filter_anchor, &hl);
1253                                 continue;
1254                         }
1255                 }
1256 #endif
1257
1258                 /*
1259                  * Test the next line to see if we have a match.
1260                  * We are successful if we either want a match and got one,
1261                  * or if we want a non-match and got one.
1262                  */
1263                 if (prev_pattern(&search_info))
1264                 {
1265                         line_match = match_pattern(info_compiled(&search_info), search_info.text,
1266                                 cline, line_len, &sp, &ep, 0, search_type);
1267                         if (line_match)
1268                         {
1269                                 /*
1270                                  * Got a match.
1271                                  */
1272                                 if (search_type & SRCH_FIND_ALL)
1273                                 {
1274 #if HILITE_SEARCH
1275                                         /*
1276                                          * We are supposed to find all matches in the range.
1277                                          * Just add the matches in this line to the 
1278                                          * hilite list and keep searching.
1279                                          */
1280                                         hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
1281 #endif
1282                                 } else if (--matches <= 0)
1283                                 {
1284                                         /*
1285                                          * Found the one match we're looking for.
1286                                          * Return it.
1287                                          */
1288 #if HILITE_SEARCH
1289                                         if (hilite_search == OPT_ON)
1290                                         {
1291                                                 /*
1292                                                  * Clear the hilite list and add only
1293                                                  * the matches in this one line.
1294                                                  */
1295                                                 clr_hilite();
1296                                                 hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops);
1297                                         }
1298 #endif
1299                                         free(cline);
1300                                         free(chpos);
1301                                         if (plinepos != NULL)
1302                                                 *plinepos = linepos;
1303                                         return (0);
1304                                 }
1305                         }
1306                 }
1307                 free(cline);
1308                 free(chpos);
1309         }
1310 }
1311
1312 /*
1313  * search for a pattern in history. If found, compile that pattern.
1314  */
1315         static int 
1316 hist_pattern(int search_type)
1317 {
1318 #if CMD_HISTORY
1319         char *pattern;
1320
1321         set_mlist(ml_search, 0);
1322         pattern = cmd_lastpattern();
1323         if (pattern == NULL)
1324                 return (0);
1325
1326         if (set_pattern(&search_info, pattern, search_type) < 0)
1327                 return (0);
1328
1329 #if HILITE_SEARCH
1330         if (hilite_search == OPT_ONPLUS && !hide_hilite)
1331                 hilite_screen();
1332 #endif
1333
1334         return (1);
1335 #else /* CMD_HISTORY */
1336         return (0);
1337 #endif /* CMD_HISTORY */
1338 }
1339
1340 /*
1341  * Search for the n-th occurrence of a specified pattern, 
1342  * either forward or backward.
1343  * Return the number of matches not yet found in this file
1344  * (that is, n minus the number of matches found).
1345  * Return -1 if the search should be aborted.
1346  * Caller may continue the search in another file 
1347  * if less than n matches are found in this file.
1348  */
1349         public int
1350 search(int search_type, char *pattern, int n)
1351 {
1352         POSITION pos;
1353
1354         if (pattern == NULL || *pattern == '\0')
1355         {
1356                 /*
1357                  * A null pattern means use the previously compiled pattern.
1358                  */
1359                 search_type |= SRCH_AFTER_TARGET;
1360                 if (!prev_pattern(&search_info) && !hist_pattern(search_type))
1361                 {
1362                         error("No previous regular expression", NULL_PARG);
1363                         return (-1);
1364                 }
1365                 if ((search_type & SRCH_NO_REGEX) != 
1366                       (search_info.search_type & SRCH_NO_REGEX))
1367                 {
1368                         error("Please re-enter search pattern", NULL_PARG);
1369                         return -1;
1370                 }
1371 #if HILITE_SEARCH
1372                 if (hilite_search == OPT_ON)
1373                 {
1374                         /*
1375                          * Erase the highlights currently on screen.
1376                          * If the search fails, we'll redisplay them later.
1377                          */
1378                         repaint_hilite(0);
1379                 }
1380                 if (hilite_search == OPT_ONPLUS && hide_hilite)
1381                 {
1382                         /*
1383                          * Highlight any matches currently on screen,
1384                          * before we actually start the search.
1385                          */
1386                         hide_hilite = 0;
1387                         hilite_screen();
1388                 }
1389                 hide_hilite = 0;
1390 #endif
1391         } else
1392         {
1393                 /*
1394                  * Compile the pattern.
1395                  */
1396                 if (set_pattern(&search_info, pattern, search_type) < 0)
1397                         return (-1);
1398 #if HILITE_SEARCH
1399                 if (hilite_search)
1400                 {
1401                         /*
1402                          * Erase the highlights currently on screen.
1403                          * Also permanently delete them from the hilite list.
1404                          */
1405                         repaint_hilite(0);
1406                         hide_hilite = 0;
1407                         clr_hilite();
1408                 }
1409                 if (hilite_search == OPT_ONPLUS)
1410                 {
1411                         /*
1412                          * Highlight any matches currently on screen,
1413                          * before we actually start the search.
1414                          */
1415                         hilite_screen();
1416                 }
1417 #endif
1418         }
1419
1420         /*
1421          * Figure out where to start the search.
1422          */
1423         pos = search_pos(search_type);
1424         if (pos == NULL_POSITION)
1425         {
1426                 /*
1427                  * Can't find anyplace to start searching from.
1428                  */
1429                 if (search_type & SRCH_PAST_EOF)
1430                         return (n);
1431                 /* repaint(); -- why was this here? */
1432                 error("Nothing to search", NULL_PARG);
1433                 return (-1);
1434         }
1435
1436         n = search_range(pos, NULL_POSITION, search_type, n, -1,
1437                         &pos, (POSITION*)NULL);
1438         if (n != 0)
1439         {
1440                 /*
1441                  * Search was unsuccessful.
1442                  */
1443 #if HILITE_SEARCH
1444                 if (hilite_search == OPT_ON && n > 0)
1445                         /*
1446                          * Redisplay old hilites.
1447                          */
1448                         repaint_hilite(1);
1449 #endif
1450                 return (n);
1451         }
1452
1453         if (!(search_type & SRCH_NO_MOVE))
1454         {
1455                 /*
1456                  * Go to the matching line.
1457                  */
1458                 jump_loc(pos, jump_sline);
1459         }
1460
1461 #if HILITE_SEARCH
1462         if (hilite_search == OPT_ON)
1463                 /*
1464                  * Display new hilites in the matching line.
1465                  */
1466                 repaint_hilite(1);
1467 #endif
1468         return (0);
1469 }
1470
1471
1472 #if HILITE_SEARCH
1473 /*
1474  * Prepare hilites in a given range of the file.
1475  *
1476  * The pair (prep_startpos,prep_endpos) delimits a contiguous region
1477  * of the file that has been "prepared"; that is, scanned for matches for
1478  * the current search pattern, and hilites have been created for such matches.
1479  * If prep_startpos == NULL_POSITION, the prep region is empty.
1480  * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
1481  * prep_hilite asks that the range (spos,epos) be covered by the prep region.
1482  */
1483         public void
1484 prep_hilite(POSITION spos, POSITION epos, int maxlines)
1485 {
1486         POSITION nprep_startpos = prep_startpos;
1487         POSITION nprep_endpos = prep_endpos;
1488         POSITION new_epos;
1489         POSITION max_epos;
1490         int result;
1491         int i;
1492
1493 /*
1494  * Search beyond where we're asked to search, so the prep region covers
1495  * more than we need.  Do one big search instead of a bunch of small ones.
1496  */
1497 #define SEARCH_MORE (3*size_linebuf)
1498
1499         if (!prev_pattern(&search_info) && !is_filtering())
1500                 return;
1501
1502         /*
1503          * Make sure our prep region always starts at the beginning of
1504          * a line. (search_range takes care of the end boundary below.)
1505          */
1506         spos = back_raw_line(spos+1, (char **)NULL, (int *)NULL);
1507
1508         /*
1509          * If we're limited to a max number of lines, figure out the
1510          * file position we should stop at.
1511          */
1512         if (maxlines < 0)
1513                 max_epos = NULL_POSITION;
1514         else
1515         {
1516                 max_epos = spos;
1517                 for (i = 0;  i < maxlines;  i++)
1518                         max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL);
1519         }
1520
1521         /*
1522          * Find two ranges:
1523          * The range that we need to search (spos,epos); and the range that
1524          * the "prep" region will then cover (nprep_startpos,nprep_endpos).
1525          */
1526
1527         if (prep_startpos == NULL_POSITION ||
1528             (epos != NULL_POSITION && epos < prep_startpos) ||
1529             spos > prep_endpos)
1530         {
1531                 /*
1532                  * New range is not contiguous with old prep region.
1533                  * Discard the old prep region and start a new one.
1534                  */
1535                 clr_hilite();
1536                 clr_filter();
1537                 if (epos != NULL_POSITION)
1538                         epos += SEARCH_MORE;
1539                 nprep_startpos = spos;
1540         } else
1541         {
1542                 /*
1543                  * New range partially or completely overlaps old prep region.
1544                  */
1545                 if (epos == NULL_POSITION)
1546                 {
1547                         /*
1548                          * New range goes to end of file.
1549                          */
1550                         ;
1551                 } else if (epos > prep_endpos)
1552                 {
1553                         /*
1554                          * New range ends after old prep region.
1555                          * Extend prep region to end at end of new range.
1556                          */
1557                         epos += SEARCH_MORE;
1558                 } else /* (epos <= prep_endpos) */
1559                 {
1560                         /*
1561                          * New range ends within old prep region.
1562                          * Truncate search to end at start of old prep region.
1563                          */
1564                         epos = prep_startpos;
1565                 }
1566
1567                 if (spos < prep_startpos)
1568                 {
1569                         /*
1570                          * New range starts before old prep region.
1571                          * Extend old prep region backwards to start at 
1572                          * start of new range.
1573                          */
1574                         if (spos < SEARCH_MORE)
1575                                 spos = 0;
1576                         else
1577                                 spos -= SEARCH_MORE;
1578                         nprep_startpos = spos;
1579                 } else /* (spos >= prep_startpos) */
1580                 {
1581                         /*
1582                          * New range starts within or after old prep region.
1583                          * Trim search to start at end of old prep region.
1584                          */
1585                         spos = prep_endpos;
1586                 }
1587         }
1588
1589         if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
1590             epos > max_epos)
1591                 /*
1592                  * Don't go past the max position we're allowed.
1593                  */
1594                 epos = max_epos;
1595
1596         if (epos == NULL_POSITION || epos > spos)
1597         {
1598                 int search_type = SRCH_FORW | SRCH_FIND_ALL;
1599                 search_type |= (search_info.search_type & SRCH_NO_REGEX);
1600                 for (;;) 
1601                 {
1602                         result = search_range(spos, epos, search_type, 0, maxlines, (POSITION*)NULL, &new_epos);
1603                         if (result < 0)
1604                                 return;
1605                         if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
1606                                 nprep_endpos = new_epos;
1607
1608                         /*
1609                          * Check both ends of the resulting prep region to
1610                          * make sure they're not filtered. If they are,
1611                          * keep going at least one more line until we find
1612                          * something that isn't filtered, or hit the end.
1613                          */
1614                         if (prep_endpos == NULL_POSITION || nprep_endpos > prep_endpos)
1615                         {
1616                                 if (new_epos >= nprep_endpos && is_filtered(new_epos-1))
1617                                 {
1618                                         spos = nprep_endpos;
1619                                         epos = forw_raw_line(nprep_endpos, (char **)NULL, (int *)NULL);
1620                                         if (epos == NULL_POSITION)
1621                                                 break;
1622                                         maxlines = 1;
1623                                         continue;
1624                                 }
1625                         }
1626
1627                         if (prep_startpos == NULL_POSITION || nprep_startpos < prep_startpos)
1628                         {
1629                                 if (nprep_startpos > 0 && is_filtered(nprep_startpos))
1630                                 {
1631                                         epos = nprep_startpos;
1632                                         spos = back_raw_line(nprep_startpos, (char **)NULL, (int *)NULL);
1633                                         if (spos == NULL_POSITION)
1634                                                 break;
1635                                         nprep_startpos = spos;
1636                                         maxlines = 1;
1637                                         continue;
1638                                 }
1639                         }
1640                         break;
1641                 }
1642         }
1643         prep_startpos = nprep_startpos;
1644         prep_endpos = nprep_endpos;
1645 }
1646
1647 /*
1648  * Set the pattern to be used for line filtering.
1649  */
1650         public void
1651 set_filter_pattern(char *pattern, int search_type)
1652 {
1653         clr_filter();
1654         if (pattern == NULL || *pattern == '\0')
1655                 clear_pattern(&filter_info);
1656         else
1657                 set_pattern(&filter_info, pattern, search_type);
1658         screen_trashed = 1;
1659 }
1660
1661 /*
1662  * Is there a line filter in effect?
1663  */
1664         public int
1665 is_filtering(void)
1666 {
1667         if (ch_getflags() & CH_HELPFILE)
1668                 return (0);
1669         return prev_pattern(&filter_info);
1670 }
1671 #endif
1672
1673 #if HAVE_V8_REGCOMP
1674 /*
1675  * This function is called by the V8 regcomp to report 
1676  * errors in regular expressions.
1677  */
1678 public int reg_show_error = 1;
1679
1680         void 
1681 regerror(s) 
1682         char *s; 
1683 {
1684         PARG parg;
1685
1686         if (!reg_show_error)
1687                 return;
1688         parg.p_string = s;
1689         error("%s", &parg);
1690 }
1691 #endif
1692