#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 REG_EXTENDED
#else
#define REGCOMP_FLAG 0
#endif
#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 how_search;
extern int caseless;
extern int linenums;
extern int sc_height;
extern int jump_sline;
extern int bs_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
#if HAVE_POSIX_REGCOMP
static regex_t *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;
#define CVT_TO_LC 01
#define CVT_BS 02
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++, dst++)
{
if ((ops & CVT_TO_LC) && isupper((unsigned char) *src))
*dst = tolower((unsigned char) *src);
else if ((ops & CVT_BS) && *src == '\b' && dst > odst)
dst -= 2;
else
*dst = *src;
}
*dst = '\0';
}
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);
}
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_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
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 (is_hilited(pos, epos, 1))
{
(void) forw_line(pos);
goto_line(slinenum);
put_line();
}
}
hide_hilite = save_hide_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
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
}
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_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)
{
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);
}
static void
uncompile_pattern()
{
#if HAVE_POSIX_REGCOMP
if (regpattern != NULL)
regfree(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;
}
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);
*sp = line + rm.rm_so;
*ep = line + rm.rm_eo;
}
#endif
#if HAVE_RE_COMP
matched = (re_exec(line) == 1);
*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
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;
}
public int
is_hilited(pos, epos, nohide)
POSITION pos;
POSITION epos;
int nohide;
{
struct hilite *hl;
if (start_attnpos != NULL_POSITION &&
pos < end_attnpos &&
(epos == NULL_POSITION || epos > start_attnpos))
return (1);
if (hilite_search == 0)
return (0);
if (!nohide && hide_hilite)
return (0);
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);
}
static void
add_hilite(anchor, hl)
struct hilite *anchor;
struct hilite *hl;
{
struct hilite *ihl;
for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next)
{
if (ihl->hl_next->hl_startpos > hl->hl_startpos)
break;
}
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)
{
free(hl);
return;
}
hl->hl_next = ihl->hl_next;
ihl->hl_next = hl;
}
static void
adj_hilite(anchor, linepos)
struct hilite *anchor;
POSITION linepos;
{
char *line;
struct hilite *hl;
int checkstart;
POSITION opos;
POSITION npos;
(void) forw_raw_line(linepos, &line);
opos = npos = linepos;
hl = anchor->hl_first;
checkstart = TRUE;
while (hl != NULL)
{
if (checkstart && hl->hl_startpos == opos)
{
hl->hl_startpos = npos;
checkstart = FALSE;
continue;
} else if (!checkstart && hl->hl_endpos == opos)
{
hl->hl_endpos = npos;
checkstart = TRUE;
hl = hl->hl_next;
continue;
}
if (*line == '\0')
break;
opos++;
npos++;
line++;
while (line[0] == '\b' && line[1] != '\0')
{
npos += 2;
line += 2;
}
}
}
static void
hilite_line(linepos, line, sp, ep)
POSITION linepos;
char *line;
char *sp;
char *ep;
{
char *searchp;
struct hilite *hl;
struct hilite hilites;
if (sp == NULL || ep == NULL)
return;
searchp = line;
hilites.hl_first = NULL;
do {
if (ep > sp)
{
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 (ep > searchp)
searchp = ep;
else if (*searchp != '\0')
searchp++;
else
break;
} while (match_pattern(searchp, &sp, &ep, 1));
if (bs_mode == BS_SPECIAL)
{
adj_hilite(&hilites, linepos);
}
while ((hl = hilites.hl_next) != NULL)
{
hilites.hl_next = hl->hl_next;
add_hilite(&hilite_anchor, hl);
}
}
#endif
public void
chg_caseless()
{
if (!is_ucase_pattern)
is_caseless = caseless;
else
uncompile_pattern();
}
#if HILITE_SEARCH
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);
}
public void
chg_hilite()
{
clr_hilite();
hide_hilite = 0;
if (hilite_search == OPT_ONPLUS)
hilite_screen();
}
#endif
static POSITION
search_pos(search_type)
int search_type;
{
POSITION pos;
int linenum;
if (empty_screen())
{
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)
{
if (search_type & SRCH_FORW)
linenum = BOTTOM_PLUS_ONE;
else
linenum = TOP;
pos = position(linenum);
} else
{
linenum = adjsline(jump_sline);
pos = position(linenum);
if (search_type & SRCH_FORW)
pos = forw_raw_line(pos, (char **)NULL);
}
return (pos);
}
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;
int linenum;
char *sp, *ep;
int line_match;
POSITION linepos, oldpos;
linenum = find_linenum(pos);
oldpos = pos;
for (;;)
{
if (ABORT_SIGS())
{
return (-1);
}
if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0)
{
if (pendpos != NULL)
*pendpos = pos;
return (matches);
}
if (maxlines > 0)
maxlines--;
if (search_type & SRCH_FORW)
{
linepos = pos;
pos = forw_raw_line(pos, &line);
if (linenum != 0)
linenum++;
} else
{
pos = back_raw_line(pos, &line);
linepos = pos;
if (linenum != 0)
linenum--;
}
if (pos == NULL_POSITION)
{
if (pendpos != NULL)
*pendpos = oldpos;
return (matches);
}
if (linenums && abs((int)(pos - oldpos)) > 1024)
add_lnum(linenum, pos);
oldpos = pos;
if (is_caseless || bs_mode == BS_SPECIAL)
{
int ops = 0;
if (is_caseless)
ops |= CVT_TO_LC;
if (bs_mode == BS_SPECIAL)
ops |= CVT_BS;
cvt_text(line, line, ops);
}
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;
if (search_type & SRCH_FIND_ALL)
{
#if HILITE_SEARCH
if (line_match)
hilite_line(linepos, line, sp, ep);
#endif
} else if (--matches <= 0)
{
#if HILITE_SEARCH
if (hilite_search == 1)
{
clr_hilite();
if (line_match)
hilite_line(linepos, line, sp, ep);
}
#endif
if (plinepos != NULL)
*plinepos = linepos;
return (0);
}
}
}
public int
search(search_type, pattern, n)
int search_type;
char *pattern;
int n;
{
POSITION pos;
int ucase;
if (pattern == NULL || *pattern == '\0')
{
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)
{
repaint_hilite(0);
}
if (hilite_search == OPT_ONPLUS && hide_hilite)
{
hide_hilite = 0;
hilite_screen();
}
hide_hilite = 0;
#endif
} else
{
ucase = is_ucase(pattern);
if (caseless == OPT_ONPLUS)
cvt_text(pattern, pattern, CVT_TO_LC);
if (compile_pattern(pattern, search_type) < 0)
return (-1);
is_ucase_pattern = ucase;
if (is_ucase_pattern && caseless != OPT_ONPLUS)
is_caseless = 0;
else
is_caseless = caseless;
#if HILITE_SEARCH
if (hilite_search)
{
repaint_hilite(0);
hide_hilite = 0;
clr_hilite();
}
if (hilite_search == OPT_ONPLUS)
{
hilite_screen();
}
#endif
}
pos = search_pos(search_type);
if (pos == NULL_POSITION)
{
if (search_type & SRCH_PAST_EOF)
return (n);
error("Nothing to search", NULL_PARG);
return (-1);
}
n = search_range(pos, NULL_POSITION, search_type, n, -1,
&pos, (POSITION*)NULL);
if (n != 0)
{
#if HILITE_SEARCH
if (hilite_search == OPT_ON && n > 0)
repaint_hilite(1);
#endif
return (n);
}
if (!(search_type & SRCH_NO_MOVE))
{
jump_loc(pos, jump_sline);
}
#if HILITE_SEARCH
if (hilite_search == OPT_ON)
repaint_hilite(1);
#endif
return (0);
}
#if HILITE_SEARCH
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;
#define SEARCH_MORE (3*size_linebuf)
if (!prev_pattern())
return;
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);
}
if (prep_startpos == NULL_POSITION ||
(epos != NULL_POSITION && epos < prep_startpos) ||
spos > prep_endpos)
{
clr_hilite();
if (epos != NULL_POSITION)
epos += SEARCH_MORE;
nprep_startpos = spos;
} else
{
if (epos == NULL_POSITION)
{
;
} else if (epos > prep_endpos)
{
epos += SEARCH_MORE;
} else
{
epos = prep_startpos;
}
if (spos < prep_startpos)
{
if (spos < SEARCH_MORE)
spos = 0;
else
spos -= SEARCH_MORE;
nprep_startpos = spos;
} else
{
spos = prep_endpos;
}
}
if (epos != NULL_POSITION && max_epos != NULL_POSITION &&
epos > max_epos)
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
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
void
regerror(s)
char *s;
{
PARG parg;
parg.p_string = s;
error("%s", &parg);
}
#endif
#if !HAVE_STRCHR
char *
strchr(s, c)
char *s;
int c;
{
for ( ; *s != '\0'; s++)
if (*s == c)
return (s);
if (c == '\0')
return (s);
return (NULL);
}
#endif