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