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