Annotation of src/usr.bin/less/search.c, Revision 1.14
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:
16: #include "less.h"
1.6 shadchin 17: #include "pattern.h"
1.1 etheisen 18: #include "position.h"
1.6 shadchin 19: #include "charset.h"
1.1 etheisen 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: {
124: if (info->text != NULL)
125: free(info->text);
126: info->text = NULL;
127: uncompile_pattern(&info->compiled);
128: }
1.1 etheisen 129:
1.6 shadchin 130: /*
131: * Initialize saved pattern to nothing.
132: */
1.10 nicm 133: static void
134: init_pattern(struct pattern_info *info)
1.6 shadchin 135: {
1.10 nicm 136: info->compiled = NULL;
1.6 shadchin 137: info->text = NULL;
138: info->search_type = 0;
139: }
140:
141: /*
142: * Initialize search variables.
143: */
1.10 nicm 144: void
145: init_search(void)
1.6 shadchin 146: {
147: init_pattern(&search_info);
148: init_pattern(&filter_info);
1.1 etheisen 149: }
150:
151: /*
1.6 shadchin 152: * Determine which text conversions to perform before pattern matching.
1.5 millert 153: */
1.10 nicm 154: static int
155: get_cvt_ops(void)
1.5 millert 156: {
157: int ops = 0;
1.10 nicm 158: if (is_caseless || bs_mode == BS_SPECIAL) {
159: if (is_caseless)
1.5 millert 160: ops |= CVT_TO_LC;
161: if (bs_mode == BS_SPECIAL)
162: ops |= CVT_BS;
163: if (bs_mode != BS_CONTROL)
164: ops |= CVT_CRLF;
1.10 nicm 165: } else if (bs_mode != BS_CONTROL) {
1.5 millert 166: ops |= CVT_CRLF;
167: }
168: if (ctldisp == OPT_ONPLUS)
169: ops |= CVT_ANSI;
170: return (ops);
171: }
172:
173: /*
1.1 etheisen 174: * Is there a previous (remembered) search pattern?
175: */
1.10 nicm 176: static int
177: prev_pattern(struct pattern_info *info)
1.1 etheisen 178: {
1.8 shadchin 179: if ((info->search_type & SRCH_NO_REGEX) == 0)
1.14 ! tedu 180: return (info->compiled != NULL);
1.8 shadchin 181: return (info->text != NULL);
1.1 etheisen 182: }
183:
184: /*
185: * Repaint the hilites currently displayed on the screen.
186: * Repaint each line which contains highlighted text.
187: * If on==0, force all hilites off.
188: */
1.10 nicm 189: void
190: repaint_hilite(int on)
1.1 etheisen 191: {
192: int slinenum;
1.10 nicm 193: off_t pos;
1.1 etheisen 194: int save_hide_hilite;
1.5 millert 195:
196: if (squished)
197: repaint();
1.1 etheisen 198:
199: save_hide_hilite = hide_hilite;
1.10 nicm 200: if (!on) {
1.1 etheisen 201: if (hide_hilite)
202: return;
203: hide_hilite = 1;
204: }
205:
1.10 nicm 206: if (!can_goto_line) {
1.1 etheisen 207: repaint();
208: hide_hilite = save_hide_hilite;
209: return;
210: }
211:
1.10 nicm 212: for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) {
1.1 etheisen 213: pos = position(slinenum);
1.10 nicm 214: if (pos == -1)
1.1 etheisen 215: continue;
1.6 shadchin 216: (void) forw_line(pos);
217: goto_line(slinenum);
218: put_line();
1.1 etheisen 219: }
1.6 shadchin 220: lower_left();
1.1 etheisen 221: hide_hilite = save_hide_hilite;
222: }
1.5 millert 223:
224: /*
225: * Clear the attn hilite.
226: */
1.10 nicm 227: void
228: clear_attn(void)
1.5 millert 229: {
230: int slinenum;
1.10 nicm 231: off_t old_start_attnpos;
232: off_t old_end_attnpos;
233: off_t pos;
234: off_t epos;
1.6 shadchin 235: int moved = 0;
1.5 millert 236:
1.10 nicm 237: if (start_attnpos == -1)
1.5 millert 238: return;
239: old_start_attnpos = start_attnpos;
240: old_end_attnpos = end_attnpos;
1.10 nicm 241: start_attnpos = end_attnpos = -1;
1.5 millert 242:
1.10 nicm 243: if (!can_goto_line) {
1.5 millert 244: repaint();
245: return;
246: }
247: if (squished)
248: repaint();
249:
1.10 nicm 250: for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) {
1.5 millert 251: pos = position(slinenum);
1.10 nicm 252: if (pos == -1)
1.5 millert 253: continue;
254: epos = position(slinenum+1);
255: if (pos < old_end_attnpos &&
1.10 nicm 256: (epos == -1 || epos > old_start_attnpos)) {
1.5 millert 257: (void) forw_line(pos);
258: goto_line(slinenum);
259: put_line();
1.6 shadchin 260: moved = 1;
1.5 millert 261: }
262: }
1.6 shadchin 263: if (moved)
264: lower_left();
1.5 millert 265: }
1.1 etheisen 266:
267: /*
268: * Hide search string highlighting.
269: */
1.10 nicm 270: void
271: undo_search(void)
1.1 etheisen 272: {
1.10 nicm 273: if (!prev_pattern(&search_info)) {
1.13 deraadt 274: error("No previous regular expression", NULL);
1.1 etheisen 275: return;
276: }
277: hide_hilite = !hide_hilite;
278: repaint_hilite(1);
279: }
280:
281: /*
1.6 shadchin 282: * Clear the hilite list.
1.1 etheisen 283: */
1.10 nicm 284: static void
285: clr_hlist(struct hilite *anchor)
1.1 etheisen 286: {
1.6 shadchin 287: struct hilite *hl;
288: struct hilite *nexthl;
289:
1.10 nicm 290: for (hl = anchor->hl_first; hl != NULL; hl = nexthl) {
1.6 shadchin 291: nexthl = hl->hl_next;
1.12 mmcc 292: free(hl);
1.1 etheisen 293: }
1.6 shadchin 294: anchor->hl_first = NULL;
1.10 nicm 295: prep_startpos = prep_endpos = -1;
1.6 shadchin 296: }
1.5 millert 297:
1.10 nicm 298: void
299: clr_hilite(void)
1.6 shadchin 300: {
301: clr_hlist(&hilite_anchor);
1.1 etheisen 302: }
303:
1.10 nicm 304: static void
305: clr_filter(void)
1.1 etheisen 306: {
1.6 shadchin 307: clr_hlist(&filter_anchor);
1.1 etheisen 308: }
309:
310: /*
1.6 shadchin 311: * Should any characters in a specified range be highlighted?
1.1 etheisen 312: */
313: static int
1.10 nicm 314: is_hilited_range(off_t pos, off_t epos)
1.6 shadchin 315: {
316: struct hilite *hl;
317:
318: /*
319: * Look at each highlight and see if any part of it falls in the range.
320: */
1.10 nicm 321: for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) {
1.6 shadchin 322: if (hl->hl_endpos > pos &&
1.10 nicm 323: (epos == -1 || epos > hl->hl_startpos))
1.6 shadchin 324: return (1);
1.5 millert 325: }
1.6 shadchin 326: return (0);
1.1 etheisen 327: }
328:
1.10 nicm 329: /*
1.6 shadchin 330: * Is a line "filtered" -- that is, should it be hidden?
1.1 etheisen 331: */
1.10 nicm 332: int
333: is_filtered(off_t pos)
1.1 etheisen 334: {
335: struct hilite *hl;
336:
1.6 shadchin 337: if (ch_getflags() & CH_HELPFILE)
338: return (0);
339:
340: /*
341: * Look at each filter and see if the start position
342: * equals the start position of the line.
343: */
1.10 nicm 344: for (hl = filter_anchor.hl_first; hl != NULL; hl = hl->hl_next) {
1.6 shadchin 345: if (hl->hl_startpos == pos)
346: return (1);
1.1 etheisen 347: }
1.6 shadchin 348: return (0);
1.1 etheisen 349: }
350:
351: /*
352: * Should any characters in a specified range be highlighted?
353: * If nohide is nonzero, don't consider hide_hilite.
354: */
1.10 nicm 355: int
356: is_hilited(off_t pos, off_t epos, int nohide, int *p_matches)
1.1 etheisen 357: {
1.6 shadchin 358: int match;
359:
360: if (p_matches != NULL)
361: *p_matches = 0;
1.1 etheisen 362:
1.5 millert 363: if (!status_col &&
1.10 nicm 364: start_attnpos != -1 &&
1.5 millert 365: pos < end_attnpos &&
1.10 nicm 366: (epos == -1 || epos > start_attnpos))
1.5 millert 367: /*
368: * The attn line overlaps this range.
369: */
370: return (1);
371:
1.6 shadchin 372: match = is_hilited_range(pos, epos);
373: if (!match)
374: return (0);
375:
376: if (p_matches != NULL)
377: /*
378: * Report matches, even if we're hiding highlights.
379: */
380: *p_matches = 1;
381:
1.1 etheisen 382: if (hilite_search == 0)
383: /*
384: * Not doing highlighting.
385: */
386: return (0);
387:
388: if (!nohide && hide_hilite)
389: /*
390: * Highlighting is hidden.
391: */
392: return (0);
393:
1.6 shadchin 394: return (1);
1.1 etheisen 395: }
396:
397: /*
398: * Add a new hilite to a hilite list.
399: */
1.10 nicm 400: static void
401: add_hilite(struct hilite *anchor, struct hilite *hl)
1.1 etheisen 402: {
403: struct hilite *ihl;
404:
405: /*
406: * Hilites are sorted in the list; find where new one belongs.
407: * Insert new one after ihl.
408: */
409: for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next)
410: {
411: if (ihl->hl_next->hl_startpos > hl->hl_startpos)
412: break;
413: }
414:
415: /*
416: * Truncate hilite so it doesn't overlap any existing ones
417: * above and below it.
418: */
419: if (ihl != anchor)
420: hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
421: if (ihl->hl_next != NULL)
1.10 nicm 422: hl->hl_endpos = MINPOS(hl->hl_endpos,
423: ihl->hl_next->hl_startpos);
424: if (hl->hl_startpos >= hl->hl_endpos) {
1.1 etheisen 425: /*
426: * Hilite was truncated out of existence.
427: */
428: free(hl);
429: return;
430: }
431: hl->hl_next = ihl->hl_next;
432: ihl->hl_next = hl;
433: }
434:
435: /*
1.8 shadchin 436: * Hilight every character in a range of displayed characters.
437: */
1.10 nicm 438: static void
439: create_hilites(off_t linepos, int start_index, int end_index, int *chpos)
1.8 shadchin 440: {
441: struct hilite *hl;
442: int i;
443:
444: /* Start the first hilite. */
1.10 nicm 445: hl = ecalloc(1, sizeof (struct hilite));
1.8 shadchin 446: hl->hl_startpos = linepos + chpos[start_index];
447:
448: /*
449: * Step through the displayed chars.
450: * If the source position (before cvt) of the char is one more
451: * than the source pos of the previous char (the usual case),
452: * just increase the size of the current hilite by one.
453: * Otherwise (there are backspaces or something involved),
454: * finish the current hilite and start a new one.
455: */
1.10 nicm 456: for (i = start_index+1; i <= end_index; i++) {
457: if (chpos[i] != chpos[i-1] + 1 || i == end_index) {
1.8 shadchin 458: hl->hl_endpos = linepos + chpos[i-1] + 1;
459: add_hilite(&hilite_anchor, hl);
460: /* Start new hilite unless this is the last char. */
1.10 nicm 461: if (i < end_index) {
462: hl = ecalloc(1, sizeof (struct hilite));
1.8 shadchin 463: hl->hl_startpos = linepos + chpos[i];
464: }
465: }
466: }
467: }
468:
469: /*
1.10 nicm 470: * Make a hilite for each string in a physical line which matches
1.1 etheisen 471: * the current pattern.
472: * sp,ep delimit the first match already found.
473: */
1.10 nicm 474: static void
475: hilite_line(off_t linepos, char *line, int line_len, int *chpos,
476: char *sp, char *ep)
1.1 etheisen 477: {
478: char *searchp;
1.6 shadchin 479: char *line_end = line + line_len;
1.1 etheisen 480:
481: if (sp == NULL || ep == NULL)
482: return;
483: /*
484: * sp and ep delimit the first match in the line.
485: * Mark the corresponding file positions, then
486: * look for further matches and mark them.
487: * {{ This technique, of calling match_pattern on subsequent
488: * substrings of the line, may mark more than is correct
1.5 millert 489: * if the pattern starts with "^". This bug is fixed
490: * for those regex functions that accept a notbol parameter
1.6 shadchin 491: * (currently POSIX, PCRE and V8-with-regexec2). }}
1.1 etheisen 492: */
493: searchp = line;
494: do {
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.5 millert 662: LINENUM 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: }