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