1ea316270f
compiler-time type checking, and also causes problems for targets where multiple incompatible calling conventions may be selected based on argument types. This change switches less(1) to ANSI prototypes. While there, we also remove use of "register", and attempt to use "const" a bit better now that the compiler can check argument types. Reviewed by: cem, emaste MFC after: 3 weeks Sponsored by: DARPA, AFRL Differential Revision: https://reviews.freebsd.org/D10152
382 lines
7.6 KiB
C
382 lines
7.6 KiB
C
/*
|
|
* Copyright (C) 1984-2015 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, see the README file.
|
|
*/
|
|
|
|
/*
|
|
* Routines to do pattern matching.
|
|
*/
|
|
|
|
#include "less.h"
|
|
#include "pattern.h"
|
|
|
|
extern int caseless;
|
|
|
|
/*
|
|
* Compile a search pattern, for future use by match_pattern.
|
|
*/
|
|
static int
|
|
compile_pattern2(char *pattern, int search_type, void **comp_pattern, int show_error)
|
|
{
|
|
if (search_type & SRCH_NO_REGEX)
|
|
return (0);
|
|
{
|
|
#if HAVE_GNU_REGEX
|
|
struct re_pattern_buffer *comp = (struct re_pattern_buffer *)
|
|
ecalloc(1, sizeof(struct re_pattern_buffer));
|
|
struct re_pattern_buffer **pcomp =
|
|
(struct re_pattern_buffer **) comp_pattern;
|
|
re_set_syntax(RE_SYNTAX_POSIX_EXTENDED);
|
|
if (re_compile_pattern(pattern, strlen(pattern), comp))
|
|
{
|
|
free(comp);
|
|
if (show_error)
|
|
error("Invalid pattern", NULL_PARG);
|
|
return (-1);
|
|
}
|
|
if (*pcomp != NULL)
|
|
regfree(*pcomp);
|
|
*pcomp = comp;
|
|
#endif
|
|
#if HAVE_POSIX_REGCOMP
|
|
regex_t *comp = (regex_t *) ecalloc(1, sizeof(regex_t));
|
|
regex_t **pcomp = (regex_t **) comp_pattern;
|
|
if (regcomp(comp, pattern, REGCOMP_FLAG))
|
|
{
|
|
free(comp);
|
|
if (show_error)
|
|
error("Invalid pattern", NULL_PARG);
|
|
return (-1);
|
|
}
|
|
if (*pcomp != NULL)
|
|
regfree(*pcomp);
|
|
*pcomp = comp;
|
|
#endif
|
|
#if HAVE_PCRE
|
|
pcre *comp;
|
|
pcre **pcomp = (pcre **) comp_pattern;
|
|
constant char *errstring;
|
|
int erroffset;
|
|
PARG parg;
|
|
comp = pcre_compile(pattern, 0,
|
|
&errstring, &erroffset, NULL);
|
|
if (comp == NULL)
|
|
{
|
|
parg.p_string = (char *) errstring;
|
|
if (show_error)
|
|
error("%s", &parg);
|
|
return (-1);
|
|
}
|
|
*pcomp = comp;
|
|
#endif
|
|
#if HAVE_RE_COMP
|
|
PARG parg;
|
|
int *pcomp = (int *) comp_pattern;
|
|
if ((parg.p_string = re_comp(pattern)) != NULL)
|
|
{
|
|
if (show_error)
|
|
error("%s", &parg);
|
|
return (-1);
|
|
}
|
|
*pcomp = 1;
|
|
#endif
|
|
#if HAVE_REGCMP
|
|
char *comp;
|
|
char **pcomp = (char **) comp_pattern;
|
|
if ((comp = regcmp(pattern, 0)) == NULL)
|
|
{
|
|
if (show_error)
|
|
error("Invalid pattern", NULL_PARG);
|
|
return (-1);
|
|
}
|
|
if (pcomp != NULL)
|
|
free(*pcomp);
|
|
*pcomp = comp;
|
|
#endif
|
|
#if HAVE_V8_REGCOMP
|
|
struct regexp *comp;
|
|
struct regexp **pcomp = (struct regexp **) comp_pattern;
|
|
reg_show_error = show_error;
|
|
comp = regcomp(pattern);
|
|
reg_show_error = 1;
|
|
if (comp == NULL)
|
|
{
|
|
/*
|
|
* regcomp has already printed an error message
|
|
* via regerror().
|
|
*/
|
|
return (-1);
|
|
}
|
|
if (*pcomp != NULL)
|
|
free(*pcomp);
|
|
*pcomp = comp;
|
|
#endif
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Like compile_pattern2, but convert the pattern to lowercase if necessary.
|
|
*/
|
|
public int
|
|
compile_pattern(char *pattern, int search_type, void **comp_pattern)
|
|
{
|
|
char *cvt_pattern;
|
|
int result;
|
|
|
|
if (caseless != OPT_ONPLUS)
|
|
cvt_pattern = pattern;
|
|
else
|
|
{
|
|
cvt_pattern = (char*) ecalloc(1, cvt_length(strlen(pattern), CVT_TO_LC));
|
|
cvt_text(cvt_pattern, pattern, (int *)NULL, (int *)NULL, CVT_TO_LC);
|
|
}
|
|
result = compile_pattern2(cvt_pattern, search_type, comp_pattern, 1);
|
|
if (cvt_pattern != pattern)
|
|
free(cvt_pattern);
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Forget that we have a compiled pattern.
|
|
*/
|
|
public void
|
|
uncompile_pattern(void **pattern)
|
|
{
|
|
#if HAVE_GNU_REGEX
|
|
struct re_pattern_buffer **pcomp = (struct re_pattern_buffer **) pattern;
|
|
if (*pcomp != NULL)
|
|
regfree(*pcomp);
|
|
*pcomp = NULL;
|
|
#endif
|
|
#if HAVE_POSIX_REGCOMP
|
|
regex_t **pcomp = (regex_t **) pattern;
|
|
if (*pcomp != NULL)
|
|
regfree(*pcomp);
|
|
*pcomp = NULL;
|
|
#endif
|
|
#if HAVE_PCRE
|
|
pcre **pcomp = (pcre **) pattern;
|
|
if (*pcomp != NULL)
|
|
pcre_free(*pcomp);
|
|
*pcomp = NULL;
|
|
#endif
|
|
#if HAVE_RE_COMP
|
|
int *pcomp = (int *) pattern;
|
|
*pcomp = 0;
|
|
#endif
|
|
#if HAVE_REGCMP
|
|
char **pcomp = (char **) pattern;
|
|
if (*pcomp != NULL)
|
|
free(*pcomp);
|
|
*pcomp = NULL;
|
|
#endif
|
|
#if HAVE_V8_REGCOMP
|
|
struct regexp **pcomp = (struct regexp **) pattern;
|
|
if (*pcomp != NULL)
|
|
free(*pcomp);
|
|
*pcomp = NULL;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Can a pattern be successfully compiled?
|
|
*/
|
|
public int
|
|
valid_pattern(char *pattern)
|
|
{
|
|
void *comp_pattern;
|
|
int result;
|
|
|
|
CLEAR_PATTERN(comp_pattern);
|
|
result = compile_pattern2(pattern, 0, &comp_pattern, 0);
|
|
if (result != 0)
|
|
return (0);
|
|
uncompile_pattern(&comp_pattern);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Is a compiled pattern null?
|
|
*/
|
|
public int
|
|
is_null_pattern(void *pattern)
|
|
{
|
|
#if HAVE_GNU_REGEX
|
|
return (pattern == NULL);
|
|
#endif
|
|
#if HAVE_POSIX_REGCOMP
|
|
return (pattern == NULL);
|
|
#endif
|
|
#if HAVE_PCRE
|
|
return (pattern == NULL);
|
|
#endif
|
|
#if HAVE_RE_COMP
|
|
return (pattern == 0);
|
|
#endif
|
|
#if HAVE_REGCMP
|
|
return (pattern == NULL);
|
|
#endif
|
|
#if HAVE_V8_REGCOMP
|
|
return (pattern == NULL);
|
|
#endif
|
|
#if NO_REGEX
|
|
return (pattern == NULL);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Simple pattern matching function.
|
|
* It supports no metacharacters like *, etc.
|
|
*/
|
|
static int
|
|
match(char *pattern, int pattern_len, char *buf, int buf_len, char **pfound, char **pend)
|
|
{
|
|
char *pp, *lp;
|
|
char *pattern_end = pattern + pattern_len;
|
|
char *buf_end = buf + buf_len;
|
|
|
|
for ( ; buf < buf_end; buf++)
|
|
{
|
|
for (pp = pattern, lp = buf; ; pp++, lp++)
|
|
{
|
|
char cp = *pp;
|
|
char cl = *lp;
|
|
if (caseless == OPT_ONPLUS && ASCII_IS_UPPER(cp))
|
|
cp = ASCII_TO_LOWER(cp);
|
|
if (cp != cl)
|
|
break;
|
|
if (pp == pattern_end || lp == buf_end)
|
|
break;
|
|
}
|
|
if (pp == pattern_end)
|
|
{
|
|
if (pfound != NULL)
|
|
*pfound = buf;
|
|
if (pend != NULL)
|
|
*pend = lp;
|
|
return (1);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Perform a pattern match with the previously compiled pattern.
|
|
* Set sp and ep to the start and end of the matched string.
|
|
*/
|
|
public int
|
|
match_pattern(void *pattern, char *tpattern, char *line, int line_len, char **sp, char **ep, int notbol, int search_type)
|
|
{
|
|
int matched;
|
|
#if HAVE_GNU_REGEX
|
|
struct re_pattern_buffer *spattern = (struct re_pattern_buffer *) pattern;
|
|
#endif
|
|
#if HAVE_POSIX_REGCOMP
|
|
regex_t *spattern = (regex_t *) pattern;
|
|
#endif
|
|
#if HAVE_PCRE
|
|
pcre *spattern = (pcre *) pattern;
|
|
#endif
|
|
#if HAVE_RE_COMP
|
|
int spattern = (int) pattern;
|
|
#endif
|
|
#if HAVE_REGCMP
|
|
char *spattern = (char *) pattern;
|
|
#endif
|
|
#if HAVE_V8_REGCOMP
|
|
struct regexp *spattern = (struct regexp *) pattern;
|
|
#endif
|
|
|
|
*sp = *ep = NULL;
|
|
#if NO_REGEX
|
|
search_type |= SRCH_NO_REGEX;
|
|
#endif
|
|
if (search_type & SRCH_NO_REGEX)
|
|
matched = match(tpattern, strlen(tpattern), line, line_len, sp, ep);
|
|
else
|
|
{
|
|
#if HAVE_GNU_REGEX
|
|
{
|
|
struct re_registers search_regs;
|
|
spattern->not_bol = notbol;
|
|
spattern->regs_allocated = REGS_UNALLOCATED;
|
|
matched = re_search(spattern, line, line_len, 0, line_len, &search_regs) >= 0;
|
|
if (matched)
|
|
{
|
|
*sp = line + search_regs.start[0];
|
|
*ep = line + search_regs.end[0];
|
|
}
|
|
}
|
|
#endif
|
|
#if HAVE_POSIX_REGCOMP
|
|
{
|
|
regmatch_t rm;
|
|
int flags = (notbol) ? REG_NOTBOL : 0;
|
|
#ifdef REG_STARTEND
|
|
flags |= REG_STARTEND;
|
|
rm.rm_so = 0;
|
|
rm.rm_eo = line_len;
|
|
#endif
|
|
matched = !regexec(spattern, line, 1, &rm, flags);
|
|
if (matched)
|
|
{
|
|
#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(spattern, NULL, line, line_len,
|
|
0, flags, ovector, 3) >= 0;
|
|
if (matched)
|
|
{
|
|
*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(spattern, line);
|
|
matched = (*ep != NULL);
|
|
if (matched)
|
|
*sp = __loc1;
|
|
#endif
|
|
#if HAVE_V8_REGCOMP
|
|
#if HAVE_REGEXEC2
|
|
matched = regexec2(spattern, line, notbol);
|
|
#else
|
|
matched = regexec(spattern, line);
|
|
#endif
|
|
if (matched)
|
|
{
|
|
*sp = spattern->startp[0];
|
|
*ep = spattern->endp[0];
|
|
}
|
|
#endif
|
|
}
|
|
matched = (!(search_type & SRCH_NO_MATCH) && matched) ||
|
|
((search_type & SRCH_NO_MATCH) && !matched);
|
|
return (matched);
|
|
}
|
|
|