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