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