Annotation of src/usr.bin/less/search.c, Revision 1.18
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: if (sp == NULL || ep == NULL)
481: return;
482: /*
483: * sp and ep delimit the first match in the line.
484: * Mark the corresponding file positions, then
485: * look for further matches and mark them.
486: * {{ This technique, of calling match_pattern on subsequent
487: * substrings of the line, may mark more than is correct
1.5 millert 488: * if the pattern starts with "^". This bug is fixed
489: * for those regex functions that accept a notbol parameter
1.6 shadchin 490: * (currently POSIX, PCRE and V8-with-regexec2). }}
1.1 etheisen 491: */
492: searchp = line;
493: do {
1.10 nicm 494: create_hilites(linepos, (intptr_t)sp - (intptr_t)line,
495: (intptr_t)ep - (intptr_t)line, chpos);
1.1 etheisen 496: /*
497: * If we matched more than zero characters,
498: * move to the first char after the string we matched.
499: * If we matched zero, just move to the next char.
500: */
501: if (ep > searchp)
502: searchp = ep;
1.6 shadchin 503: else if (searchp != line_end)
1.1 etheisen 504: searchp++;
505: else /* end of line */
506: break;
1.8 shadchin 507: } while (match_pattern(info_compiled(&search_info), search_info.text,
1.10 nicm 508: searchp, (intptr_t)line_end - (intptr_t)searchp, &sp, &ep, 1,
509: search_info.search_type));
1.1 etheisen 510: }
511:
512: /*
1.10 nicm 513: * Change the caseless-ness of searches.
1.1 etheisen 514: * Updates the internal search state to reflect a change in the -i flag.
515: */
1.10 nicm 516: void
517: chg_caseless(void)
1.1 etheisen 518: {
519: if (!is_ucase_pattern)
520: /*
521: * Pattern did not have uppercase.
522: * Just set the search caselessness to the global caselessness.
523: */
524: is_caseless = caseless;
525: else
526: /*
527: * Pattern did have uppercase.
528: * Discard the pattern; we can't change search caselessness now.
529: */
1.6 shadchin 530: clear_pattern(&search_info);
1.1 etheisen 531: }
532:
533: /*
534: * Find matching text which is currently on screen and highlight it.
535: */
1.10 nicm 536: static void
537: hilite_screen(void)
1.1 etheisen 538: {
539: struct scrpos scrpos;
540:
541: get_scrpos(&scrpos);
1.10 nicm 542: if (scrpos.pos == -1)
1.1 etheisen 543: return;
1.5 millert 544: prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
1.1 etheisen 545: repaint_hilite(1);
546: }
547:
548: /*
549: * Change highlighting parameters.
550: */
1.10 nicm 551: void
552: chg_hilite(void)
1.1 etheisen 553: {
554: /*
555: * Erase any highlights currently on screen.
556: */
557: clr_hilite();
558: hide_hilite = 0;
559:
560: if (hilite_search == OPT_ONPLUS)
561: /*
562: * Display highlights.
563: */
564: hilite_screen();
565: }
566:
567: /*
568: * Figure out where to start a search.
569: */
1.10 nicm 570: static off_t
571: search_pos(int search_type)
1.1 etheisen 572: {
1.10 nicm 573: off_t pos;
1.1 etheisen 574: int linenum;
575:
1.10 nicm 576: if (empty_screen()) {
1.1 etheisen 577: /*
578: * Start at the beginning (or end) of the file.
1.10 nicm 579: * The empty_screen() case is mainly for
1.1 etheisen 580: * command line initiated searches;
581: * for example, "+/xyz" on the command line.
582: * Also for multi-file (SRCH_PAST_EOF) searches.
583: */
1.10 nicm 584: if (search_type & SRCH_FORW) {
1.6 shadchin 585: pos = ch_zero();
1.10 nicm 586: } else {
1.1 etheisen 587: pos = ch_length();
1.10 nicm 588: if (pos == -1) {
1.1 etheisen 589: (void) ch_end_seek();
590: pos = ch_length();
591: }
592: }
1.6 shadchin 593: linenum = 0;
1.10 nicm 594: } else {
1.6 shadchin 595: int add_one = 0;
596:
1.10 nicm 597: if (how_search == OPT_ON) {
1.6 shadchin 598: /*
599: * Search does not include current screen.
600: */
601: if (search_type & SRCH_FORW)
602: linenum = BOTTOM_PLUS_ONE;
603: else
604: linenum = TOP;
1.10 nicm 605: } else if (how_search == OPT_ONPLUS &&
606: !(search_type & SRCH_AFTER_TARGET)) {
1.6 shadchin 607: /*
608: * Search includes all of displayed screen.
609: */
610: if (search_type & SRCH_FORW)
611: linenum = TOP;
612: else
613: linenum = BOTTOM_PLUS_ONE;
1.10 nicm 614: } else {
1.6 shadchin 615: /*
1.10 nicm 616: * Search includes the part of current screen beyond
617: * the jump target.
618: * It starts at the jump target (if searching
619: * backwards), or at the jump target plus one
620: * (if forwards).
1.6 shadchin 621: */
622: linenum = jump_sline;
1.10 nicm 623: if (search_type & SRCH_FORW)
624: add_one = 1;
1.5 millert 625: }
1.6 shadchin 626: linenum = adjsline(linenum);
627: pos = position(linenum);
628: if (add_one)
1.10 nicm 629: pos = forw_raw_line(pos, NULL, NULL);
1.6 shadchin 630: }
631:
632: /*
633: * If the line is empty, look around for a plausible starting place.
634: */
1.10 nicm 635: if (search_type & SRCH_FORW) {
636: while (pos == -1) {
637: if (++linenum >= sc_height)
638: break;
639: pos = position(linenum);
640: }
641: } else {
642: while (pos == -1) {
643: if (--linenum < 0)
644: break;
645: pos = position(linenum);
646: }
1.1 etheisen 647: }
648: return (pos);
649: }
650:
651: /*
652: * Search a subset of the file, specified by start/end position.
653: */
1.10 nicm 654: static int
655: search_range(off_t pos, off_t endpos, int search_type, int matches,
656: int maxlines, off_t *plinepos, off_t *pendpos)
1.1 etheisen 657: {
658: char *line;
1.6 shadchin 659: char *cline;
660: int line_len;
1.16 mmcc 661: off_t linenum;
1.1 etheisen 662: char *sp, *ep;
663: int line_match;
1.5 millert 664: int cvt_ops;
1.6 shadchin 665: int cvt_len;
666: int *chpos;
1.10 nicm 667: off_t linepos, oldpos;
1.1 etheisen 668:
669: linenum = find_linenum(pos);
670: oldpos = pos;
1.10 nicm 671: for (;;) {
1.1 etheisen 672: /*
673: * Get lines until we find a matching one or until
1.10 nicm 674: * we hit end-of-file (or beginning-of-file if we're
1.1 etheisen 675: * going backwards), or until we hit the end position.
676: */
1.10 nicm 677: if (ABORT_SIGS()) {
1.1 etheisen 678: /*
679: * A signal aborts the search.
680: */
681: return (-1);
682: }
683:
1.10 nicm 684: if ((endpos != -1 && pos >= endpos) ||
685: maxlines == 0) {
1.1 etheisen 686: /*
687: * Reached end position without a match.
688: */
689: if (pendpos != NULL)
690: *pendpos = pos;
1.5 millert 691: return (matches);
1.1 etheisen 692: }
1.5 millert 693: if (maxlines > 0)
694: maxlines--;
1.1 etheisen 695:
1.10 nicm 696: if (search_type & SRCH_FORW) {
1.1 etheisen 697: /*
1.10 nicm 698: * Read the next line, and save the
1.1 etheisen 699: * starting position of that line in linepos.
700: */
701: linepos = pos;
1.6 shadchin 702: pos = forw_raw_line(pos, &line, &line_len);
1.1 etheisen 703: if (linenum != 0)
704: linenum++;
1.10 nicm 705: } else {
1.1 etheisen 706: /*
707: * Read the previous line and save the
708: * starting position of that line in linepos.
709: */
1.6 shadchin 710: pos = back_raw_line(pos, &line, &line_len);
1.1 etheisen 711: linepos = pos;
712: if (linenum != 0)
713: linenum--;
714: }
715:
1.10 nicm 716: if (pos == -1) {
1.1 etheisen 717: /*
718: * Reached EOF/BOF without a match.
719: */
720: if (pendpos != NULL)
1.5 millert 721: *pendpos = oldpos;
722: return (matches);
1.1 etheisen 723: }
724:
725: /*
726: * If we're using line numbers, we might as well
727: * remember the information we have now (the position
728: * and line number of the current line).
729: * Don't do it for every line because it slows down
730: * the search. Remember the line number only if
731: * we're "far" from the last place we remembered it.
732: */
1.6 shadchin 733: if (linenums && abs((int)(pos - oldpos)) > 2048)
1.1 etheisen 734: add_lnum(linenum, pos);
1.5 millert 735: oldpos = pos;
1.1 etheisen 736:
1.6 shadchin 737: if (is_filtered(linepos))
738: continue;
739:
1.1 etheisen 740: /*
741: * If it's a caseless search, convert the line to lowercase.
742: * If we're doing backspace processing, delete backspaces.
743: */
1.5 millert 744: cvt_ops = get_cvt_ops();
1.10 nicm 745: cvt_len = cvt_length(line_len);
746: cline = ecalloc(1, cvt_len);
1.6 shadchin 747: chpos = cvt_alloc_chpos(cvt_len);
748: cvt_text(cline, line, chpos, &line_len, cvt_ops);
749:
750: /*
751: * Check to see if the line matches the filter pattern.
752: * If so, add an entry to the filter list.
753: */
1.10 nicm 754: if ((search_type & SRCH_FIND_ALL) &&
755: prev_pattern(&filter_info)) {
756: int line_filter =
757: match_pattern(info_compiled(&filter_info),
758: filter_info.text, cline, line_len, &sp, &ep, 0,
759: filter_info.search_type);
760: if (line_filter) {
761: struct hilite *hl =
762: ecalloc(1, sizeof (struct hilite));
1.6 shadchin 763: hl->hl_startpos = linepos;
764: hl->hl_endpos = pos;
765: add_hilite(&filter_anchor, hl);
766: }
767: }
1.1 etheisen 768:
769: /*
770: * Test the next line to see if we have a match.
771: * We are successful if we either want a match and got one,
772: * or if we want a non-match and got one.
773: */
1.10 nicm 774: if (prev_pattern(&search_info)) {
775: line_match = match_pattern(info_compiled(&search_info),
776: search_info.text, cline, line_len, &sp, &ep, 0,
777: search_type);
778: if (line_match) {
1.1 etheisen 779: /*
1.6 shadchin 780: * Got a match.
1.1 etheisen 781: */
1.10 nicm 782: if (search_type & SRCH_FIND_ALL) {
1.6 shadchin 783: /*
1.10 nicm 784: * We are supposed to find all matches
785: * in the range.
786: * Just add the matches in this line
787: * to the hilite list and keep
788: * searching.
1.6 shadchin 789: */
1.10 nicm 790: hilite_line(linepos, cline, line_len,
791: chpos, sp, ep);
792: } else if (--matches <= 0) {
1.6 shadchin 793: /*
1.10 nicm 794: * Found the one match we're looking
795: * for. Return it.
1.6 shadchin 796: */
1.10 nicm 797: if (hilite_search == OPT_ON) {
1.6 shadchin 798: /*
1.10 nicm 799: * Clear the hilite list and
800: * add only
1.6 shadchin 801: * the matches in this one line.
802: */
803: clr_hilite();
1.10 nicm 804: hilite_line(linepos, cline,
805: line_len, chpos, sp, ep);
1.6 shadchin 806: }
807: free(cline);
808: free(chpos);
809: if (plinepos != NULL)
810: *plinepos = linepos;
811: return (0);
812: }
1.1 etheisen 813: }
814: }
1.6 shadchin 815: free(cline);
816: free(chpos);
1.1 etheisen 817: }
818: }
819:
820: /*
1.6 shadchin 821: * search for a pattern in history. If found, compile that pattern.
822: */
1.10 nicm 823: static int
824: hist_pattern(int search_type)
1.6 shadchin 825: {
826: char *pattern;
827:
828: set_mlist(ml_search, 0);
829: pattern = cmd_lastpattern();
830: if (pattern == NULL)
831: return (0);
832:
833: if (set_pattern(&search_info, pattern, search_type) < 0)
834: return (0);
835:
836: if (hilite_search == OPT_ONPLUS && !hide_hilite)
837: hilite_screen();
838:
839: return (1);
840: }
841:
842: /*
1.10 nicm 843: * Search for the n-th occurrence of a specified pattern,
1.1 etheisen 844: * either forward or backward.
845: * Return the number of matches not yet found in this file
846: * (that is, n minus the number of matches found).
847: * Return -1 if the search should be aborted.
1.10 nicm 848: * Caller may continue the search in another file
1.1 etheisen 849: * if less than n matches are found in this file.
850: */
1.10 nicm 851: int
852: search(int search_type, char *pattern, int n)
1.1 etheisen 853: {
1.10 nicm 854: off_t pos;
1.1 etheisen 855:
1.10 nicm 856: if (pattern == NULL || *pattern == '\0') {
1.1 etheisen 857: /*
858: * A null pattern means use the previously compiled pattern.
859: */
1.6 shadchin 860: search_type |= SRCH_AFTER_TARGET;
1.10 nicm 861: if (!prev_pattern(&search_info) && !hist_pattern(search_type)) {
1.13 deraadt 862: error("No previous regular expression", NULL);
1.1 etheisen 863: return (-1);
864: }
1.10 nicm 865: if ((search_type & SRCH_NO_REGEX) !=
866: (search_info.search_type & SRCH_NO_REGEX)) {
1.13 deraadt 867: error("Please re-enter search pattern", NULL);
1.10 nicm 868: return (-1);
1.5 millert 869: }
1.10 nicm 870: if (hilite_search == OPT_ON) {
1.1 etheisen 871: /*
872: * Erase the highlights currently on screen.
873: * If the search fails, we'll redisplay them later.
874: */
875: repaint_hilite(0);
876: }
1.10 nicm 877: if (hilite_search == OPT_ONPLUS && hide_hilite) {
1.1 etheisen 878: /*
879: * Highlight any matches currently on screen,
880: * before we actually start the search.
881: */
882: hide_hilite = 0;
883: hilite_screen();
884: }
885: hide_hilite = 0;
1.10 nicm 886: } else {
1.1 etheisen 887: /*
888: * Compile the pattern.
889: */
1.6 shadchin 890: if (set_pattern(&search_info, pattern, search_type) < 0)
1.1 etheisen 891: return (-1);
1.10 nicm 892: if (hilite_search) {
1.1 etheisen 893: /*
894: * Erase the highlights currently on screen.
895: * Also permanently delete them from the hilite list.
896: */
897: repaint_hilite(0);
898: hide_hilite = 0;
899: clr_hilite();
900: }
1.10 nicm 901: if (hilite_search == OPT_ONPLUS) {
1.1 etheisen 902: /*
903: * Highlight any matches currently on screen,
904: * before we actually start the search.
905: */
906: hilite_screen();
907: }
908: }
909:
910: /*
911: * Figure out where to start the search.
912: */
913: pos = search_pos(search_type);
1.10 nicm 914: if (pos == -1) {
1.1 etheisen 915: /*
916: * Can't find anyplace to start searching from.
917: */
918: if (search_type & SRCH_PAST_EOF)
919: return (n);
1.5 millert 920: /* repaint(); -- why was this here? */
1.13 deraadt 921: error("Nothing to search", NULL);
1.1 etheisen 922: return (-1);
923: }
924:
1.10 nicm 925: n = search_range(pos, -1, search_type, n, -1, &pos, NULL);
926: if (n != 0) {
1.1 etheisen 927: /*
928: * Search was unsuccessful.
929: */
930: if (hilite_search == OPT_ON && n > 0)
931: /*
932: * Redisplay old hilites.
933: */
934: repaint_hilite(1);
935: return (n);
936: }
937:
1.10 nicm 938: if (!(search_type & SRCH_NO_MOVE)) {
1.5 millert 939: /*
940: * Go to the matching line.
941: */
942: jump_loc(pos, jump_sline);
943: }
1.1 etheisen 944:
945: if (hilite_search == OPT_ON)
946: /*
947: * Display new hilites in the matching line.
948: */
949: repaint_hilite(1);
950: return (0);
951: }
952:
1.5 millert 953:
1.1 etheisen 954: /*
955: * Prepare hilites in a given range of the file.
956: *
957: * The pair (prep_startpos,prep_endpos) delimits a contiguous region
1.5 millert 958: * of the file that has been "prepared"; that is, scanned for matches for
1.1 etheisen 959: * the current search pattern, and hilites have been created for such matches.
1.10 nicm 960: * If prep_startpos == -1, the prep region is empty.
961: * If prep_endpos == -1, the prep region extends to EOF.
1.1 etheisen 962: * prep_hilite asks that the range (spos,epos) be covered by the prep region.
963: */
1.10 nicm 964: void
965: prep_hilite(off_t spos, off_t epos, int maxlines)
966: {
967: off_t nprep_startpos = prep_startpos;
968: off_t nprep_endpos = prep_endpos;
969: off_t new_epos;
970: off_t max_epos;
1.5 millert 971: int result;
972: int i;
1.6 shadchin 973:
1.1 etheisen 974: /*
975: * Search beyond where we're asked to search, so the prep region covers
976: * more than we need. Do one big search instead of a bunch of small ones.
977: */
978: #define SEARCH_MORE (3*size_linebuf)
979:
1.6 shadchin 980: if (!prev_pattern(&search_info) && !is_filtering())
1.1 etheisen 981: return;
1.5 millert 982:
983: /*
984: * If we're limited to a max number of lines, figure out the
985: * file position we should stop at.
986: */
1.10 nicm 987: if (maxlines < 0) {
988: max_epos = -1;
989: } else {
1.5 millert 990: max_epos = spos;
1.10 nicm 991: for (i = 0; i < maxlines; i++)
992: max_epos = forw_raw_line(max_epos, NULL, NULL);
1.5 millert 993: }
994:
1.1 etheisen 995: /*
996: * Find two ranges:
997: * The range that we need to search (spos,epos); and the range that
998: * the "prep" region will then cover (nprep_startpos,nprep_endpos).
999: */
1000:
1.10 nicm 1001: if (prep_startpos == -1 ||
1002: (epos != -1 && epos < prep_startpos) ||
1003: spos > prep_endpos) {
1.1 etheisen 1004: /*
1005: * New range is not contiguous with old prep region.
1006: * Discard the old prep region and start a new one.
1007: */
1008: clr_hilite();
1.6 shadchin 1009: clr_filter();
1.10 nicm 1010: if (epos != -1)
1.1 etheisen 1011: epos += SEARCH_MORE;
1012: nprep_startpos = spos;
1.10 nicm 1013: } else {
1.1 etheisen 1014: /*
1015: * New range partially or completely overlaps old prep region.
1016: */
1.10 nicm 1017: if (epos != -1) {
1018: if (epos > prep_endpos) {
1019: /*
1020: * New range ends after old prep region.
1021: * Extend prep region to end at end of new
1022: * range.
1023: */
1024: epos += SEARCH_MORE;
1025:
1026: } else {
1027: /*
1028: * New range ends within old prep region.
1029: * Truncate search to end at start of old prep
1030: * region.
1031: */
1032: epos = prep_startpos;
1033: }
1.1 etheisen 1034: }
1035:
1.10 nicm 1036: if (spos < prep_startpos) {
1.1 etheisen 1037: /*
1038: * New range starts before old prep region.
1.10 nicm 1039: * Extend old prep region backwards to start at
1.1 etheisen 1040: * start of new range.
1041: */
1042: if (spos < SEARCH_MORE)
1043: spos = 0;
1044: else
1045: spos -= SEARCH_MORE;
1046: nprep_startpos = spos;
1.10 nicm 1047: } else { /* (spos >= prep_startpos) */
1.1 etheisen 1048: /*
1049: * New range starts within or after old prep region.
1.5 millert 1050: * Trim search to start at end of old prep region.
1.1 etheisen 1051: */
1.5 millert 1052: spos = prep_endpos;
1.1 etheisen 1053: }
1054: }
1055:
1.10 nicm 1056: if (epos != -1 && max_epos != -1 &&
1.5 millert 1057: epos > max_epos)
1058: /*
1059: * Don't go past the max position we're allowed.
1060: */
1061: epos = max_epos;
1062:
1.10 nicm 1063: if (epos == -1 || epos > spos) {
1.6 shadchin 1064: int search_type = SRCH_FORW | SRCH_FIND_ALL;
1065: search_type |= (search_info.search_type & SRCH_NO_REGEX);
1066: result = search_range(spos, epos, search_type, 0,
1.10 nicm 1067: maxlines, NULL, &new_epos);
1.5 millert 1068: if (result < 0)
1069: return;
1.10 nicm 1070: if (prep_endpos == -1 || new_epos > prep_endpos)
1.5 millert 1071: nprep_endpos = new_epos;
1.1 etheisen 1072: }
1073: prep_startpos = nprep_startpos;
1074: prep_endpos = nprep_endpos;
1075: }
1076:
1077: /*
1.6 shadchin 1078: * Set the pattern to be used for line filtering.
1079: */
1.10 nicm 1080: void
1081: set_filter_pattern(char *pattern, int search_type)
1.6 shadchin 1082: {
1083: clr_filter();
1084: if (pattern == NULL || *pattern == '\0')
1085: clear_pattern(&filter_info);
1086: else
1.10 nicm 1087: (void) set_pattern(&filter_info, pattern, search_type);
1.6 shadchin 1088: screen_trashed = 1;
1089: }
1090:
1091: /*
1092: * Is there a line filter in effect?
1.1 etheisen 1093: */
1.10 nicm 1094: int
1095: is_filtering(void)
1.6 shadchin 1096: {
1097: if (ch_getflags() & CH_HELPFILE)
1098: return (0);
1.10 nicm 1099: return (prev_pattern(&filter_info));
1.1 etheisen 1100: }