/commands/less/less/search.c
C | 1452 lines | 973 code | 93 blank | 386 comment | 290 complexity | 6e9d23aecb3326f8b2c75f9f31d38477 MD5 | raw file
Possible License(s): AGPL-1.0, BSD-3-Clause
- /* $NetBSD: search.c,v 1.10 2006/10/26 01:33:08 mrg Exp $ */
- /*
- * Copyright (C) 1984-2005 Mark Nudelman
- *
- * You may distribute under the terms of either the GNU General Public
- * License or the Less License, as specified in the README file.
- *
- * For more information about less, or for information on how to
- * contact the author, see the README file.
- */
- /*
- * Routines to search a file for a pattern.
- */
- #include "less.h"
- #include "position.h"
- #define MINPOS(a,b) (((a) < (b)) ? (a) : (b))
- #define MAXPOS(a,b) (((a) > (b)) ? (a) : (b))
- #if HAVE_POSIX_REGCOMP
- #include <regex.h>
- #ifdef REG_EXTENDED
- #define REGCOMP_FLAG (more_mode ? 0 : REG_EXTENDED)
- #else
- #define REGCOMP_FLAG 0
- #endif
- #endif
- #if HAVE_PCRE
- #include <pcre.h>
- #endif
- #if HAVE_RE_COMP
- char *re_comp();
- int re_exec();
- #endif
- #if HAVE_REGCMP
- char *regcmp();
- char *regex();
- extern char *__loc1;
- #endif
- #if HAVE_V8_REGCOMP
- #include "regexp.h"
- #endif
- static void cvt_text __P((char *, char *, int));
- static int is_ucase __P((char *));
- static int prev_pattern __P((void));
- static int compile_pattern __P((char *, int));
- static void uncompile_pattern __P((void));
- static int match_pattern __P((char *, char **, char **, int));
- struct hilite;
- static void add_hilite __P((struct hilite *, struct hilite *));
- static void adj_hilite __P((struct hilite *, POSITION, int));
- static void hilite_line __P((POSITION, char *, char *, char *, int));
- static void hilite_screen __P((void));
- static POSITION search_pos __P((int));
- static int search_range __P((POSITION, POSITION, int, int, int, POSITION *, POSITION *));
- static int match __P((char *, char *, char **, char **));
- extern int sigs;
- extern int how_search;
- extern int caseless;
- extern int linenums;
- extern int sc_height;
- extern int jump_sline;
- extern int bs_mode;
- extern int ctldisp;
- extern int status_col;
- extern int more_mode;
- extern POSITION start_attnpos;
- extern POSITION end_attnpos;
- #if HILITE_SEARCH
- extern int hilite_search;
- extern int screen_trashed;
- extern int size_linebuf;
- extern int squished;
- extern int can_goto_line;
- static int hide_hilite;
- static POSITION prep_startpos;
- static POSITION prep_endpos;
- struct hilite
- {
- struct hilite *hl_next;
- POSITION hl_startpos;
- POSITION hl_endpos;
- };
- static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION };
- #define hl_first hl_next
- #endif
- /*
- * These are the static variables that represent the "remembered"
- * search pattern.
- */
- #if HAVE_POSIX_REGCOMP
- static regex_t *regpattern = NULL;
- #endif
- #if HAVE_PCRE
- pcre *regpattern = NULL;
- #endif
- #if HAVE_RE_COMP
- int re_pattern = 0;
- #endif
- #if HAVE_REGCMP
- static char *cpattern = NULL;
- #endif
- #if HAVE_V8_REGCOMP
- static struct regexp *regpattern = NULL;
- #endif
- static int is_caseless;
- static int is_ucase_pattern;
- static int last_search_type;
- static char *last_pattern = NULL;
- /*
- * Convert text. Perform one or more of these transformations:
- */
- #define CVT_TO_LC 01 /* Convert upper-case to lower-case */
- #define CVT_BS 02 /* Do backspace processing */
- #define CVT_CRLF 04 /* Remove CR after LF */
- #define CVT_ANSI 010 /* Remove ANSI escape sequences */
- static void
- cvt_text(odst, osrc, ops)
- char *odst;
- char *osrc;
- int ops;
- {
- register char *dst;
- register char *src;
- for (src = osrc, dst = odst; *src != '\0'; src++)
- {
- if ((ops & CVT_TO_LC) && IS_UPPER(*src))
- /* Convert uppercase to lowercase. */
- *dst++ = TO_LOWER(*src);
- else if ((ops & CVT_BS) && *src == '\b' && dst > odst)
- /* Delete BS and preceding char. */
- dst--;
- else if ((ops & CVT_ANSI) && *src == ESC)
- {
- /* Skip to end of ANSI escape sequence. */
- while (src[1] != '\0')
- if (!is_ansi_middle(*++src))
- break;
- } else
- /* Just copy. */
- *dst++ = *src;
- }
- if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r')
- dst--;
- *dst = '\0';
- }
- /*
- * Determine which conversions to perform.
- */
- static int
- get_cvt_ops()
- {
- int ops = 0;
- if (is_caseless || bs_mode == BS_SPECIAL)
- {
- if (is_caseless)
- ops |= CVT_TO_LC;
- if (bs_mode == BS_SPECIAL)
- ops |= CVT_BS;
- if (bs_mode != BS_CONTROL)
- ops |= CVT_CRLF;
- } else if (bs_mode != BS_CONTROL)
- {
- ops |= CVT_CRLF;
- }
- if (ctldisp == OPT_ONPLUS)
- ops |= CVT_ANSI;
- return (ops);
- }
- /*
- * Are there any uppercase letters in this string?
- */
- static int
- is_ucase(s)
- char *s;
- {
- register char *p;
- for (p = s; *p != '\0'; p++)
- if (IS_UPPER(*p))
- return (1);
- return (0);
- }
- /*
- * Is there a previous (remembered) search pattern?
- */
- static int
- prev_pattern()
- {
- if (last_search_type & SRCH_NO_REGEX)
- return (last_pattern != NULL);
- #if HAVE_POSIX_REGCOMP
- return (regpattern != NULL);
- #endif
- #if HAVE_PCRE
- return (regpattern != NULL);
- #endif
- #if HAVE_RE_COMP
- return (re_pattern != 0);
- #endif
- #if HAVE_REGCMP
- return (cpattern != NULL);
- #endif
- #if HAVE_V8_REGCOMP
- return (regpattern != NULL);
- #endif
- #if NO_REGEX
- return (last_pattern != NULL);
- #endif
- }
- #if HILITE_SEARCH
- /*
- * Repaint the hilites currently displayed on the screen.
- * Repaint each line which contains highlighted text.
- * If on==0, force all hilites off.
- */
- public void
- repaint_hilite(on)
- int on;
- {
- int slinenum;
- POSITION pos;
- POSITION epos;
- int save_hide_hilite;
- if (squished)
- repaint();
- save_hide_hilite = hide_hilite;
- if (!on)
- {
- if (hide_hilite)
- return;
- hide_hilite = 1;
- }
- if (!can_goto_line)
- {
- repaint();
- hide_hilite = save_hide_hilite;
- return;
- }
- for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++)
- {
- pos = position(slinenum);
- if (pos == NULL_POSITION)
- continue;
- epos = position(slinenum+1);
- #if 0
- /*
- * If any character in the line is highlighted,
- * repaint the line.
- *
- * {{ This doesn't work -- if line is drawn with highlights
- * which should be erased (e.g. toggle -i with status column),
- * we must redraw the line even if it has no highlights.
- * For now, just repaint every line. }}
- */
- if (is_hilited(pos, epos, 1, NULL))
- #endif
- {
- (void) forw_line(pos);
- goto_line(slinenum);
- put_line();
- }
- }
- hide_hilite = save_hide_hilite;
- }
- /*
- * Clear the attn hilite.
- */
- public void
- clear_attn()
- {
- int slinenum;
- POSITION old_start_attnpos;
- POSITION old_end_attnpos;
- POSITION pos;
- POSITION epos;
- if (start_attnpos == NULL_POSITION)
- return;
- old_start_attnpos = start_attnpos;
- old_end_attnpos = end_attnpos;
- start_attnpos = end_attnpos = NULL_POSITION;
- if (!can_goto_line)
- {
- repaint();
- return;
- }
- if (squished)
- repaint();
- for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++)
- {
- pos = position(slinenum);
- if (pos == NULL_POSITION)
- continue;
- epos = position(slinenum+1);
- if (pos < old_end_attnpos &&
- (epos == NULL_POSITION || epos > old_start_attnpos))
- {
- (void) forw_line(pos);
- goto_line(slinenum);
- put_line();
- }
- }
- }
- #endif
- /*
- * Hide search string highlighting.
- */
- public void
- undo_search()
- {
- if (!prev_pattern())
- {
- error("No previous regular expression", NULL_PARG);
- return;
- }
- #if HILITE_SEARCH
- hide_hilite = !hide_hilite;
- repaint_hilite(1);
- #endif
- }
- /*
- * Compile a search pattern, for future use by match_pattern.
- */
- static int
- compile_pattern(pattern, search_type)
- char *pattern;
- int search_type;
- {
- if ((search_type & SRCH_NO_REGEX) == 0)
- {
- #if HAVE_POSIX_REGCOMP
- regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t));
- if (regcomp(s, pattern, REGCOMP_FLAG))
- {
- free(s);
- error("Invalid pattern", NULL_PARG);
- return (-1);
- }
- if (regpattern != NULL)
- regfree(regpattern);
- regpattern = s;
- #endif
- #if HAVE_PCRE
- pcre *comp;
- const char *errstring;
- int erroffset;
- PARG parg;
- comp = pcre_compile(pattern, 0,
- &errstring, &erroffset, NULL);
- if (comp == NULL)
- {
- parg.p_string = (char *) errstring;
- error("%s", &parg);
- return (-1);
- }
- regpattern = comp;
- #endif
- #if HAVE_RE_COMP
- PARG parg;
- if ((parg.p_string = re_comp(pattern)) != NULL)
- {
- error("%s", &parg);
- return (-1);
- }
- re_pattern = 1;
- #endif
- #if HAVE_REGCMP
- char *s;
- if ((s = regcmp(pattern, 0)) == NULL)
- {
- error("Invalid pattern", NULL_PARG);
- return (-1);
- }
- if (cpattern != NULL)
- free(cpattern);
- cpattern = s;
- #endif
- #if HAVE_V8_REGCOMP
- struct regexp *s;
- if ((s = regcomp(pattern)) == NULL)
- {
- /*
- * regcomp has already printed an error message
- * via regerror().
- */
- return (-1);
- }
- if (regpattern != NULL)
- free(regpattern);
- regpattern = s;
- #endif
- }
- if (last_pattern != NULL)
- free(last_pattern);
- last_pattern = (char *) calloc(1, strlen(pattern)+1);
- if (last_pattern != NULL)
- strcpy(last_pattern, pattern);
- last_search_type = search_type;
- return (0);
- }
- /*
- * Forget that we have a compiled pattern.
- */
- static void
- uncompile_pattern()
- {
- #if HAVE_POSIX_REGCOMP
- if (regpattern != NULL)
- regfree(regpattern);
- regpattern = NULL;
- #endif
- #if HAVE_PCRE
- if (regpattern != NULL)
- pcre_free(regpattern);
- regpattern = NULL;
- #endif
- #if HAVE_RE_COMP
- re_pattern = 0;
- #endif
- #if HAVE_REGCMP
- if (cpattern != NULL)
- free(cpattern);
- cpattern = NULL;
- #endif
- #if HAVE_V8_REGCOMP
- if (regpattern != NULL)
- free(regpattern);
- regpattern = NULL;
- #endif
- last_pattern = NULL;
- }
- /*
- * Perform a pattern match with the previously compiled pattern.
- * Set sp and ep to the start and end of the matched string.
- */
- static int
- match_pattern(line, sp, ep, notbol)
- char *line;
- char **sp;
- char **ep;
- int notbol;
- {
- int matched;
- if (last_search_type & SRCH_NO_REGEX)
- return (match(last_pattern, line, sp, ep));
- #if HAVE_POSIX_REGCOMP
- {
- regmatch_t rm;
- int flags = (notbol) ? REG_NOTBOL : 0;
- matched = !regexec(regpattern, line, 1, &rm, flags);
- if (!matched)
- return (0);
- #ifndef __WATCOMC__
- *sp = line + rm.rm_so;
- *ep = line + rm.rm_eo;
- #else
- *sp = rm.rm_sp;
- *ep = rm.rm_ep;
- #endif
- }
- #endif
- #if HAVE_PCRE
- {
- int flags = (notbol) ? PCRE_NOTBOL : 0;
- int ovector[3];
- matched = pcre_exec(regpattern, NULL, line, strlen(line),
- 0, flags, ovector, 3) >= 0;
- if (!matched)
- return (0);
- *sp = line + ovector[0];
- *ep = line + ovector[1];
- }
- #endif
- #if HAVE_RE_COMP
- matched = (re_exec(line) == 1);
- /*
- * re_exec doesn't seem to provide a way to get the matched string.
- */
- *sp = *ep = NULL;
- #endif
- #if HAVE_REGCMP
- *ep = regex(cpattern, line);
- matched = (*ep != NULL);
- if (!matched)
- return (0);
- *sp = __loc1;
- #endif
- #if HAVE_V8_REGCOMP
- #if HAVE_REGEXEC2
- matched = regexec2(regpattern, line, notbol);
- #else
- matched = regexec(regpattern, line);
- #endif
- if (!matched)
- return (0);
- *sp = regpattern->startp[0];
- *ep = regpattern->endp[0];
- #endif
- #if NO_REGEX
- matched = match(last_pattern, line, sp, ep);
- #endif
- return (matched);
- }
- #if HILITE_SEARCH
- /*
- * Clear the hilite list.
- */
- public void
- clr_hilite()
- {
- struct hilite *hl;
- struct hilite *nexthl;
- for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl)
- {
- nexthl = hl->hl_next;
- free((void*)hl);
- }
- hilite_anchor.hl_first = NULL;
- prep_startpos = prep_endpos = NULL_POSITION;
- }
- /*
- * Should any characters in a specified range be highlighted?
- */
- static int
- is_hilited_range(pos, epos)
- POSITION pos;
- POSITION epos;
- {
- struct hilite *hl;
- /*
- * Look at each highlight and see if any part of it falls in the range.
- */
- for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next)
- {
- if (hl->hl_endpos > pos &&
- (epos == NULL_POSITION || epos > hl->hl_startpos))
- return (1);
- }
- return (0);
- }
- /*
- * Should any characters in a specified range be highlighted?
- * If nohide is nonzero, don't consider hide_hilite.
- */
- public int
- is_hilited(pos, epos, nohide, p_matches)
- POSITION pos;
- POSITION epos;
- int nohide;
- int *p_matches;
- {
- int match;
- if (p_matches != NULL)
- *p_matches = 0;
- if (!status_col &&
- start_attnpos != NULL_POSITION &&
- pos < end_attnpos &&
- (epos == NULL_POSITION || epos > start_attnpos))
- /*
- * The attn line overlaps this range.
- */
- return (1);
- match = is_hilited_range(pos, epos);
- if (!match)
- return (0);
- if (p_matches != NULL)
- /*
- * Report matches, even if we're hiding highlights.
- */
- *p_matches = 1;
- if (hilite_search == 0)
- /*
- * Not doing highlighting.
- */
- return (0);
- if (!nohide && hide_hilite)
- /*
- * Highlighting is hidden.
- */
- return (0);
- return (1);
- }
- /*
- * Add a new hilite to a hilite list.
- */
- static void
- add_hilite(anchor, hl)
- struct hilite *anchor;
- struct hilite *hl;
- {
- struct hilite *ihl;
- /*
- * Hilites are sorted in the list; find where new one belongs.
- * Insert new one after ihl.
- */
- for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next)
- {
- if (ihl->hl_next->hl_startpos > hl->hl_startpos)
- break;
- }
- /*
- * Truncate hilite so it doesn't overlap any existing ones
- * above and below it.
- */
- if (ihl != anchor)
- hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos);
- if (ihl->hl_next != NULL)
- hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos);
- if (hl->hl_startpos >= hl->hl_endpos)
- {
- /*
- * Hilite was truncated out of existence.
- */
- free(hl);
- return;
- }
- hl->hl_next = ihl->hl_next;
- ihl->hl_next = hl;
- }
- static void
- adj_hilite_ansi(cvt_ops, line, npos)
- int cvt_ops;
- char **line;
- POSITION *npos;
- {
- if (cvt_ops & CVT_ANSI)
- while (**line == ESC)
- {
- /*
- * Found an ESC. The file position moves
- * forward past the entire ANSI escape sequence.
- */
- (*line)++;
- (*npos)++;
- while (**line != '\0')
- {
- (*npos)++;
- if (!is_ansi_middle(*(*line)++))
- break;
- }
- }
- }
- /*
- * Adjust hl_startpos & hl_endpos to account for backspace processing.
- */
- static void
- adj_hilite(anchor, linepos, cvt_ops)
- struct hilite *anchor;
- POSITION linepos;
- int cvt_ops;
- {
- char *line;
- struct hilite *hl;
- int checkstart;
- POSITION opos;
- POSITION npos;
- /*
- * The line was already scanned and hilites were added (in hilite_line).
- * But it was assumed that each char position in the line
- * correponds to one char position in the file.
- * This may not be true if there are backspaces in the line.
- * Get the raw line again. Look at each character.
- */
- (void) forw_raw_line(linepos, &line);
- opos = npos = linepos;
- hl = anchor->hl_first;
- checkstart = TRUE;
- while (hl != NULL)
- {
- /*
- * See if we need to adjust the current hl_startpos or
- * hl_endpos. After adjusting startpos[i], move to endpos[i].
- * After adjusting endpos[i], move to startpos[i+1].
- * The hilite list must be sorted thus:
- * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc.
- */
- if (checkstart && hl->hl_startpos == opos)
- {
- hl->hl_startpos = npos;
- checkstart = FALSE;
- continue; /* {{ not really necessary }} */
- } else if (!checkstart && hl->hl_endpos == opos)
- {
- hl->hl_endpos = npos;
- checkstart = TRUE;
- hl = hl->hl_next;
- continue; /* {{ necessary }} */
- }
- if (*line == '\0')
- break;
- adj_hilite_ansi(cvt_ops, &line, &npos);
- opos++;
- npos++;
- line++;
- if (cvt_ops & CVT_BS)
- {
- while (*line == '\b')
- {
- npos++;
- line++;
- adj_hilite_ansi(cvt_ops, &line, &npos);
- if (*line == '\0')
- {
- --npos;
- --line;
- break;
- }
- /*
- * Found a backspace. The file position moves
- * forward by 2 relative to the processed line
- * which was searched in hilite_line.
- */
- npos++;
- line++;
- }
- }
- }
- }
- /*
- * Make a hilite for each string in a physical line which matches
- * the current pattern.
- * sp,ep delimit the first match already found.
- */
- static void
- hilite_line(linepos, line, sp, ep, cvt_ops)
- POSITION linepos;
- char *line;
- char *sp;
- char *ep;
- int cvt_ops;
- {
- char *searchp;
- struct hilite *hl;
- struct hilite hilites;
- if (sp == NULL || ep == NULL)
- return;
- /*
- * sp and ep delimit the first match in the line.
- * Mark the corresponding file positions, then
- * look for further matches and mark them.
- * {{ This technique, of calling match_pattern on subsequent
- * substrings of the line, may mark more than is correct
- * if the pattern starts with "^". This bug is fixed
- * for those regex functions that accept a notbol parameter
- * (currently POSIX and V8-with-regexec2). }}
- */
- searchp = line;
- /*
- * Put the hilites into a temporary list until they're adjusted.
- */
- hilites.hl_first = NULL;
- do {
- if (ep > sp)
- {
- /*
- * Assume that each char position in the "line"
- * buffer corresponds to one char position in the file.
- * This is not quite true; we need to adjust later.
- */
- hl = (struct hilite *) ecalloc(1, sizeof(struct hilite));
- hl->hl_startpos = linepos + (sp-line);
- hl->hl_endpos = linepos + (ep-line);
- add_hilite(&hilites, hl);
- }
- /*
- * If we matched more than zero characters,
- * move to the first char after the string we matched.
- * If we matched zero, just move to the next char.
- */
- if (ep > searchp)
- searchp = ep;
- else if (*searchp != '\0')
- searchp++;
- else /* end of line */
- break;
- } while (match_pattern(searchp, &sp, &ep, 1));
- /*
- * If there were backspaces in the original line, they
- * were removed, and hl_startpos/hl_endpos are not correct.
- * {{ This is very ugly. }}
- */
- adj_hilite(&hilites, linepos, cvt_ops);
- /*
- * Now put the hilites into the real list.
- */
- while ((hl = hilites.hl_next) != NULL)
- {
- hilites.hl_next = hl->hl_next;
- add_hilite(&hilite_anchor, hl);
- }
- }
- #endif
- /*
- * Change the caseless-ness of searches.
- * Updates the internal search state to reflect a change in the -i flag.
- */
- public void
- chg_caseless()
- {
- if (!is_ucase_pattern)
- /*
- * Pattern did not have uppercase.
- * Just set the search caselessness to the global caselessness.
- */
- is_caseless = caseless;
- else
- /*
- * Pattern did have uppercase.
- * Discard the pattern; we can't change search caselessness now.
- */
- uncompile_pattern();
- }
- #if HILITE_SEARCH
- /*
- * Find matching text which is currently on screen and highlight it.
- */
- static void
- hilite_screen()
- {
- struct scrpos scrpos;
- get_scrpos(&scrpos);
- if (scrpos.pos == NULL_POSITION)
- return;
- prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1);
- repaint_hilite(1);
- }
- /*
- * Change highlighting parameters.
- */
- public void
- chg_hilite()
- {
- /*
- * Erase any highlights currently on screen.
- */
- clr_hilite();
- hide_hilite = 0;
- if (hilite_search == OPT_ONPLUS)
- /*
- * Display highlights.
- */
- hilite_screen();
- }
- #endif
- /*
- * Figure out where to start a search.
- */
- static POSITION
- search_pos(search_type)
- int search_type;
- {
- POSITION pos;
- int linenum;
- if (empty_screen())
- {
- /*
- * Start at the beginning (or end) of the file.
- * The empty_screen() case is mainly for
- * command line initiated searches;
- * for example, "+/xyz" on the command line.
- * Also for multi-file (SRCH_PAST_EOF) searches.
- */
- if (search_type & SRCH_FORW)
- {
- return (ch_zero());
- } else
- {
- pos = ch_length();
- if (pos == NULL_POSITION)
- {
- (void) ch_end_seek();
- pos = ch_length();
- }
- return (pos);
- }
- }
- if (how_search)
- {
- /*
- * Search does not include current screen.
- */
- if (search_type & SRCH_FORW)
- linenum = BOTTOM_PLUS_ONE;
- else
- linenum = TOP;
- pos = position(linenum);
- } else
- {
- /*
- * Search includes current screen.
- * It starts at the jump target (if searching backwards),
- * or at the jump target plus one (if forwards).
- */
- linenum = adjsline(jump_sline);
- pos = position(linenum);
- if (search_type & SRCH_FORW)
- {
- pos = forw_raw_line(pos, (char **)NULL);
- while (pos == NULL_POSITION)
- {
- if (++linenum >= sc_height)
- break;
- pos = position(linenum);
- }
- } else
- {
- while (pos == NULL_POSITION)
- {
- if (--linenum < 0)
- break;
- pos = position(linenum);
- }
- }
- }
- return (pos);
- }
- /*
- * Search a subset of the file, specified by start/end position.
- */
- static int
- search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos)
- POSITION pos;
- POSITION endpos;
- int search_type;
- int matches;
- int maxlines;
- POSITION *plinepos;
- POSITION *pendpos;
- {
- char *line;
- LINENUM linenum;
- char *sp = NULL, *ep = NULL; /* XXX: GCC */
- int line_match;
- int cvt_ops;
- POSITION linepos, oldpos;
- linenum = find_linenum(pos);
- oldpos = pos;
- for (;;)
- {
- /*
- * Get lines until we find a matching one or until
- * we hit end-of-file (or beginning-of-file if we're
- * going backwards), or until we hit the end position.
- */
- if (ABORT_SIGS())
- {
- /*
- * A signal aborts the search.
- */
- return (-1);
- }
- if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
- {
- /*
- * Reached end position without a match.
- */
- if (pendpos != NULL)
- *pendpos = pos;
- return (matches);
- }
- if (maxlines > 0)
- maxlines--;
- if (search_type & SRCH_FORW)
- {
- /*
- * Read the next line, and save the
- * starting position of that line in linepos.
- */
- linepos = pos;
- pos = forw_raw_line(pos, &line);
- if (linenum != 0)
- linenum++;
- } else
- {
- /*
- * Read the previous line and save the
- * starting position of that line in linepos.
- */
- pos = back_raw_line(pos, &line);
- linepos = pos;
- if (linenum != 0)
- linenum--;
- }
- if (pos == NULL_POSITION)
- {
- /*
- * Reached EOF/BOF without a match.
- */
- if (pendpos != NULL)
- *pendpos = oldpos;
- return (matches);
- }
- /*
- * If we're using line numbers, we might as well
- * remember the information we have now (the position
- * and line number of the current line).
- * Don't do it for every line because it slows down
- * the search. Remember the line number only if
- * we're "far" from the last place we remembered it.
- */
- if (linenums && abs((int)(pos - oldpos)) > 1024)
- add_lnum(linenum, pos);
- oldpos = pos;
- /*
- * If it's a caseless search, convert the line to lowercase.
- * If we're doing backspace processing, delete backspaces.
- */
- cvt_ops = get_cvt_ops();
- cvt_text(line, line, cvt_ops);
- /*
- * Test the next line to see if we have a match.
- * We are successful if we either want a match and got one,
- * or if we want a non-match and got one.
- */
- line_match = match_pattern(line, &sp, &ep, 0);
- line_match = (!(search_type & SRCH_NO_MATCH) && line_match) ||
- ((search_type & SRCH_NO_MATCH) && !line_match);
- if (!line_match)
- continue;
- /*
- * Got a match.
- */
- if (search_type & SRCH_FIND_ALL)
- {
- #if HILITE_SEARCH
- /*
- * We are supposed to find all matches in the range.
- * Just add the matches in this line to the
- * hilite list and keep searching.
- */
- if (line_match)
- hilite_line(linepos, line, sp, ep, cvt_ops);
- #endif
- } else if (--matches <= 0)
- {
- /*
- * Found the one match we're looking for.
- * Return it.
- */
- #if HILITE_SEARCH
- if (hilite_search == OPT_ON)
- {
- /*
- * Clear the hilite list and add only
- * the matches in this one line.
- */
- clr_hilite();
- if (line_match)
- hilite_line(linepos, line, sp, ep, cvt_ops);
- }
- #endif
- if (plinepos != NULL)
- *plinepos = linepos;
- return (0);
- }
- }
- }
- /*
- * Search for the n-th occurrence of a specified pattern,
- * either forward or backward.
- * Return the number of matches not yet found in this file
- * (that is, n minus the number of matches found).
- * Return -1 if the search should be aborted.
- * Caller may continue the search in another file
- * if less than n matches are found in this file.
- */
- public int
- search(search_type, pattern, n)
- int search_type;
- char *pattern;
- int n;
- {
- POSITION pos;
- int ucase;
- if (pattern == NULL || *pattern == '\0')
- {
- /*
- * A null pattern means use the previously compiled pattern.
- */
- if (!prev_pattern())
- {
- error("No previous regular expression", NULL_PARG);
- return (-1);
- }
- if ((search_type & SRCH_NO_REGEX) !=
- (last_search_type & SRCH_NO_REGEX))
- {
- error("Please re-enter search pattern", NULL_PARG);
- return -1;
- }
- #if HILITE_SEARCH
- if (hilite_search == OPT_ON)
- {
- /*
- * Erase the highlights currently on screen.
- * If the search fails, we'll redisplay them later.
- */
- repaint_hilite(0);
- }
- if (hilite_search == OPT_ONPLUS && hide_hilite)
- {
- /*
- * Highlight any matches currently on screen,
- * before we actually start the search.
- */
- hide_hilite = 0;
- hilite_screen();
- }
- hide_hilite = 0;
- #endif
- } else
- {
- /*
- * Compile the pattern.
- */
- ucase = is_ucase(pattern);
- if (caseless == OPT_ONPLUS)
- cvt_text(pattern, pattern, CVT_TO_LC);
- if (compile_pattern(pattern, search_type) < 0)
- return (-1);
- /*
- * Ignore case if -I is set OR
- * -i is set AND the pattern is all lowercase.
- */
- is_ucase_pattern = ucase;
- if (is_ucase_pattern && caseless != OPT_ONPLUS)
- is_caseless = 0;
- else
- is_caseless = caseless;
- #if HILITE_SEARCH
- if (hilite_search)
- {
- /*
- * Erase the highlights currently on screen.
- * Also permanently delete them from the hilite list.
- */
- repaint_hilite(0);
- hide_hilite = 0;
- clr_hilite();
- }
- if (hilite_search == OPT_ONPLUS)
- {
- /*
- * Highlight any matches currently on screen,
- * before we actually start the search.
- */
- hilite_screen();
- }
- #endif
- }
- /*
- * Figure out where to start the search.
- */
- pos = search_pos(search_type);
- if (pos == NULL_POSITION)
- {
- /*
- * Can't find anyplace to start searching from.
- */
- if (search_type & SRCH_PAST_EOF)
- return (n);
- /* repaint(); -- why was this here? */
- error("Nothing to search", NULL_PARG);
- return (-1);
- }
- n = search_range(pos, NULL_POSITION, search_type, n, -1,
- &pos, (POSITION*)NULL);
- if (n != 0)
- {
- /*
- * Search was unsuccessful.
- */
- #if HILITE_SEARCH
- if (hilite_search == OPT_ON && n > 0)
- /*
- * Redisplay old hilites.
- */
- repaint_hilite(1);
- #endif
- return (n);
- }
- if (!(search_type & SRCH_NO_MOVE))
- {
- /*
- * Go to the matching line.
- */
- jump_loc(pos, jump_sline);
- }
- #if HILITE_SEARCH
- if (hilite_search == OPT_ON)
- /*
- * Display new hilites in the matching line.
- */
- repaint_hilite(1);
- #endif
- return (0);
- }
- #if HILITE_SEARCH
- /*
- * Prepare hilites in a given range of the file.
- *
- * The pair (prep_startpos,prep_endpos) delimits a contiguous region
- * of the file that has been "prepared"; that is, scanned for matches for
- * the current search pattern, and hilites have been created for such matches.
- * If prep_startpos == NULL_POSITION, the prep region is empty.
- * If prep_endpos == NULL_POSITION, the prep region extends to EOF.
- * prep_hilite asks that the range (spos,epos) be covered by the prep region.
- */
- public void
- prep_hilite(spos, epos, maxlines)
- POSITION spos;
- POSITION epos;
- int maxlines;
- {
- POSITION nprep_startpos = prep_startpos;
- POSITION nprep_endpos = prep_endpos;
- POSITION new_epos;
- POSITION max_epos;
- int result;
- int i;
- /*
- * Search beyond where we're asked to search, so the prep region covers
- * more than we need. Do one big search instead of a bunch of small ones.
- */
- #define SEARCH_MORE (3*size_linebuf)
- if (!prev_pattern())
- return;
- /*
- * If we're limited to a max number of lines, figure out the
- * file position we should stop at.
- */
- if (maxlines < 0)
- max_epos = NULL_POSITION;
- else
- {
- max_epos = spos;
- for (i = 0; i < maxlines; i++)
- max_epos = forw_raw_line(max_epos, (char **)NULL);
- }
- /*
- * Find two ranges:
- * The range that we need to search (spos,epos); and the range that
- * the "prep" region will then cover (nprep_startpos,nprep_endpos).
- */
- if (prep_startpos == NULL_POSITION ||
- (epos != NULL_POSITION && epos < prep_startpos) ||
- spos > prep_endpos)
- {
- /*
- * New range is not contiguous with old prep region.
- * Discard the old prep region and start a new one.
- */
- clr_hilite();
- if (epos != NULL_POSITION)
- epos += SEARCH_MORE;
- nprep_startpos = spos;
- } else
- {
- /*
- * New range partially or completely overlaps old prep region.
- */
- if (epos == NULL_POSITION)
- {
- /*
- * New range goes to end of file.
- */
- ;
- } else if (epos > prep_endpos)
- {
- /*
- * New range ends after old prep region.
- * Extend prep region to end at end of new range.
- */
- epos += SEARCH_MORE;
- } else /* (epos <= prep_endpos) */
- {
- /*
- * New range ends within old prep region.
- * Truncate search to end at start of old prep region.
- */
- epos = prep_startpos;
- }
- if (spos < prep_startpos)
- {
- /*
- * New range starts before old prep region.
- * Extend old prep region backwards to start at
- * start of new range.
- */
- if (spos < SEARCH_MORE)
- spos = 0;
- else
- spos -= SEARCH_MORE;
- nprep_startpos = spos;
- } else /* (spos >= prep_startpos) */
- {
- /*
- * New range starts within or after old prep region.
- * Trim search to start at end of old prep region.
- */
- spos = prep_endpos;
- }
- }
- if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
- epos > max_epos)
- /*
- * Don't go past the max position we're allowed.
- */
- epos = max_epos;
- if (epos == NULL_POSITION || epos > spos)
- {
- result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0,
- maxlines, (POSITION*)NULL, &new_epos);
- if (result < 0)
- return;
- if (prep_endpos == NULL_POSITION || new_epos > prep_endpos)
- nprep_endpos = new_epos;
- }
- prep_startpos = nprep_startpos;
- prep_endpos = nprep_endpos;
- }
- #endif
- /*
- * Simple pattern matching function.
- * It supports no metacharacters like *, etc.
- */
- static int
- match(pattern, buf, pfound, pend)
- char *pattern, *buf;
- char **pfound, **pend;
- {
- register char *pp, *lp;
- for ( ; *buf != '\0'; buf++)
- {
- for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++)
- if (*pp == '\0' || *lp == '\0')
- break;
- if (*pp == '\0')
- {
- if (pfound != NULL)
- *pfound = buf;
- if (pend != NULL)
- *pend = lp;
- return (1);
- }
- }
- return (0);
- }
- #if HAVE_V8_REGCOMP
- /*
- * This function is called by the V8 regcomp to report
- * errors in regular expressions.
- */
- void
- regerror(s)
- char *s;
- {
- PARG parg;
- parg.p_string = s;
- error("%s", &parg);
- }
- #endif