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