version 1.1.1.2, 2003/04/13 18:21:21 |
version 1.1.1.3, 2011/09/16 17:47:08 |
|
|
/* |
/* |
* Copyright (C) 1984-2002 Mark Nudelman |
* Copyright (C) 1984-2011 Mark Nudelman |
* |
* |
* You may distribute under the terms of either the GNU General Public |
* You may distribute under the terms of either the GNU General Public |
* License or the Less License, as specified in the README file. |
* License or the Less License, as specified in the README file. |
|
|
*/ |
*/ |
|
|
#include "less.h" |
#include "less.h" |
|
#include "pattern.h" |
#include "position.h" |
#include "position.h" |
|
#include "charset.h" |
|
|
#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) |
#define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) |
#define MAXPOS(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 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 int match(); |
|
|
|
extern int sigs; |
extern int sigs; |
extern int how_search; |
extern int how_search; |
extern int caseless; |
extern int caseless; |
|
|
extern int bs_mode; |
extern int bs_mode; |
extern int ctldisp; |
extern int ctldisp; |
extern int status_col; |
extern int status_col; |
|
extern void * constant ml_search; |
extern POSITION start_attnpos; |
extern POSITION start_attnpos; |
extern POSITION end_attnpos; |
extern POSITION end_attnpos; |
|
extern int utf_mode; |
|
extern int screen_trashed; |
#if HILITE_SEARCH |
#if HILITE_SEARCH |
extern int hilite_search; |
extern int hilite_search; |
extern int screen_trashed; |
|
extern int size_linebuf; |
extern int size_linebuf; |
extern int squished; |
extern int squished; |
extern int can_goto_line; |
extern int can_goto_line; |
static int hide_hilite; |
static int hide_hilite; |
static POSITION prep_startpos; |
static POSITION prep_startpos; |
static POSITION prep_endpos; |
static POSITION prep_endpos; |
|
static int is_caseless; |
|
static int is_ucase_pattern; |
|
|
struct hilite |
struct hilite |
{ |
{ |
|
|
POSITION hl_endpos; |
POSITION hl_endpos; |
}; |
}; |
static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; |
static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; |
|
static struct hilite filter_anchor = { NULL, NULL_POSITION, NULL_POSITION }; |
#define hl_first hl_next |
#define hl_first hl_next |
#endif |
#endif |
|
|
/* |
/* |
* These are the static variables that represent the "remembered" |
* These are the static variables that represent the "remembered" |
* search pattern. |
* search pattern and filter pattern. |
*/ |
*/ |
#if HAVE_POSIX_REGCOMP |
struct pattern_info { |
static regex_t *regpattern = NULL; |
DEFINE_PATTERN(compiled); |
#endif |
char* text; |
#if HAVE_PCRE |
int search_type; |
pcre *regpattern = NULL; |
}; |
#endif |
|
#if HAVE_RE_COMP |
static struct pattern_info search_info; |
int re_pattern = 0; |
static struct pattern_info filter_info; |
#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; |
* Are there any uppercase letters in this string? |
static int last_search_type; |
*/ |
static char *last_pattern = NULL; |
static int |
|
is_ucase(str) |
|
char *str; |
|
{ |
|
char *str_end = str + strlen(str); |
|
LWCHAR ch; |
|
|
|
while (str < str_end) |
|
{ |
|
ch = step_char(&str, +1, str_end); |
|
if (IS_UPPER(ch)) |
|
return (1); |
|
} |
|
return (0); |
|
} |
|
|
/* |
/* |
* Convert text. Perform one or more of these transformations: |
* Compile and save a search pattern. |
*/ |
*/ |
#define CVT_TO_LC 01 /* Convert upper-case to lower-case */ |
static int |
#define CVT_BS 02 /* Do backspace processing */ |
set_pattern(info, pattern, search_type) |
#define CVT_CRLF 04 /* Remove CR after LF */ |
struct pattern_info *info; |
#define CVT_ANSI 010 /* Remove ANSI escape sequences */ |
char *pattern; |
|
int search_type; |
|
{ |
|
if (pattern == NULL) |
|
CLEAR_PATTERN(search_info.compiled); |
|
else if (compile_pattern(pattern, search_type, &info->compiled) < 0) |
|
return -1; |
|
/* Pattern compiled successfully; save the text too. */ |
|
if (info->text != NULL) |
|
free(info->text); |
|
info->text = NULL; |
|
if (pattern != NULL) |
|
{ |
|
info->text = (char *) ecalloc(1, strlen(pattern)+1); |
|
strcpy(info->text, pattern); |
|
} |
|
info->search_type = search_type; |
|
|
|
/* |
|
* Ignore case if -I is set OR |
|
* -i is set AND the pattern is all lowercase. |
|
*/ |
|
is_ucase_pattern = is_ucase(pattern); |
|
if (is_ucase_pattern && caseless != OPT_ONPLUS) |
|
is_caseless = 0; |
|
else |
|
is_caseless = caseless; |
|
return 0; |
|
} |
|
|
|
/* |
|
* Discard a saved pattern. |
|
*/ |
static void |
static void |
cvt_text(odst, osrc, ops) |
clear_pattern(info) |
char *odst; |
struct pattern_info *info; |
char *osrc; |
|
int ops; |
|
{ |
{ |
register char *dst; |
if (info->text != NULL) |
register char *src; |
free(info->text); |
|
info->text = NULL; |
|
uncompile_pattern(&info->compiled); |
|
} |
|
|
for (src = osrc, dst = odst; *src != '\0'; src++) |
/* |
{ |
* Initialize saved pattern to nothing. |
if ((ops & CVT_TO_LC) && isupper((unsigned char) *src)) |
*/ |
/* Convert uppercase to lowercase. */ |
static void |
*dst++ = tolower((unsigned char) *src); |
init_pattern(info) |
else if ((ops & CVT_BS) && *src == '\b' && dst > odst) |
struct pattern_info *info; |
/* Delete BS and preceding char. */ |
{ |
dst--; |
CLEAR_PATTERN(info->compiled); |
else if ((ops & CVT_ANSI) && *src == ESC) |
info->text = NULL; |
{ |
info->search_type = 0; |
/* Skip to end of ANSI escape sequence. */ |
|
while (src[1] != '\0') |
|
if (is_ansi_end(*++src)) |
|
break; |
|
} else |
|
/* Just copy. */ |
|
*dst++ = *src; |
|
} |
|
if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r') |
|
dst--; |
|
*dst = '\0'; |
|
} |
} |
|
|
/* |
/* |
* Determine which conversions to perform. |
* Initialize search variables. |
*/ |
*/ |
|
public void |
|
init_search() |
|
{ |
|
init_pattern(&search_info); |
|
init_pattern(&filter_info); |
|
} |
|
|
|
/* |
|
* Determine which text conversions to perform before pattern matching. |
|
*/ |
static int |
static int |
get_cvt_ops() |
get_cvt_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 (isupper((unsigned char) *p)) |
|
return (1); |
|
return (0); |
|
} |
|
|
|
/* |
|
* Is there a previous (remembered) search pattern? |
* Is there a previous (remembered) search pattern? |
*/ |
*/ |
static int |
static int |
prev_pattern() |
prev_pattern(info) |
|
struct pattern_info *info; |
{ |
{ |
if (last_search_type & SRCH_NO_REGEX) |
if (info->search_type & SRCH_NO_REGEX) |
return (last_pattern != NULL); |
return (info->text != NULL); |
#if HAVE_POSIX_REGCOMP |
return (!is_null_pattern(info->compiled)); |
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 |
#if HILITE_SEARCH |
|
|
if (pos == NULL_POSITION) |
if (pos == NULL_POSITION) |
continue; |
continue; |
epos = position(slinenum+1); |
epos = position(slinenum+1); |
/* |
(void) forw_line(pos); |
* If any character in the line is highlighted, |
goto_line(slinenum); |
* repaint the line. |
put_line(); |
*/ |
|
if (is_hilited(pos, epos, 1)) |
|
{ |
|
(void) forw_line(pos); |
|
goto_line(slinenum); |
|
put_line(); |
|
} |
|
} |
} |
|
lower_left(); |
hide_hilite = save_hide_hilite; |
hide_hilite = save_hide_hilite; |
} |
} |
|
|
|
|
POSITION old_end_attnpos; |
POSITION old_end_attnpos; |
POSITION pos; |
POSITION pos; |
POSITION epos; |
POSITION epos; |
|
int moved = 0; |
|
|
if (start_attnpos == NULL_POSITION) |
if (start_attnpos == NULL_POSITION) |
return; |
return; |
|
|
(void) forw_line(pos); |
(void) forw_line(pos); |
goto_line(slinenum); |
goto_line(slinenum); |
put_line(); |
put_line(); |
|
moved = 1; |
} |
} |
} |
} |
|
if (moved) |
|
lower_left(); |
} |
} |
#endif |
#endif |
|
|
|
|
public void |
public void |
undo_search() |
undo_search() |
{ |
{ |
if (!prev_pattern()) |
if (!prev_pattern(&search_info)) |
{ |
{ |
error("No previous regular expression", NULL_PARG); |
error("No previous regular expression", NULL_PARG); |
return; |
return; |
|
|
#endif |
#endif |
} |
} |
|
|
|
#if HILITE_SEARCH |
/* |
/* |
* Compile a search pattern, for future use by match_pattern. |
* Clear the hilite list. |
*/ |
*/ |
static int |
public void |
compile_pattern(pattern, search_type) |
clr_hlist(anchor) |
char *pattern; |
struct hilite *anchor; |
int search_type; |
|
{ |
{ |
if ((search_type & SRCH_NO_REGEX) == 0) |
struct hilite *hl; |
|
struct hilite *nexthl; |
|
|
|
for (hl = anchor->hl_first; hl != NULL; hl = nexthl) |
{ |
{ |
#if HAVE_POSIX_REGCOMP |
nexthl = hl->hl_next; |
regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t)); |
free((void*)hl); |
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 |
|
} |
} |
|
anchor->hl_first = NULL; |
|
prep_startpos = prep_endpos = NULL_POSITION; |
|
} |
|
|
if (last_pattern != NULL) |
public void |
free(last_pattern); |
clr_hilite() |
last_pattern = (char *) calloc(1, strlen(pattern)+1); |
{ |
if (last_pattern != NULL) |
clr_hlist(&hilite_anchor); |
strcpy(last_pattern, pattern); |
|
|
|
last_search_type = search_type; |
|
return (0); |
|
} |
} |
|
|
/* |
public void |
* Forget that we have a compiled pattern. |
clr_filter() |
*/ |
|
static void |
|
uncompile_pattern() |
|
{ |
{ |
#if HAVE_POSIX_REGCOMP |
clr_hlist(&filter_anchor); |
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. |
* Should any characters in a specified range be highlighted? |
* Set sp and ep to the start and end of the matched string. |
|
*/ |
*/ |
static int |
static int |
match_pattern(line, sp, ep, notbol) |
is_hilited_range(pos, epos) |
char *line; |
POSITION pos; |
char **sp; |
POSITION epos; |
char **ep; |
|
int notbol; |
|
{ |
{ |
int matched; |
struct hilite *hl; |
|
|
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. |
* Look at each highlight and see if any part of it falls in the range. |
*/ |
*/ |
*sp = *ep = NULL; |
for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) |
#endif |
{ |
#if HAVE_REGCMP |
if (hl->hl_endpos > pos && |
*ep = regex(cpattern, line); |
(epos == NULL_POSITION || epos > hl->hl_startpos)) |
matched = (*ep != NULL); |
return (1); |
if (!matched) |
} |
return (0); |
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 |
/* |
/* |
* Is a line "filtered" -- that is, should it be hidden? |
* Clear the hilite list. |
|
*/ |
*/ |
public void |
public int |
clr_hilite() |
is_filtered(pos) |
|
POSITION pos; |
{ |
{ |
struct hilite *hl; |
struct hilite *hl; |
struct hilite *nexthl; |
|
|
|
for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl) |
if (ch_getflags() & CH_HELPFILE) |
|
return (0); |
|
|
|
/* |
|
* Look at each filter and see if the start position |
|
* equals the start position of the line. |
|
*/ |
|
for (hl = filter_anchor.hl_first; hl != NULL; hl = hl->hl_next) |
{ |
{ |
nexthl = hl->hl_next; |
if (hl->hl_startpos == pos) |
free((void*)hl); |
return (1); |
} |
} |
hilite_anchor.hl_first = NULL; |
return (0); |
prep_startpos = prep_endpos = NULL_POSITION; |
|
} |
} |
|
|
/* |
/* |
|
|
* If nohide is nonzero, don't consider hide_hilite. |
* If nohide is nonzero, don't consider hide_hilite. |
*/ |
*/ |
public int |
public int |
is_hilited(pos, epos, nohide) |
is_hilited(pos, epos, nohide, p_matches) |
POSITION pos; |
POSITION pos; |
POSITION epos; |
POSITION epos; |
int nohide; |
int nohide; |
|
int *p_matches; |
{ |
{ |
struct hilite *hl; |
int match; |
|
|
|
if (p_matches != NULL) |
|
*p_matches = 0; |
|
|
if (!status_col && |
if (!status_col && |
start_attnpos != NULL_POSITION && |
start_attnpos != NULL_POSITION && |
pos < end_attnpos && |
pos < end_attnpos && |
|
|
*/ |
*/ |
return (1); |
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) |
if (hilite_search == 0) |
/* |
/* |
* Not doing highlighting. |
* Not doing highlighting. |
|
|
*/ |
*/ |
return (0); |
return (0); |
|
|
/* |
return (1); |
* 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); |
|
} |
} |
|
|
/* |
/* |
|
|
} |
} |
|
|
/* |
/* |
* 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; |
|
if (cvt_ops & CVT_ANSI) |
|
{ |
|
while (line[0] == ESC) |
|
{ |
|
/* |
|
* Found an ESC. The file position moves |
|
* forward past the entire ANSI escape sequence. |
|
*/ |
|
line++; |
|
npos++; |
|
while (*line != '\0') |
|
{ |
|
npos++; |
|
if (is_ansi_end(*line++)) |
|
break; |
|
} |
|
} |
|
} |
|
opos++; |
|
npos++; |
|
line++; |
|
if (cvt_ops & CVT_BS) |
|
{ |
|
while (line[0] == '\b' && line[1] != '\0') |
|
{ |
|
/* |
|
* Found a backspace. The file position moves |
|
* forward by 2 relative to the processed line |
|
* which was searched in hilite_line. |
|
*/ |
|
npos += 2; |
|
line += 2; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* Make a hilite for each string in a physical line which matches |
* Make a hilite for each string in a physical line which matches |
* the current pattern. |
* the current pattern. |
* sp,ep delimit the first match already found. |
* sp,ep delimit the first match already found. |
*/ |
*/ |
static void |
static void |
hilite_line(linepos, line, sp, ep, cvt_ops) |
hilite_line(linepos, line, line_len, chpos, sp, ep, cvt_ops) |
POSITION linepos; |
POSITION linepos; |
char *line; |
char *line; |
|
int line_len; |
|
int *chpos; |
char *sp; |
char *sp; |
char *ep; |
char *ep; |
int cvt_ops; |
int cvt_ops; |
{ |
{ |
char *searchp; |
char *searchp; |
|
char *line_end = line + line_len; |
struct hilite *hl; |
struct hilite *hl; |
struct hilite hilites; |
|
|
|
if (sp == NULL || ep == NULL) |
if (sp == NULL || ep == NULL) |
return; |
return; |
|
|
* substrings of the line, may mark more than is correct |
* substrings of the line, may mark more than is correct |
* if the pattern starts with "^". This bug is fixed |
* if the pattern starts with "^". This bug is fixed |
* for those regex functions that accept a notbol parameter |
* for those regex functions that accept a notbol parameter |
* (currently POSIX and V8-with-regexec2). }} |
* (currently POSIX, PCRE and V8-with-regexec2). }} |
*/ |
*/ |
searchp = line; |
searchp = line; |
/* |
|
* Put the hilites into a temporary list until they're adjusted. |
|
*/ |
|
hilites.hl_first = NULL; |
|
do { |
do { |
if (ep > sp) |
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 = (struct hilite *) ecalloc(1, sizeof(struct hilite)); |
hl->hl_startpos = linepos + (sp-line); |
hl->hl_startpos = linepos + chpos[sp-line]; |
hl->hl_endpos = linepos + (ep-line); |
hl->hl_endpos = linepos + chpos[ep-line]; |
add_hilite(&hilites, hl); |
add_hilite(&hilite_anchor, hl); |
} |
} |
/* |
/* |
* If we matched more than zero characters, |
* If we matched more than zero characters, |
|
|
*/ |
*/ |
if (ep > searchp) |
if (ep > searchp) |
searchp = ep; |
searchp = ep; |
else if (*searchp != '\0') |
else if (searchp != line_end) |
searchp++; |
searchp++; |
else /* end of line */ |
else /* end of line */ |
break; |
break; |
} while (match_pattern(searchp, &sp, &ep, 1)); |
} while (match_pattern(search_info.compiled, search_info.text, |
|
searchp, line_end - searchp, &sp, &ep, 1, search_info.search_type)); |
/* |
|
* 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 |
#endif |
|
|
|
|
* Pattern did have uppercase. |
* Pattern did have uppercase. |
* Discard the pattern; we can't change search caselessness now. |
* Discard the pattern; we can't change search caselessness now. |
*/ |
*/ |
uncompile_pattern(); |
clear_pattern(&search_info); |
} |
} |
|
|
#if HILITE_SEARCH |
#if HILITE_SEARCH |
|
|
*/ |
*/ |
if (search_type & SRCH_FORW) |
if (search_type & SRCH_FORW) |
{ |
{ |
return (ch_zero()); |
pos = ch_zero(); |
} else |
} else |
{ |
{ |
pos = ch_length(); |
pos = ch_length(); |
|
|
(void) ch_end_seek(); |
(void) ch_end_seek(); |
pos = ch_length(); |
pos = ch_length(); |
} |
} |
return (pos); |
|
} |
} |
} |
linenum = 0; |
if (how_search) |
} else |
{ |
{ |
/* |
int add_one = 0; |
* Search does not include current screen. |
|
*/ |
if (how_search == OPT_ON) |
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) |
* Search does not include current screen. |
{ |
*/ |
if (++linenum >= sc_height) |
if (search_type & SRCH_FORW) |
break; |
linenum = BOTTOM_PLUS_ONE; |
pos = position(linenum); |
else |
} |
linenum = TOP; |
|
} else if (how_search == OPT_ONPLUS && !(search_type & SRCH_AFTER_TARGET)) |
|
{ |
|
/* |
|
* Search includes all of displayed screen. |
|
*/ |
|
if (search_type & SRCH_FORW) |
|
linenum = TOP; |
|
else |
|
linenum = BOTTOM_PLUS_ONE; |
} else |
} else |
{ |
{ |
while (pos == NULL_POSITION) |
/* |
{ |
* Search includes the part of current screen beyond the jump target. |
if (--linenum < 0) |
* It starts at the jump target (if searching backwards), |
break; |
* or at the jump target plus one (if forwards). |
pos = position(linenum); |
*/ |
} |
linenum = jump_sline; |
|
if (search_type & SRCH_FORW) |
|
add_one = 1; |
} |
} |
|
linenum = adjsline(linenum); |
|
pos = position(linenum); |
|
if (add_one) |
|
pos = forw_raw_line(pos, (char **)NULL, (int *)NULL); |
} |
} |
|
|
|
/* |
|
* If the line is empty, look around for a plausible starting place. |
|
*/ |
|
if (search_type & SRCH_FORW) |
|
{ |
|
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); |
return (pos); |
} |
} |
|
|
|
|
POSITION *pendpos; |
POSITION *pendpos; |
{ |
{ |
char *line; |
char *line; |
|
char *cline; |
|
int line_len; |
LINENUM linenum; |
LINENUM linenum; |
char *sp, *ep; |
char *sp, *ep; |
int line_match; |
int line_match; |
int cvt_ops; |
int cvt_ops; |
|
int cvt_len; |
|
int *chpos; |
POSITION linepos, oldpos; |
POSITION linepos, oldpos; |
|
|
linenum = find_linenum(pos); |
linenum = find_linenum(pos); |
|
|
* starting position of that line in linepos. |
* starting position of that line in linepos. |
*/ |
*/ |
linepos = pos; |
linepos = pos; |
pos = forw_raw_line(pos, &line); |
pos = forw_raw_line(pos, &line, &line_len); |
if (linenum != 0) |
if (linenum != 0) |
linenum++; |
linenum++; |
} else |
} else |
|
|
* Read the previous line and save the |
* Read the previous line and save the |
* starting position of that line in linepos. |
* starting position of that line in linepos. |
*/ |
*/ |
pos = back_raw_line(pos, &line); |
pos = back_raw_line(pos, &line, &line_len); |
linepos = pos; |
linepos = pos; |
if (linenum != 0) |
if (linenum != 0) |
linenum--; |
linenum--; |
|
|
* the search. Remember the line number only if |
* the search. Remember the line number only if |
* we're "far" from the last place we remembered it. |
* we're "far" from the last place we remembered it. |
*/ |
*/ |
if (linenums && abs((int)(pos - oldpos)) > 1024) |
if (linenums && abs((int)(pos - oldpos)) > 2048) |
add_lnum(linenum, pos); |
add_lnum(linenum, pos); |
oldpos = pos; |
oldpos = pos; |
|
|
|
if (is_filtered(linepos)) |
|
continue; |
|
|
/* |
/* |
* If it's a caseless search, convert the line to lowercase. |
* If it's a caseless search, convert the line to lowercase. |
* If we're doing backspace processing, delete backspaces. |
* If we're doing backspace processing, delete backspaces. |
*/ |
*/ |
cvt_ops = get_cvt_ops(); |
cvt_ops = get_cvt_ops(); |
cvt_text(line, line, cvt_ops); |
cvt_len = cvt_length(line_len, cvt_ops); |
|
cline = (char *) ecalloc(1, cvt_len); |
|
chpos = cvt_alloc_chpos(cvt_len); |
|
cvt_text(cline, line, chpos, &line_len, cvt_ops); |
|
|
|
#if HILITE_SEARCH |
/* |
/* |
|
* Check to see if the line matches the filter pattern. |
|
* If so, add an entry to the filter list. |
|
*/ |
|
if ((search_type & SRCH_FIND_ALL) && prev_pattern(&filter_info)) { |
|
int line_filter = match_pattern(filter_info.compiled, filter_info.text, |
|
cline, line_len, &sp, &ep, 0, filter_info.search_type); |
|
if (line_filter) |
|
{ |
|
struct hilite *hl = (struct hilite *) |
|
ecalloc(1, sizeof(struct hilite)); |
|
hl->hl_startpos = linepos; |
|
hl->hl_endpos = pos; |
|
add_hilite(&filter_anchor, hl); |
|
} |
|
} |
|
#endif |
|
|
|
/* |
* Test the next line to see if we have a match. |
* Test the next line to see if we have a match. |
* We are successful if we either want a match and got one, |
* We are successful if we either want a match and got one, |
* or if we want a non-match and got one. |
* or if we want a non-match and got one. |
*/ |
*/ |
line_match = match_pattern(line, &sp, &ep, 0); |
if (prev_pattern(&search_info)) |
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 |
line_match = match_pattern(search_info.compiled, search_info.text, |
/* |
cline, line_len, &sp, &ep, 0, search_type); |
* 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) |
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 == 1) |
|
{ |
{ |
/* |
/* |
* Clear the hilite list and add only |
* Got a match. |
* the matches in this one line. |
|
*/ |
*/ |
clr_hilite(); |
if (search_type & SRCH_FIND_ALL) |
if (line_match) |
{ |
hilite_line(linepos, line, sp, ep, cvt_ops); |
#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. |
|
*/ |
|
hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); |
#endif |
#endif |
if (plinepos != NULL) |
} else if (--matches <= 0) |
*plinepos = linepos; |
{ |
return (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(); |
|
hilite_line(linepos, cline, line_len, chpos, sp, ep, cvt_ops); |
|
} |
|
#endif |
|
free(cline); |
|
free(chpos); |
|
if (plinepos != NULL) |
|
*plinepos = linepos; |
|
return (0); |
|
} |
|
} |
} |
} |
|
free(cline); |
|
free(chpos); |
} |
} |
} |
} |
|
|
/* |
/* |
|
* search for a pattern in history. If found, compile that pattern. |
|
*/ |
|
static int |
|
hist_pattern(search_type) |
|
int search_type; |
|
{ |
|
#if CMD_HISTORY |
|
char *pattern; |
|
|
|
set_mlist(ml_search, 0); |
|
pattern = cmd_lastpattern(); |
|
if (pattern == NULL) |
|
return (0); |
|
|
|
if (set_pattern(&search_info, pattern, search_type) < 0) |
|
return (0); |
|
|
|
#if HILITE_SEARCH |
|
if (hilite_search == OPT_ONPLUS && !hide_hilite) |
|
hilite_screen(); |
|
#endif |
|
|
|
return (1); |
|
#else /* CMD_HISTORY */ |
|
return (0); |
|
#endif /* CMD_HISTORY */ |
|
} |
|
|
|
/* |
* Search for the n-th occurrence of a specified pattern, |
* Search for the n-th occurrence of a specified pattern, |
* either forward or backward. |
* either forward or backward. |
* Return the number of matches not yet found in this file |
* Return the number of matches not yet found in this file |
|
|
int n; |
int n; |
{ |
{ |
POSITION pos; |
POSITION pos; |
int ucase; |
|
|
|
if (pattern == NULL || *pattern == '\0') |
if (pattern == NULL || *pattern == '\0') |
{ |
{ |
/* |
/* |
* A null pattern means use the previously compiled pattern. |
* A null pattern means use the previously compiled pattern. |
*/ |
*/ |
if (!prev_pattern()) |
search_type |= SRCH_AFTER_TARGET; |
|
if (!prev_pattern(&search_info) && !hist_pattern(search_type)) |
{ |
{ |
error("No previous regular expression", NULL_PARG); |
error("No previous regular expression", NULL_PARG); |
return (-1); |
return (-1); |
} |
} |
if ((search_type & SRCH_NO_REGEX) != |
if ((search_type & SRCH_NO_REGEX) != |
(last_search_type & SRCH_NO_REGEX)) |
(search_info.search_type & SRCH_NO_REGEX)) |
{ |
{ |
error("Please re-enter search pattern", NULL_PARG); |
error("Please re-enter search pattern", NULL_PARG); |
return -1; |
return -1; |
|
|
/* |
/* |
* Compile the pattern. |
* Compile the pattern. |
*/ |
*/ |
ucase = is_ucase(pattern); |
if (set_pattern(&search_info, pattern, search_type) < 0) |
if (caseless == OPT_ONPLUS) |
|
cvt_text(pattern, pattern, CVT_TO_LC); |
|
if (compile_pattern(pattern, search_type) < 0) |
|
return (-1); |
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 |
if (hilite_search) |
if (hilite_search) |
{ |
{ |
|
|
POSITION max_epos; |
POSITION max_epos; |
int result; |
int result; |
int i; |
int i; |
|
|
/* |
/* |
* Search beyond where we're asked to search, so the prep region covers |
* 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. |
* more than we need. Do one big search instead of a bunch of small ones. |
*/ |
*/ |
#define SEARCH_MORE (3*size_linebuf) |
#define SEARCH_MORE (3*size_linebuf) |
|
|
if (!prev_pattern()) |
if (!prev_pattern(&search_info) && !is_filtering()) |
return; |
return; |
|
|
/* |
/* |
|
|
{ |
{ |
max_epos = spos; |
max_epos = spos; |
for (i = 0; i < maxlines; i++) |
for (i = 0; i < maxlines; i++) |
max_epos = forw_raw_line(max_epos, (char **)NULL); |
max_epos = forw_raw_line(max_epos, (char **)NULL, (int *)NULL); |
} |
} |
|
|
/* |
/* |
|
|
* Discard the old prep region and start a new one. |
* Discard the old prep region and start a new one. |
*/ |
*/ |
clr_hilite(); |
clr_hilite(); |
|
clr_filter(); |
if (epos != NULL_POSITION) |
if (epos != NULL_POSITION) |
epos += SEARCH_MORE; |
epos += SEARCH_MORE; |
nprep_startpos = spos; |
nprep_startpos = spos; |
|
|
|
|
if (epos == NULL_POSITION || epos > spos) |
if (epos == NULL_POSITION || epos > spos) |
{ |
{ |
result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0, |
int search_type = SRCH_FORW | SRCH_FIND_ALL; |
|
search_type |= (search_info.search_type & SRCH_NO_REGEX); |
|
result = search_range(spos, epos, search_type, 0, |
maxlines, (POSITION*)NULL, &new_epos); |
maxlines, (POSITION*)NULL, &new_epos); |
if (result < 0) |
if (result < 0) |
return; |
return; |
|
|
prep_startpos = nprep_startpos; |
prep_startpos = nprep_startpos; |
prep_endpos = nprep_endpos; |
prep_endpos = nprep_endpos; |
} |
} |
#endif |
|
|
|
/* |
/* |
* Simple pattern matching function. |
* Set the pattern to be used for line filtering. |
* It supports no metacharacters like *, etc. |
|
*/ |
*/ |
static int |
public void |
match(pattern, buf, pfound, pend) |
set_filter_pattern(pattern, search_type) |
char *pattern, *buf; |
char *pattern; |
char **pfound, **pend; |
int search_type; |
{ |
{ |
register char *pp, *lp; |
clr_filter(); |
|
if (pattern == NULL || *pattern == '\0') |
|
clear_pattern(&filter_info); |
|
else |
|
set_pattern(&filter_info, pattern, search_type); |
|
screen_trashed = 1; |
|
} |
|
|
for ( ; *buf != '\0'; buf++) |
/* |
{ |
* Is there a line filter in effect? |
for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) |
*/ |
if (*pp == '\0' || *lp == '\0') |
public int |
break; |
is_filtering() |
if (*pp == '\0') |
{ |
{ |
if (ch_getflags() & CH_HELPFILE) |
if (pfound != NULL) |
return (0); |
*pfound = buf; |
return prev_pattern(&filter_info); |
if (pend != NULL) |
|
*pend = lp; |
|
return (1); |
|
} |
|
} |
|
return (0); |
|
} |
} |
|
#endif |
|
|
#if HAVE_V8_REGCOMP |
#if HAVE_V8_REGCOMP |
/* |
/* |