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