chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
This is the license, copyright notice, and disclaimer for TRE, a regex
|
||||
matching package (library and tools) with support for approximate
|
||||
matching.
|
||||
|
||||
Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
tre_regcomp.c - TRE POSIX compatible regex compilation functions.
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
int
|
||||
tre_regncomp(regex_t *preg, const char *regex, size_t n, int cflags)
|
||||
{
|
||||
int ret;
|
||||
if (n > TRE_MAX_RE)
|
||||
return REG_ESPACE;
|
||||
#if TRE_WCHAR
|
||||
tre_char_t *wregex;
|
||||
size_t wlen;
|
||||
|
||||
wregex = xmalloc(sizeof(tre_char_t) * (n + 1));
|
||||
if (wregex == NULL)
|
||||
return REG_ESPACE;
|
||||
|
||||
/* If the current locale uses the standard single byte encoding of
|
||||
characters, we don't do a multibyte string conversion. If we did,
|
||||
many applications which use the default locale would break since
|
||||
the default "C" locale uses the 7-bit ASCII character set, and
|
||||
all characters with the eighth bit set would be considered invalid. */
|
||||
#if TRE_MULTIBYTE
|
||||
if (TRE_MB_CUR_MAX == 1)
|
||||
#endif /* TRE_MULTIBYTE */
|
||||
{
|
||||
size_t i;
|
||||
const unsigned char *str = (const unsigned char *)regex;
|
||||
tre_char_t *wstr = wregex;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
*(wstr++) = *(str++);
|
||||
wlen = n;
|
||||
}
|
||||
#if TRE_MULTIBYTE
|
||||
else
|
||||
{
|
||||
size_t consumed;
|
||||
tre_char_t *wcptr = wregex;
|
||||
#ifdef HAVE_MBSTATE_T
|
||||
mbstate_t state;
|
||||
memset(&state, '\0', sizeof(state));
|
||||
#endif /* HAVE_MBSTATE_T */
|
||||
while (n > 0)
|
||||
{
|
||||
consumed = tre_mbrtowc(wcptr, regex, n, &state);
|
||||
|
||||
switch (consumed)
|
||||
{
|
||||
case 0:
|
||||
if (*regex == '\0')
|
||||
consumed = 1;
|
||||
else
|
||||
{
|
||||
xfree(wregex);
|
||||
return REG_BADPAT;
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
DPRINT(("mbrtowc: error %d: %s.\n", errno, strerror(errno)));
|
||||
xfree(wregex);
|
||||
return REG_BADPAT;
|
||||
case -2:
|
||||
/* The last character wasn't complete. Let's not call it a
|
||||
fatal error. */
|
||||
consumed = n;
|
||||
break;
|
||||
}
|
||||
regex += consumed;
|
||||
n -= consumed;
|
||||
wcptr++;
|
||||
}
|
||||
wlen = wcptr - wregex;
|
||||
}
|
||||
#endif /* TRE_MULTIBYTE */
|
||||
|
||||
wregex[wlen] = L'\0';
|
||||
ret = tre_compile(preg, wregex, wlen, cflags);
|
||||
xfree(wregex);
|
||||
#else /* !TRE_WCHAR */
|
||||
ret = tre_compile(preg, (const tre_char_t *)regex, n, cflags);
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this version takes bytes literally, to be used with raw vectors */
|
||||
int
|
||||
tre_regncompb(regex_t *preg, const char *regex, size_t n, int cflags)
|
||||
{
|
||||
int ret;
|
||||
if (n > TRE_MAX_RE)
|
||||
return REG_ESPACE;
|
||||
#if TRE_WCHAR /* wide chars = we need to convert it all to the wide format */
|
||||
tre_char_t *wregex;
|
||||
size_t i;
|
||||
|
||||
wregex = xmalloc(sizeof(tre_char_t) * n);
|
||||
if (wregex == NULL)
|
||||
return REG_ESPACE;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
wregex[i] = (tre_char_t) ((unsigned char) regex[i]);
|
||||
|
||||
ret = tre_compile(preg, wregex, n, cflags | REG_USEBYTES);
|
||||
xfree(wregex);
|
||||
#else /* !TRE_WCHAR */
|
||||
ret = tre_compile(preg, (const tre_char_t *)regex, n, cflags | REG_USEBYTES);
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
tre_regcomp(regex_t *preg, const char *regex, int cflags)
|
||||
{
|
||||
size_t n = regex ? strlen(regex) : 0;
|
||||
if (n > TRE_MAX_RE)
|
||||
return REG_ESPACE;
|
||||
return tre_regncomp(preg, regex, n, cflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regcompb(regex_t *preg, const char *regex, int cflags)
|
||||
{
|
||||
int ret;
|
||||
tre_char_t *wregex;
|
||||
size_t i, n = regex ? strlen(regex) : 0;
|
||||
const unsigned char *str = (const unsigned char *)regex;
|
||||
tre_char_t *wstr;
|
||||
|
||||
if (n > TRE_MAX_RE)
|
||||
return REG_ESPACE;
|
||||
wregex = xmalloc(sizeof(tre_char_t) * (n + 1));
|
||||
if (wregex == NULL) return REG_ESPACE;
|
||||
wstr = wregex;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
*(wstr++) = *(str++);
|
||||
wregex[n] = L'\0';
|
||||
ret = tre_compile(preg, wregex, n, cflags | REG_USEBYTES);
|
||||
xfree(wregex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
int
|
||||
tre_regwncomp(regex_t *preg, const wchar_t *regex, size_t n, int cflags)
|
||||
{
|
||||
if (n > TRE_MAX_RE)
|
||||
return REG_ESPACE;
|
||||
return tre_compile(preg, regex, n, cflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regwcomp(regex_t *preg, const wchar_t *regex, int cflags)
|
||||
{
|
||||
size_t n = regex ? wcslen(regex) : 0;
|
||||
if (n > TRE_MAX_RE)
|
||||
return REG_ESPACE;
|
||||
return tre_compile(preg, regex, n, cflags);
|
||||
}
|
||||
#endif /* TRE_WCHAR */
|
||||
|
||||
void
|
||||
tre_regfree(regex_t *preg)
|
||||
{
|
||||
tre_free(preg);
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
tre_regerror.c - POSIX tre_regerror() implementation for TRE.
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <string.h>
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
#ifdef HAVE_WCTYPE_H
|
||||
#include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H */
|
||||
|
||||
#include "tre-internal.h"
|
||||
|
||||
#ifdef HAVE_GETTEXT
|
||||
#include <libintl.h>
|
||||
#else
|
||||
#define dgettext(p, s) s
|
||||
#define gettext(s) s
|
||||
#endif
|
||||
|
||||
#define _(String) dgettext(PACKAGE, String)
|
||||
#define gettext_noop(String) String
|
||||
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
/* Error message strings for error codes listed in `tre.h'. This list
|
||||
needs to be in sync with the codes listed there, naturally. */
|
||||
static const char *tre_error_messages[] =
|
||||
{ gettext_noop("No error"), /* REG_OK */
|
||||
gettext_noop("No match"), /* REG_NOMATCH */
|
||||
gettext_noop("Invalid regexp"), /* REG_BADPAT */
|
||||
gettext_noop("Unknown collating element"), /* REG_ECOLLATE */
|
||||
gettext_noop("Unknown character class name"), /* REG_ECTYPE */
|
||||
gettext_noop("Trailing backslash"), /* REG_EESCAPE */
|
||||
gettext_noop("Invalid back reference"), /* REG_ESUBREG */
|
||||
gettext_noop("Missing ']'"), /* REG_EBRACK */
|
||||
gettext_noop("Missing ')'"), /* REG_EPAREN */
|
||||
gettext_noop("Missing '}'"), /* REG_EBRACE */
|
||||
gettext_noop("Invalid contents of {}"), /* REG_BADBR */
|
||||
gettext_noop("Invalid character range"), /* REG_ERANGE */
|
||||
gettext_noop("Out of memory"), /* REG_ESPACE */
|
||||
gettext_noop("Invalid use of repetition operators"), /* REG_BADRPT */
|
||||
gettext_noop("Maximum repetition in {} larger than " xstr(RE_DUP_MAX)), /* REG_BADMAX */
|
||||
};
|
||||
|
||||
size_t
|
||||
tre_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
|
||||
{
|
||||
const char *err;
|
||||
size_t err_len;
|
||||
|
||||
/*LINTED*/(void)&preg;
|
||||
if (errcode >= 0
|
||||
&& errcode < (int)(sizeof(tre_error_messages)
|
||||
/ sizeof(*tre_error_messages)))
|
||||
err = gettext(tre_error_messages[errcode]);
|
||||
else
|
||||
err = gettext("Unknown error");
|
||||
|
||||
err_len = strlen(err) + 1;
|
||||
if (errbuf_size > 0 && errbuf != NULL)
|
||||
{
|
||||
if (err_len > errbuf_size)
|
||||
{
|
||||
strncpy(errbuf, err, errbuf_size - 1);
|
||||
errbuf[errbuf_size - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(errbuf, err);
|
||||
}
|
||||
}
|
||||
return err_len;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
regex.h - TRE legacy API
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
This header is for source level compatibility with old code using
|
||||
the <tre/regex.h> header which defined the TRE API functions without
|
||||
a prefix. New code should include <tre/tre.h> instead.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TRE_REXEX_H
|
||||
#define TRE_REGEX_H 1
|
||||
|
||||
#ifdef USE_LOCAL_TRE_H
|
||||
/* Use the header(s) from the TRE package that this file is part of.
|
||||
(Yes, this file is in local_include too, but the explict path
|
||||
means there is no way to get a system tre.h by accident.) */
|
||||
#include "tre.h"
|
||||
#else
|
||||
/* Use the header(s) from an installed version of the TRE package
|
||||
(so that this application matches the installed libtre),
|
||||
not the one(s) in the local_includes directory. */
|
||||
#include <tre/tre.h>
|
||||
#endif
|
||||
|
||||
#ifndef TRE_USE_SYSTEM_REGEX_H
|
||||
#define regcomp tre_regcomp
|
||||
#define regerror tre_regerror
|
||||
#define regexec tre_regexec
|
||||
#define regfree tre_regfree
|
||||
#endif /* TRE_USE_SYSTEM_REGEX_H */
|
||||
|
||||
#define regacomp tre_regacomp
|
||||
#define regaexec tre_regaexec
|
||||
#define regancomp tre_regancomp
|
||||
#define reganexec tre_reganexec
|
||||
#define regawncomp tre_regawncomp
|
||||
#define regawnexec tre_regawnexec
|
||||
#define regncomp tre_regncomp
|
||||
#define regnexec tre_regnexec
|
||||
#define regwcomp tre_regwcomp
|
||||
#define regwexec tre_regwexec
|
||||
#define regwncomp tre_regwncomp
|
||||
#define regwnexec tre_regwnexec
|
||||
|
||||
#endif /* TRE_REGEX_H */
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
tre_regexec.c - TRE POSIX compatible matching functions (and more).
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# if HAVE_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
char *alloca ();
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#endif /* TRE_USE_ALLOCA */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
#ifdef HAVE_WCTYPE_H
|
||||
#include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H */
|
||||
#ifndef TRE_WCHAR
|
||||
#include <ctype.h>
|
||||
#endif /* !TRE_WCHAR */
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif /* HAVE_MALLOC_H */
|
||||
#include <limits.h>
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
||||
/* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match
|
||||
endpoint values. */
|
||||
void
|
||||
tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
|
||||
const tre_tnfa_t *tnfa, int *tags, int match_eo)
|
||||
{
|
||||
tre_submatch_data_t *submatch_data;
|
||||
unsigned int i, j;
|
||||
int *parents;
|
||||
|
||||
i = 0;
|
||||
if (match_eo >= 0 && !(cflags & REG_NOSUB))
|
||||
{
|
||||
/* Construct submatch offsets from the tags. */
|
||||
DPRINT(("end tag = t%d = %d\n", tnfa->end_tag, match_eo));
|
||||
submatch_data = tnfa->submatch_data;
|
||||
while (i < tnfa->num_submatches && i < nmatch)
|
||||
{
|
||||
if (submatch_data[i].so_tag == tnfa->end_tag)
|
||||
pmatch[i].rm_so = match_eo;
|
||||
else
|
||||
pmatch[i].rm_so = tags[submatch_data[i].so_tag];
|
||||
|
||||
if (submatch_data[i].eo_tag == tnfa->end_tag)
|
||||
pmatch[i].rm_eo = match_eo;
|
||||
else
|
||||
pmatch[i].rm_eo = tags[submatch_data[i].eo_tag];
|
||||
|
||||
/* If either of the endpoints were not used, this submatch
|
||||
was not part of the match. */
|
||||
if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1)
|
||||
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
|
||||
|
||||
DPRINT(("pmatch[%d] = {t%d = %d, t%d = %d}\n", i,
|
||||
submatch_data[i].so_tag, pmatch[i].rm_so,
|
||||
submatch_data[i].eo_tag, pmatch[i].rm_eo));
|
||||
i++;
|
||||
}
|
||||
/* Reset all submatches that are not within all of their parent
|
||||
submatches. */
|
||||
i = 0;
|
||||
while (i < tnfa->num_submatches && i < nmatch)
|
||||
{
|
||||
if (pmatch[i].rm_eo == -1)
|
||||
assert(pmatch[i].rm_so == -1);
|
||||
assert(pmatch[i].rm_so <= pmatch[i].rm_eo);
|
||||
|
||||
parents = submatch_data[i].parents;
|
||||
if (parents != NULL)
|
||||
for (j = 0; parents[j] >= 0; j++)
|
||||
{
|
||||
DPRINT(("pmatch[%d] parent %d\n", i, parents[j]));
|
||||
if (pmatch[i].rm_so < pmatch[parents[j]].rm_so
|
||||
|| pmatch[i].rm_eo > pmatch[parents[j]].rm_eo)
|
||||
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
while (i < nmatch)
|
||||
{
|
||||
pmatch[i].rm_so = -1;
|
||||
pmatch[i].rm_eo = -1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Wrapper functions for POSIX compatible regexp matching.
|
||||
*/
|
||||
|
||||
int
|
||||
tre_have_backrefs(const regex_t *preg)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
return tnfa->have_backrefs;
|
||||
}
|
||||
|
||||
int
|
||||
tre_have_approx(const regex_t *preg)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
return tnfa->have_approx;
|
||||
}
|
||||
|
||||
static int
|
||||
tre_match(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, size_t nmatch, regmatch_t pmatch[],
|
||||
int eflags)
|
||||
{
|
||||
reg_errcode_t status;
|
||||
int *tags = NULL, eo;
|
||||
if (tnfa->num_tags > 0 && nmatch > 0)
|
||||
{
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
tags = alloca(sizeof(*tags) * tnfa->num_tags);
|
||||
#else /* !TRE_USE_ALLOCA */
|
||||
tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
if (tags == NULL)
|
||||
return REG_ESPACE;
|
||||
}
|
||||
|
||||
/* Dispatch to the appropriate matcher. */
|
||||
if (tnfa->have_backrefs || eflags & REG_BACKTRACKING_MATCHER)
|
||||
{
|
||||
/* The regex has back references, use the backtracking matcher. */
|
||||
if (type == STR_USER)
|
||||
{
|
||||
const tre_str_source *source = string;
|
||||
if (source->rewind == NULL || source->compare == NULL)
|
||||
{
|
||||
/* The backtracking matcher requires rewind and compare
|
||||
capabilities from the input stream. */
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (tags)
|
||||
xfree(tags);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
return REG_BADPAT;
|
||||
}
|
||||
}
|
||||
status = tre_tnfa_run_backtrack(tnfa, string, len, type,
|
||||
tags, eflags, &eo);
|
||||
}
|
||||
#ifdef TRE_APPROX
|
||||
else if (tnfa->have_approx || eflags & REG_APPROX_MATCHER)
|
||||
{
|
||||
/* The regex uses approximate matching, use the approximate matcher. */
|
||||
regamatch_t match;
|
||||
regaparams_t params;
|
||||
tre_regaparams_default(¶ms);
|
||||
params.max_err = 0;
|
||||
params.max_cost = 0;
|
||||
status = tre_tnfa_run_approx(tnfa, string, len, type, tags,
|
||||
&match, params, eflags, &eo);
|
||||
}
|
||||
#endif /* TRE_APPROX */
|
||||
else
|
||||
{
|
||||
/* Exact matching, no back references, use the parallel matcher. */
|
||||
status = tre_tnfa_run_parallel(tnfa, string, len, type,
|
||||
tags, eflags, &eo);
|
||||
}
|
||||
|
||||
if (status == REG_OK)
|
||||
/* A match was found, so fill the submatch registers. */
|
||||
tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo);
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (tags)
|
||||
xfree(tags);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
tre_regnexec(const regex_t *preg, const char *str, size_t len,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS;
|
||||
|
||||
return tre_match(tnfa, str, len, type, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
#ifdef TRE_USE_GNUC_REGEXEC_FPL
|
||||
int
|
||||
tre_regexec(const regex_t *preg, const char *str,
|
||||
size_t nmatch, regmatch_t pmatch[_Restrict_arr_ _REGEX_NELTS (nmatch)],
|
||||
int eflags)
|
||||
#else
|
||||
int
|
||||
tre_regexec(const regex_t *preg, const char *str,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
#endif
|
||||
{
|
||||
return tre_regnexec(preg, str, -1, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regexecb(const regex_t *preg, const char *str,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
|
||||
return tre_match(tnfa, str, -1, STR_BYTE, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regnexecb(const regex_t *preg, const char *str, size_t len,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
|
||||
return tre_match(tnfa, str, len, STR_BYTE, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
|
||||
int
|
||||
tre_regwnexec(const regex_t *preg, const wchar_t *str, size_t len,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
return tre_match(tnfa, str, len, STR_WIDE, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regwexec(const regex_t *preg, const wchar_t *str,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
return tre_regwnexec(preg, str, -1, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
#endif /* TRE_WCHAR */
|
||||
|
||||
int
|
||||
tre_reguexec(const regex_t *preg, const tre_str_source *str,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
return tre_match(tnfa, str, -1, STR_USER, nmatch, pmatch, eflags);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TRE_APPROX
|
||||
|
||||
/*
|
||||
Wrapper functions for approximate regexp matching.
|
||||
*/
|
||||
|
||||
static int
|
||||
tre_match_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, regamatch_t *match, regaparams_t params,
|
||||
int eflags)
|
||||
{
|
||||
reg_errcode_t status;
|
||||
int *tags = NULL, eo;
|
||||
|
||||
/* If the regexp does not use approximate matching features, the
|
||||
maximum cost is zero, and the approximate matcher isn't forced,
|
||||
use the exact matcher instead. */
|
||||
if (params.max_cost == 0 && !tnfa->have_approx
|
||||
&& !(eflags & REG_APPROX_MATCHER))
|
||||
return tre_match(tnfa, string, len, type, match->nmatch, match->pmatch,
|
||||
eflags);
|
||||
|
||||
/* Back references are not supported by the approximate matcher. */
|
||||
if (tnfa->have_backrefs)
|
||||
return REG_BADPAT;
|
||||
|
||||
if (tnfa->num_tags > 0 && match->nmatch > 0)
|
||||
{
|
||||
#if TRE_USE_ALLOCA
|
||||
tags = alloca(sizeof(*tags) * tnfa->num_tags);
|
||||
#else /* !TRE_USE_ALLOCA */
|
||||
tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
if (tags == NULL)
|
||||
return REG_ESPACE;
|
||||
}
|
||||
status = tre_tnfa_run_approx(tnfa, string, len, type, tags,
|
||||
match, params, eflags, &eo);
|
||||
if (status == REG_OK)
|
||||
tre_fill_pmatch(match->nmatch, match->pmatch, tnfa->cflags, tnfa, tags, eo);
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (tags)
|
||||
xfree(tags);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
tre_reganexec(const regex_t *preg, const char *str, size_t len,
|
||||
regamatch_t *match, regaparams_t params, int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS;
|
||||
|
||||
return tre_match_approx(tnfa, str, len, type, match, params, eflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regaexec(const regex_t *preg, const char *str,
|
||||
regamatch_t *match, regaparams_t params, int eflags)
|
||||
{
|
||||
return tre_reganexec(preg, str, -1, match, params, eflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regaexecb(const regex_t *preg, const char *str,
|
||||
regamatch_t *match, regaparams_t params, int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
|
||||
return tre_match_approx(tnfa, str, -1, STR_BYTE, match, params, eflags);
|
||||
}
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
|
||||
int
|
||||
tre_regawnexec(const regex_t *preg, const wchar_t *str, size_t len,
|
||||
regamatch_t *match, regaparams_t params, int eflags)
|
||||
{
|
||||
tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD;
|
||||
return tre_match_approx(tnfa, str, len, STR_WIDE,
|
||||
match, params, eflags);
|
||||
}
|
||||
|
||||
int
|
||||
tre_regawexec(const regex_t *preg, const wchar_t *str,
|
||||
regamatch_t *match, regaparams_t params, int eflags)
|
||||
{
|
||||
return tre_regawnexec(preg, str, -1, match, params, eflags);
|
||||
}
|
||||
|
||||
#endif /* TRE_WCHAR */
|
||||
|
||||
void
|
||||
tre_regaparams_default(regaparams_t *params)
|
||||
{
|
||||
memset(params, 0, sizeof(*params));
|
||||
params->cost_ins = 1;
|
||||
params->cost_del = 1;
|
||||
params->cost_subst = 1;
|
||||
params->max_cost = INT_MAX;
|
||||
params->max_ins = INT_MAX;
|
||||
params->max_del = INT_MAX;
|
||||
params->max_subst = INT_MAX;
|
||||
params->max_err = INT_MAX;
|
||||
}
|
||||
|
||||
#endif /* TRE_APPROX */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
tre-ast.c - Abstract syntax tree (AST) routines
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#include <assert.h>
|
||||
|
||||
#include "tre-ast.h"
|
||||
#include "tre-mem.h"
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size)
|
||||
{
|
||||
tre_ast_node_t *node;
|
||||
|
||||
node = tre_mem_calloc(mem, sizeof(*node));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->obj = tre_mem_calloc(mem, size);
|
||||
if (!node->obj)
|
||||
return NULL;
|
||||
node->type = type;
|
||||
node->nullable = -1;
|
||||
node->submatch_id = -1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max)
|
||||
{
|
||||
tre_ast_node_t *node;
|
||||
tre_literal_t *lit;
|
||||
|
||||
node = tre_ast_new_node(mem, LITERAL, sizeof(tre_literal_t));
|
||||
if (!node)
|
||||
return NULL;
|
||||
lit = node->obj;
|
||||
lit->code_min = code_min;
|
||||
lit->code_max = code_max;
|
||||
lit->position = -1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
|
||||
int minimal)
|
||||
{
|
||||
tre_ast_node_t *node;
|
||||
tre_iteration_t *iter;
|
||||
|
||||
node = tre_ast_new_node(mem, ITERATION, sizeof(tre_iteration_t));
|
||||
if (!node)
|
||||
return NULL;
|
||||
iter = node->obj;
|
||||
iter->arg = arg;
|
||||
iter->min = min;
|
||||
iter->max = max;
|
||||
iter->minimal = minimal;
|
||||
node->num_submatches = arg->num_submatches;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
|
||||
{
|
||||
tre_ast_node_t *node;
|
||||
|
||||
node = tre_ast_new_node(mem, UNION, sizeof(tre_union_t));
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
((tre_union_t *)node->obj)->left = left;
|
||||
((tre_union_t *)node->obj)->right = right;
|
||||
node->num_submatches = left->num_submatches + right->num_submatches;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
|
||||
tre_ast_node_t *right)
|
||||
{
|
||||
tre_ast_node_t *node;
|
||||
|
||||
node = tre_ast_new_node(mem, CATENATION, sizeof(tre_catenation_t));
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
((tre_catenation_t *)node->obj)->left = left;
|
||||
((tre_catenation_t *)node->obj)->right = right;
|
||||
node->num_submatches = left->num_submatches + right->num_submatches;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
|
||||
static void
|
||||
tre_findent(FILE *stream, int i)
|
||||
{
|
||||
while (i-- > 0)
|
||||
fputc(' ', stream);
|
||||
}
|
||||
|
||||
void
|
||||
tre_print_params(int *params)
|
||||
{
|
||||
int i;
|
||||
if (params)
|
||||
{
|
||||
DPRINT(("params ["));
|
||||
for (i = 0; i < TRE_PARAM_LAST; i++)
|
||||
{
|
||||
if (params[i] == TRE_PARAM_UNSET)
|
||||
DPRINT(("unset"));
|
||||
else if (params[i] == TRE_PARAM_DEFAULT)
|
||||
DPRINT(("default"));
|
||||
else
|
||||
DPRINT(("%d", params[i]));
|
||||
if (i < TRE_PARAM_LAST - 1)
|
||||
DPRINT((", "));
|
||||
}
|
||||
DPRINT(("]"));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tre_do_print(FILE *stream, tre_ast_node_t *ast, int indent)
|
||||
{
|
||||
int code_min, code_max, pos;
|
||||
int num_tags = ast->num_tags;
|
||||
tre_literal_t *lit;
|
||||
tre_iteration_t *iter;
|
||||
|
||||
tre_findent(stream, indent);
|
||||
switch (ast->type)
|
||||
{
|
||||
case LITERAL:
|
||||
lit = ast->obj;
|
||||
code_min = lit->code_min;
|
||||
code_max = lit->code_max;
|
||||
pos = lit->position;
|
||||
if (IS_EMPTY(lit))
|
||||
{
|
||||
fprintf(stream, "literal empty\n");
|
||||
}
|
||||
else if (IS_ASSERTION(lit))
|
||||
{
|
||||
int i;
|
||||
char *assertions[] = { "bol", "eol", "ctype", "!ctype",
|
||||
"bow", "eow", "wb", "!wb" };
|
||||
if (code_max >= ASSERT_LAST << 1)
|
||||
assert(0);
|
||||
fprintf(stream, "assertions: ");
|
||||
for (i = 0; (1 << i) <= ASSERT_LAST; i++)
|
||||
if (code_max & (1 << i))
|
||||
fprintf(stream, "%s ", assertions[i]);
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
else if (IS_TAG(lit))
|
||||
{
|
||||
fprintf(stream, "tag %d\n", code_max);
|
||||
}
|
||||
else if (IS_BACKREF(lit))
|
||||
{
|
||||
fprintf(stream, "backref %d, pos %d\n", code_max, pos);
|
||||
}
|
||||
else if (IS_PARAMETER(lit))
|
||||
{
|
||||
tre_print_params(lit->u.params);
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stream, "literal (%c, %c) (%d, %d), pos %d, sub %d, "
|
||||
"%d tags\n", code_min, code_max, code_min, code_max, pos,
|
||||
ast->submatch_id, num_tags);
|
||||
}
|
||||
break;
|
||||
case ITERATION:
|
||||
iter = ast->obj;
|
||||
fprintf(stream, "iteration {%d, %d}, sub %d, %d tags, %s\n",
|
||||
iter->min, iter->max, ast->submatch_id, num_tags,
|
||||
iter->minimal ? "minimal" : "greedy");
|
||||
tre_do_print(stream, iter->arg, indent + 2);
|
||||
break;
|
||||
case UNION:
|
||||
fprintf(stream, "union, sub %d, %d tags\n", ast->submatch_id, num_tags);
|
||||
tre_do_print(stream, ((tre_union_t *)ast->obj)->left, indent + 2);
|
||||
tre_do_print(stream, ((tre_union_t *)ast->obj)->right, indent + 2);
|
||||
break;
|
||||
case CATENATION:
|
||||
fprintf(stream, "catenation, sub %d, %d tags\n", ast->submatch_id,
|
||||
num_tags);
|
||||
tre_do_print(stream, ((tre_catenation_t *)ast->obj)->left, indent + 2);
|
||||
tre_do_print(stream, ((tre_catenation_t *)ast->obj)->right, indent + 2);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tre_ast_fprint(FILE *stream, tre_ast_node_t *ast)
|
||||
{
|
||||
tre_do_print(stream, ast, 0);
|
||||
}
|
||||
|
||||
void
|
||||
tre_ast_print(tre_ast_node_t *tree)
|
||||
{
|
||||
printf("AST:\n");
|
||||
tre_ast_fprint(stdout, tree);
|
||||
}
|
||||
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
tre-ast.h - Abstract syntax tree (AST) definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TRE_AST_H
|
||||
#define TRE_AST_H 1
|
||||
|
||||
#include "tre-mem.h"
|
||||
#include "tre-internal.h"
|
||||
#include "tre-compile.h"
|
||||
|
||||
/* The different AST node types. */
|
||||
typedef enum {
|
||||
LITERAL,
|
||||
CATENATION,
|
||||
ITERATION,
|
||||
UNION
|
||||
} tre_ast_type_t;
|
||||
|
||||
/* Special subtypes of TRE_LITERAL. */
|
||||
#define EMPTY -1 /* Empty leaf (denotes empty string). */
|
||||
#define ASSERTION -2 /* Assertion leaf. */
|
||||
#define TAG -3 /* Tag leaf. */
|
||||
#define BACKREF -4 /* Back reference leaf. */
|
||||
#define PARAMETER -5 /* Parameter. */
|
||||
|
||||
#define IS_SPECIAL(x) ((x)->code_min < 0)
|
||||
#define IS_EMPTY(x) ((x)->code_min == EMPTY)
|
||||
#define IS_ASSERTION(x) ((x)->code_min == ASSERTION)
|
||||
#define IS_TAG(x) ((x)->code_min == TAG)
|
||||
#define IS_BACKREF(x) ((x)->code_min == BACKREF)
|
||||
#define IS_PARAMETER(x) ((x)->code_min == PARAMETER)
|
||||
|
||||
|
||||
/* A generic AST node. All AST nodes consist of this node on the top
|
||||
level with `obj' pointing to the actual content. */
|
||||
typedef struct {
|
||||
tre_ast_type_t type; /* Type of the node. */
|
||||
void *obj; /* Pointer to actual node. */
|
||||
int nullable;
|
||||
int submatch_id;
|
||||
unsigned int num_submatches;
|
||||
unsigned int num_tags;
|
||||
tre_pos_and_tags_t *firstpos;
|
||||
tre_pos_and_tags_t *lastpos;
|
||||
} tre_ast_node_t;
|
||||
|
||||
|
||||
/* A "literal" node. These are created for assertions, back references,
|
||||
tags, matching parameter settings, and all expressions that match one
|
||||
character. */
|
||||
typedef struct {
|
||||
long code_min;
|
||||
long code_max;
|
||||
int position;
|
||||
union {
|
||||
tre_ctype_t class;
|
||||
int *params;
|
||||
} u;
|
||||
tre_ctype_t *neg_classes;
|
||||
} tre_literal_t;
|
||||
|
||||
/* A "catenation" node. These are created when two regexps are concatenated.
|
||||
If there are more than one subexpressions in sequence, the `left' part
|
||||
holds all but the last, and `right' part holds the last subexpression
|
||||
(catenation is left associative). */
|
||||
typedef struct {
|
||||
tre_ast_node_t *left;
|
||||
tre_ast_node_t *right;
|
||||
} tre_catenation_t;
|
||||
|
||||
/* An "iteration" node. These are created for the "*", "+", "?", and "{m,n}"
|
||||
operators. */
|
||||
typedef struct {
|
||||
/* Subexpression to match. */
|
||||
tre_ast_node_t *arg;
|
||||
/* Minimum number of consecutive matches. */
|
||||
int min;
|
||||
/* Maximum number of consecutive matches. */
|
||||
int max;
|
||||
/* If 0, match as many characters as possible, if 1 match as few as
|
||||
possible. Note that this does not always mean the same thing as
|
||||
matching as many/few repetitions as possible. */
|
||||
unsigned int minimal:1;
|
||||
/* Approximate matching parameters (or NULL). */
|
||||
int *params;
|
||||
} tre_iteration_t;
|
||||
|
||||
/* An "union" node. These are created for the "|" operator. */
|
||||
typedef struct {
|
||||
tre_ast_node_t *left;
|
||||
tre_ast_node_t *right;
|
||||
} tre_union_t;
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size);
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max);
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
|
||||
int minimal);
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
|
||||
|
||||
tre_ast_node_t *
|
||||
tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
|
||||
tre_ast_node_t *right);
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
void
|
||||
tre_ast_print(tre_ast_node_t *tree);
|
||||
|
||||
/* XXX - rethink AST printing API */
|
||||
void
|
||||
tre_print_params(int *params);
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
#endif /* TRE_AST_H */
|
||||
|
||||
/* EOF */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
tre-compile.h: Regex compilation definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TRE_COMPILE_H
|
||||
#define TRE_COMPILE_H 1
|
||||
|
||||
typedef struct {
|
||||
int position;
|
||||
int code_min;
|
||||
int code_max;
|
||||
int *tags;
|
||||
int assertions;
|
||||
tre_ctype_t class;
|
||||
tre_ctype_t *neg_classes;
|
||||
int backref;
|
||||
int *params;
|
||||
} tre_pos_and_tags_t;
|
||||
|
||||
#endif /* TRE_COMPILE_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Minimal TRE config for vendored use — no autoconf needed */
|
||||
#define TRE_VERSION "0.9.0"
|
||||
#define HAVE_STDLIB_H 1
|
||||
#define HAVE_STRING_H 1
|
||||
#define HAVE_WCHAR_H 1
|
||||
#define HAVE_WCTYPE_H 1
|
||||
#define HAVE_MBSTATE_T 1
|
||||
/* Do NOT define TRE_MULTIBYTE or TRE_WCHAR — we use single-byte char only.
|
||||
* #ifdef checks presence, not value, so defining as 0 still enables them. */
|
||||
/* #undef TRE_MULTIBYTE */
|
||||
/* #undef TRE_WCHAR */
|
||||
#define TRE_APPROX 0
|
||||
#define TRE_REGEX_T_FIELD value
|
||||
#define HAVE_MBRTOWC 1
|
||||
#define HAVE_MBTOWC 1
|
||||
#define HAVE_WCSLEN 1
|
||||
#define HAVE_ISASCII 1
|
||||
#define NDEBUG 1
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
tre-filter.c: Histogram filter to quickly find regexp match candidates
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
/* The idea of this filter is quite simple. First, let's assume the
|
||||
search pattern is a simple string. In order for a substring of a
|
||||
longer string to match the search pattern, it must have the same
|
||||
numbers of different characters as the pattern, and those
|
||||
characters must occur in the same order as they occur in pattern. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#include <stdio.h>
|
||||
#include "tre-internal.h"
|
||||
#include "tre-filter.h"
|
||||
|
||||
int
|
||||
tre_filter_find(const unsigned char *str, size_t len, tre_filter_t *filter)
|
||||
{
|
||||
unsigned short counts[256];
|
||||
unsigned int i;
|
||||
unsigned int window_len = filter->window_len;
|
||||
tre_filter_profile_t *profile = filter->profile;
|
||||
const unsigned char *str_orig = str;
|
||||
|
||||
DPRINT(("tre_filter_find: %.*s\n", len, str));
|
||||
|
||||
for (i = 0; i < elementsof(counts); i++)
|
||||
counts[i] = 0;
|
||||
|
||||
i = 0;
|
||||
while (*str && i < window_len && i < len)
|
||||
{
|
||||
counts[*str]++;
|
||||
i++;
|
||||
str++;
|
||||
len--;
|
||||
}
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
tre_filter_profile_t *p;
|
||||
counts[*str]++;
|
||||
counts[*(str - window_len)]--;
|
||||
|
||||
p = profile;
|
||||
while (p->ch)
|
||||
{
|
||||
if (counts[p->ch] < p->count)
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
if (!p->ch)
|
||||
{
|
||||
DPRINT(("Found possible match at %d\n",
|
||||
str - str_orig));
|
||||
return str - str_orig;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT(("No match so far...\n"));
|
||||
}
|
||||
len--;
|
||||
str++;
|
||||
}
|
||||
DPRINT(("This string cannot match.\n"));
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned char ch;
|
||||
unsigned char count;
|
||||
} tre_filter_profile_t;
|
||||
|
||||
typedef struct {
|
||||
/* Length of the window where the character counts are kept. */
|
||||
int window_len;
|
||||
/* Required character counts table. */
|
||||
tre_filter_profile_t *profile;
|
||||
} tre_filter_t;
|
||||
|
||||
|
||||
int
|
||||
tre_filter_find(const unsigned char *str, size_t len, tre_filter_t *filter);
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
tre-internal.h - TRE internal definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TRE_INTERNAL_H
|
||||
#define TRE_INTERNAL_H 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <basetsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#else
|
||||
#include <sys/types.h> /* ssize_t */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
|
||||
#ifdef HAVE_WCTYPE_H
|
||||
#include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H */
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif /* HAVE_SYS_TYPES_H */
|
||||
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include "tre.h"
|
||||
|
||||
#define TRE_MAX_RE 65536
|
||||
#define TRE_MAX_STRING INT_MAX
|
||||
#define TRE_MAX_STACK 1048576
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
#include <stdio.h>
|
||||
#define DPRINT(msg) do {printf msg; fflush(stdout);} while(/*CONSTCOND*/(void)0,0)
|
||||
#else /* !TRE_DEBUG */
|
||||
#define DPRINT(msg) do { } while(/*CONSTCOND*/(void)0,0)
|
||||
#endif /* !TRE_DEBUG */
|
||||
|
||||
#define elementsof(x) ( sizeof(x) / sizeof(x[0]) )
|
||||
|
||||
#ifdef HAVE_MBRTOWC
|
||||
#define tre_mbrtowc(pwc, s, n, ps) (mbrtowc((pwc), (s), (n), (ps)))
|
||||
#else /* !HAVE_MBRTOWC */
|
||||
#ifdef HAVE_MBTOWC
|
||||
#define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n)))
|
||||
#endif /* HAVE_MBTOWC */
|
||||
#endif /* !HAVE_MBRTOWC */
|
||||
|
||||
#ifdef TRE_MULTIBYTE
|
||||
#ifdef HAVE_MBSTATE_T
|
||||
#define TRE_MBSTATE
|
||||
#endif /* TRE_MULTIBYTE */
|
||||
#endif /* HAVE_MBSTATE_T */
|
||||
|
||||
/* Define the character types and functions. */
|
||||
#ifdef TRE_WCHAR
|
||||
|
||||
/* Wide characters. */
|
||||
typedef wint_t tre_cint_t;
|
||||
#if WCHAR_MAX <= INT_MAX
|
||||
#define TRE_CHAR_MAX WCHAR_MAX
|
||||
#else /* WCHAR_MAX > INT_MAX */
|
||||
#define TRE_CHAR_MAX INT_MAX
|
||||
#endif
|
||||
|
||||
#ifdef TRE_MULTIBYTE
|
||||
#define TRE_MB_CUR_MAX MB_CUR_MAX
|
||||
#else /* !TRE_MULTIBYTE */
|
||||
#define TRE_MB_CUR_MAX 1
|
||||
#endif /* !TRE_MULTIBYTE */
|
||||
|
||||
#define tre_isalnum iswalnum
|
||||
#define tre_isalpha iswalpha
|
||||
#ifdef HAVE_ISWBLANK
|
||||
#define tre_isblank iswblank
|
||||
#endif /* HAVE_ISWBLANK */
|
||||
#define tre_iscntrl iswcntrl
|
||||
#define tre_isdigit iswdigit
|
||||
#define tre_isgraph iswgraph
|
||||
#define tre_islower iswlower
|
||||
#define tre_isprint iswprint
|
||||
#define tre_ispunct iswpunct
|
||||
#define tre_isspace iswspace
|
||||
#define tre_isupper iswupper
|
||||
#define tre_isxdigit iswxdigit
|
||||
|
||||
#define tre_tolower towlower
|
||||
#define tre_toupper towupper
|
||||
#define tre_strlen wcslen
|
||||
|
||||
#else /* !TRE_WCHAR */
|
||||
|
||||
/* 8 bit characters. */
|
||||
typedef short tre_cint_t;
|
||||
#define TRE_CHAR_MAX 255
|
||||
#define TRE_MB_CUR_MAX 1
|
||||
|
||||
#define tre_isalnum isalnum
|
||||
#define tre_isalpha isalpha
|
||||
#ifdef HAVE_ISASCII
|
||||
#define tre_isascii isascii
|
||||
#endif /* HAVE_ISASCII */
|
||||
#ifdef HAVE_ISBLANK
|
||||
#define tre_isblank isblank
|
||||
#endif /* HAVE_ISBLANK */
|
||||
#define tre_iscntrl iscntrl
|
||||
#define tre_isdigit isdigit
|
||||
#define tre_isgraph isgraph
|
||||
#define tre_islower islower
|
||||
#define tre_isprint isprint
|
||||
#define tre_ispunct ispunct
|
||||
#define tre_isspace isspace
|
||||
#define tre_isupper isupper
|
||||
#define tre_isxdigit isxdigit
|
||||
|
||||
#define tre_tolower(c) (tre_cint_t)(tolower(c))
|
||||
#define tre_toupper(c) (tre_cint_t)(toupper(c))
|
||||
#define tre_strlen(s) (strlen((const char*)s))
|
||||
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
#if defined(TRE_WCHAR) && defined(HAVE_ISWCTYPE) && defined(HAVE_WCTYPE)
|
||||
#define TRE_USE_SYSTEM_WCTYPE 1
|
||||
#endif
|
||||
|
||||
#ifdef TRE_USE_SYSTEM_WCTYPE
|
||||
/* Use system provided iswctype() and wctype(). */
|
||||
typedef wctype_t tre_ctype_t;
|
||||
#define tre_isctype iswctype
|
||||
#define tre_ctype wctype
|
||||
#else /* !TRE_USE_SYSTEM_WCTYPE */
|
||||
/* Define our own versions of iswctype() and wctype(). */
|
||||
typedef int (*tre_ctype_t)(tre_cint_t);
|
||||
#define tre_isctype(c, type) ( (type)(c) )
|
||||
tre_ctype_t tre_ctype(const char *name);
|
||||
#endif /* !TRE_USE_SYSTEM_WCTYPE */
|
||||
|
||||
typedef enum { STR_WIDE, STR_BYTE, STR_MBS, STR_USER } tre_str_type_t;
|
||||
|
||||
/* Returns number of bytes to add to (char *)ptr to make it
|
||||
properly aligned for the type. */
|
||||
#define ALIGN(ptr, type) \
|
||||
((((long)ptr) % sizeof(type)) \
|
||||
? (sizeof(type) - (((long)ptr) % sizeof(type))) \
|
||||
: 0)
|
||||
|
||||
#undef MAX
|
||||
#undef MIN
|
||||
#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
|
||||
#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
|
||||
|
||||
/* Define STRF to the correct printf formatter for strings. */
|
||||
#ifdef TRE_WCHAR
|
||||
#define STRF "ls"
|
||||
#else /* !TRE_WCHAR */
|
||||
#define STRF "s"
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
/* TNFA transition type. A TNFA state is an array of transitions,
|
||||
the terminator is a transition with NULL `state'. */
|
||||
typedef struct tnfa_transition tre_tnfa_transition_t;
|
||||
|
||||
struct tnfa_transition {
|
||||
/* Range of accepted characters. */
|
||||
tre_cint_t code_min;
|
||||
tre_cint_t code_max;
|
||||
/* Pointer to the destination state. */
|
||||
tre_tnfa_transition_t *state;
|
||||
/* ID number of the destination state. */
|
||||
int state_id;
|
||||
/* -1 terminated array of tags (or NULL). */
|
||||
int *tags;
|
||||
/* Matching parameters settings (or NULL). */
|
||||
int *params;
|
||||
/* Assertion bitmap. */
|
||||
int assertions;
|
||||
/* Assertion parameters. */
|
||||
union {
|
||||
/* Character class assertion. */
|
||||
tre_ctype_t class;
|
||||
/* Back reference assertion. */
|
||||
int backref;
|
||||
} u;
|
||||
/* Negative character class assertions. */
|
||||
tre_ctype_t *neg_classes;
|
||||
};
|
||||
|
||||
|
||||
/* Assertions. */
|
||||
#define ASSERT_AT_BOL 1 /* Beginning of line. */
|
||||
#define ASSERT_AT_EOL 2 /* End of line. */
|
||||
#define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */
|
||||
#define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */
|
||||
#define ASSERT_AT_BOW 16 /* Beginning of word. */
|
||||
#define ASSERT_AT_EOW 32 /* End of word. */
|
||||
#define ASSERT_AT_WB 64 /* Word boundary. */
|
||||
#define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */
|
||||
#define ASSERT_BACKREF 256 /* A back reference in `backref'. */
|
||||
#define ASSERT_LAST 256
|
||||
|
||||
/* Tag directions. */
|
||||
typedef enum {
|
||||
TRE_TAG_MINIMIZE = 0,
|
||||
TRE_TAG_MAXIMIZE = 1
|
||||
} tre_tag_direction_t;
|
||||
|
||||
/* Parameters that can be changed dynamically while matching. */
|
||||
typedef enum {
|
||||
TRE_PARAM_COST_INS = 0,
|
||||
TRE_PARAM_COST_DEL = 1,
|
||||
TRE_PARAM_COST_SUBST = 2,
|
||||
TRE_PARAM_COST_MAX = 3,
|
||||
TRE_PARAM_MAX_INS = 4,
|
||||
TRE_PARAM_MAX_DEL = 5,
|
||||
TRE_PARAM_MAX_SUBST = 6,
|
||||
TRE_PARAM_MAX_ERR = 7,
|
||||
TRE_PARAM_DEPTH = 8,
|
||||
TRE_PARAM_LAST = 9
|
||||
} tre_param_t;
|
||||
|
||||
/* Unset matching parameter */
|
||||
#define TRE_PARAM_UNSET -1
|
||||
|
||||
/* Signifies the default matching parameter value. */
|
||||
#define TRE_PARAM_DEFAULT -2
|
||||
|
||||
/* Instructions to compute submatch register values from tag values
|
||||
after a successful match. */
|
||||
struct tre_submatch_data {
|
||||
/* Tag that gives the value for rm_so (submatch start offset). */
|
||||
int so_tag;
|
||||
/* Tag that gives the value for rm_eo (submatch end offset). */
|
||||
int eo_tag;
|
||||
/* List of submatches this submatch is contained in. */
|
||||
int *parents;
|
||||
};
|
||||
|
||||
typedef struct tre_submatch_data tre_submatch_data_t;
|
||||
|
||||
|
||||
/* TNFA definition. */
|
||||
typedef struct tnfa tre_tnfa_t;
|
||||
|
||||
struct tnfa {
|
||||
tre_tnfa_transition_t *transitions;
|
||||
unsigned int num_transitions;
|
||||
tre_tnfa_transition_t *initial;
|
||||
tre_tnfa_transition_t *final;
|
||||
tre_submatch_data_t *submatch_data;
|
||||
char *firstpos_chars;
|
||||
int first_char;
|
||||
unsigned int num_submatches;
|
||||
tre_tag_direction_t *tag_directions;
|
||||
int *minimal_tags;
|
||||
int num_tags;
|
||||
int num_minimals;
|
||||
int end_tag;
|
||||
int num_states;
|
||||
int cflags;
|
||||
int have_backrefs;
|
||||
int have_approx;
|
||||
int params_depth;
|
||||
};
|
||||
|
||||
int
|
||||
tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags);
|
||||
|
||||
void
|
||||
tre_free(regex_t *preg);
|
||||
|
||||
void
|
||||
tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
|
||||
const tre_tnfa_t *tnfa, int *tags, int match_eo);
|
||||
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, int *match_tags, int eflags,
|
||||
int *match_end_ofs);
|
||||
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, int *match_tags, int eflags,
|
||||
int *match_end_ofs);
|
||||
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, int *match_tags, int eflags,
|
||||
int *match_end_ofs);
|
||||
|
||||
#ifdef TRE_APPROX
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, int *match_tags, regamatch_t *match,
|
||||
regaparams_t params, int eflags, int *match_end_ofs);
|
||||
#endif /* TRE_APPROX */
|
||||
|
||||
#endif /* TRE_INTERNAL_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,819 @@
|
||||
/*
|
||||
tre-match-approx.c - TRE approximate regex matching engine
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
#ifndef __GNUC__
|
||||
# if HAVE_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
char *alloca ();
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#endif /* TRE_USE_ALLOCA */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
#ifdef HAVE_WCTYPE_H
|
||||
#include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H */
|
||||
#ifndef TRE_WCHAR
|
||||
#include <ctype.h>
|
||||
#endif /* !TRE_WCHAR */
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif /* HAVE_MALLOC_H */
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "tre-match-utils.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
#define TRE_M_COST 0
|
||||
#define TRE_M_NUM_INS 1
|
||||
#define TRE_M_NUM_DEL 2
|
||||
#define TRE_M_NUM_SUBST 3
|
||||
#define TRE_M_NUM_ERR 4
|
||||
#define TRE_M_LAST 5
|
||||
|
||||
#define TRE_M_MAX_DEPTH 3
|
||||
|
||||
typedef struct {
|
||||
/* State in the TNFA transition table. */
|
||||
tre_tnfa_transition_t *state;
|
||||
/* Position in input string. */
|
||||
int pos;
|
||||
/* Tag values. */
|
||||
int *tags;
|
||||
/* Matching parameters. */
|
||||
regaparams_t params;
|
||||
/* Nesting depth of parameters. This is used as an index in
|
||||
the `costs' array. */
|
||||
int depth;
|
||||
/* Costs and counter values for different parameter nesting depths. */
|
||||
int costs[TRE_M_MAX_DEPTH + 1][TRE_M_LAST];
|
||||
} tre_tnfa_approx_reach_t;
|
||||
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
/* Prints the `reach' array in a readable fashion with DPRINT. */
|
||||
static void
|
||||
tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_approx_reach_t *reach,
|
||||
int pos, int num_tags)
|
||||
{
|
||||
int id;
|
||||
|
||||
/* Print each state on one line. */
|
||||
DPRINT((" reach:\n"));
|
||||
for (id = 0; id < tnfa->num_states; id++)
|
||||
{
|
||||
int i, j;
|
||||
if (reach[id].pos < pos)
|
||||
continue; /* Not reached. */
|
||||
DPRINT((" %03d, costs ", id));
|
||||
for (i = 0; i <= reach[id].depth; i++)
|
||||
{
|
||||
DPRINT(("["));
|
||||
for (j = 0; j < TRE_M_LAST; j++)
|
||||
{
|
||||
DPRINT(("%2d", reach[id].costs[i][j]));
|
||||
if (j + 1 < TRE_M_LAST)
|
||||
DPRINT((","));
|
||||
}
|
||||
DPRINT(("]"));
|
||||
if (i + 1 <= reach[id].depth)
|
||||
DPRINT((", "));
|
||||
}
|
||||
DPRINT(("\n tags "));
|
||||
for (i = 0; i < num_tags; i++)
|
||||
{
|
||||
DPRINT(("%02d", reach[id].tags[i]));
|
||||
if (i + 1 < num_tags)
|
||||
DPRINT((","));
|
||||
}
|
||||
DPRINT(("\n"));
|
||||
}
|
||||
DPRINT(("\n"));
|
||||
}
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
|
||||
/* Sets the matching parameters in `reach' to the ones defined in the `pa'
|
||||
array. If `pa' specifies default values, they are taken from
|
||||
`default_params'. */
|
||||
inline static void
|
||||
tre_set_params(tre_tnfa_approx_reach_t *reach,
|
||||
int *pa, regaparams_t default_params)
|
||||
{
|
||||
int value;
|
||||
|
||||
/* If depth is increased reset costs and counters to zero for the
|
||||
new levels. */
|
||||
value = pa[TRE_PARAM_DEPTH];
|
||||
assert(value <= TRE_M_MAX_DEPTH);
|
||||
if (value > reach->depth)
|
||||
{
|
||||
int i, j;
|
||||
for (i = reach->depth + 1; i <= value; i++)
|
||||
for (j = 0; j < TRE_M_LAST; j++)
|
||||
reach->costs[i][j] = 0;
|
||||
}
|
||||
reach->depth = value;
|
||||
|
||||
/* Set insert cost. */
|
||||
value = pa[TRE_PARAM_COST_INS];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.cost_ins = default_params.cost_ins;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.cost_ins = value;
|
||||
|
||||
/* Set delete cost. */
|
||||
value = pa[TRE_PARAM_COST_DEL];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.cost_del = default_params.cost_del;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.cost_del = value;
|
||||
|
||||
/* Set substitute cost. */
|
||||
value = pa[TRE_PARAM_COST_SUBST];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.cost_subst = default_params.cost_subst;
|
||||
else
|
||||
reach->params.cost_subst = value;
|
||||
|
||||
/* Set maximum cost. */
|
||||
value = pa[TRE_PARAM_COST_MAX];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.max_cost = default_params.max_cost;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.max_cost = value;
|
||||
|
||||
/* Set maximum inserts. */
|
||||
value = pa[TRE_PARAM_MAX_INS];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.max_ins = default_params.max_ins;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.max_ins = value;
|
||||
|
||||
/* Set maximum deletes. */
|
||||
value = pa[TRE_PARAM_MAX_DEL];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.max_del = default_params.max_del;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.max_del = value;
|
||||
|
||||
/* Set maximum substitutes. */
|
||||
value = pa[TRE_PARAM_MAX_SUBST];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.max_subst = default_params.max_subst;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.max_subst = value;
|
||||
|
||||
/* Set maximum number of errors. */
|
||||
value = pa[TRE_PARAM_MAX_ERR];
|
||||
if (value == TRE_PARAM_DEFAULT)
|
||||
reach->params.max_err = default_params.max_err;
|
||||
else if (value != TRE_PARAM_UNSET)
|
||||
reach->params.max_err = value;
|
||||
}
|
||||
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, int *match_tags,
|
||||
regamatch_t *match, regaparams_t default_params,
|
||||
int eflags, int *match_end_ofs)
|
||||
{
|
||||
/* State variables required by GET_NEXT_WCHAR. */
|
||||
tre_char_t prev_c = 0, next_c = 0;
|
||||
const char *str_byte = string;
|
||||
ssize_t pos = -1;
|
||||
unsigned int pos_add_next = 1;
|
||||
#ifdef TRE_WCHAR
|
||||
const wchar_t *str_wide = string;
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate_t mbstate;
|
||||
#endif /* !TRE_WCHAR */
|
||||
#endif /* TRE_WCHAR */
|
||||
reg_errcode_t ret;
|
||||
int reg_notbol = eflags & REG_NOTBOL;
|
||||
int reg_noteol = eflags & REG_NOTEOL;
|
||||
int reg_newline = tnfa->cflags & REG_NEWLINE;
|
||||
int str_user_end = 0;
|
||||
|
||||
int prev_pos;
|
||||
|
||||
/* Number of tags. */
|
||||
int num_tags;
|
||||
/* The reach tables. */
|
||||
tre_tnfa_approx_reach_t *reach, *reach_next;
|
||||
/* Tag array for temporary use. */
|
||||
int *tmp_tags;
|
||||
|
||||
/* End offset of best match so far, or -1 if no match found yet. */
|
||||
int match_eo = -1;
|
||||
/* Costs of the match. */
|
||||
int match_costs[TRE_M_LAST];
|
||||
|
||||
/* Space for temporary data required for matching. */
|
||||
unsigned char *buf;
|
||||
|
||||
int i, id;
|
||||
|
||||
/*
|
||||
* TRE internals tend to use int instead of size_t for positions or
|
||||
* lengths and don't check for overflow. This will take time to fix
|
||||
* properly. In the meantime, simply limit the input to what we can
|
||||
* handle.
|
||||
*/
|
||||
if (len > TRE_MAX_STRING)
|
||||
len = TRE_MAX_STRING;
|
||||
|
||||
#ifdef TRE_MBSTATE
|
||||
memset(&mbstate, '\0', sizeof(mbstate));
|
||||
#endif /* TRE_MBSTATE */
|
||||
|
||||
DPRINT(("tre_tnfa_run_approx, input type %d, len %zd, eflags %d, "
|
||||
"match_tags %p\n",
|
||||
type, len, eflags,
|
||||
match_tags));
|
||||
DPRINT(("max cost %d, ins %d, del %d, subst %d\n",
|
||||
default_params.max_cost,
|
||||
default_params.cost_ins,
|
||||
default_params.cost_del,
|
||||
default_params.cost_subst));
|
||||
|
||||
if (!match_tags)
|
||||
num_tags = 0;
|
||||
else
|
||||
num_tags = tnfa->num_tags;
|
||||
|
||||
/* Allocate memory for temporary data required for matching. This needs to
|
||||
be done for every matching operation to be thread safe. This allocates
|
||||
everything in a single large block from the stack frame using alloca()
|
||||
or with malloc() if alloca is unavailable. */
|
||||
{
|
||||
unsigned char *buf_cursor;
|
||||
/* Space needed for one array of tags. */
|
||||
int tag_bytes = sizeof(*tmp_tags) * num_tags;
|
||||
/* Space needed for one reach table. */
|
||||
int reach_bytes = sizeof(*reach_next) * tnfa->num_states;
|
||||
/* Total space needed. */
|
||||
int total_bytes = reach_bytes * 2 + (tnfa->num_states * 2 + 1 ) * tag_bytes;
|
||||
/* Add some extra to make sure we can align the pointers. The multiplier
|
||||
used here must be equal to the number of ALIGN calls below. */
|
||||
total_bytes += (sizeof(long) - 1) * 3;
|
||||
|
||||
/* Allocate the memory. */
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
buf = alloca(total_bytes);
|
||||
#else /* !TRE_USE_ALLOCA */
|
||||
buf = xmalloc((unsigned)total_bytes);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
if (!buf)
|
||||
return REG_ESPACE;
|
||||
memset(buf, 0, (size_t)total_bytes);
|
||||
|
||||
/* Allocate `tmp_tags' from `buf'. */
|
||||
tmp_tags = (void *)buf;
|
||||
buf_cursor = buf + tag_bytes;
|
||||
buf_cursor += ALIGN(buf_cursor, long);
|
||||
|
||||
/* Allocate `reach' from `buf'. */
|
||||
reach = (void *)buf_cursor;
|
||||
buf_cursor += reach_bytes;
|
||||
buf_cursor += ALIGN(buf_cursor, long);
|
||||
|
||||
/* Allocate `reach_next' from `buf'. */
|
||||
reach_next = (void *)buf_cursor;
|
||||
buf_cursor += reach_bytes;
|
||||
buf_cursor += ALIGN(buf_cursor, long);
|
||||
|
||||
/* Allocate tag arrays for `reach' and `reach_next' from `buf'. */
|
||||
for (i = 0; i < tnfa->num_states; i++)
|
||||
{
|
||||
reach[i].tags = (void *)buf_cursor;
|
||||
buf_cursor += tag_bytes;
|
||||
reach_next[i].tags = (void *)buf_cursor;
|
||||
buf_cursor += tag_bytes;
|
||||
}
|
||||
assert(buf_cursor <= buf + total_bytes);
|
||||
}
|
||||
|
||||
for (i = 0; i < TRE_M_LAST; i++)
|
||||
match_costs[i] = INT_MAX;
|
||||
|
||||
/* Mark the reach arrays empty. */
|
||||
for (i = 0; i < tnfa->num_states; i++)
|
||||
reach[i].pos = reach_next[i].pos = -2;
|
||||
|
||||
prev_pos = pos;
|
||||
GET_NEXT_WCHAR();
|
||||
pos = 0;
|
||||
|
||||
while (/*CONSTCOND*/(void)1,1)
|
||||
{
|
||||
DPRINT(("%03zd:%2lc/%05d\n", pos, (tre_cint_t)next_c, (int)next_c));
|
||||
|
||||
/* Add initial states to `reach_next' if an exact match has not yet
|
||||
been found. */
|
||||
if (match_costs[TRE_M_COST] > 0)
|
||||
{
|
||||
tre_tnfa_transition_t *trans;
|
||||
DPRINT((" init"));
|
||||
for (trans = tnfa->initial; trans->state; trans++)
|
||||
{
|
||||
int stateid = trans->state_id;
|
||||
|
||||
/* If this state is not currently in `reach_next', add it
|
||||
there. */
|
||||
if (reach_next[stateid].pos < pos)
|
||||
{
|
||||
if (trans->assertions && CHECK_ASSERTIONS(trans->assertions))
|
||||
{
|
||||
/* Assertions failed, don't add this state. */
|
||||
DPRINT((" !%d (assert)", stateid));
|
||||
continue;
|
||||
}
|
||||
DPRINT((" %d", stateid));
|
||||
reach_next[stateid].state = trans->state;
|
||||
reach_next[stateid].pos = pos;
|
||||
|
||||
/* Compute tag values after this transition. */
|
||||
for (i = 0; i < num_tags; i++)
|
||||
reach_next[stateid].tags[i] = -1;
|
||||
|
||||
if (trans->tags)
|
||||
for (i = 0; trans->tags[i] >= 0; i++)
|
||||
if (trans->tags[i] < num_tags)
|
||||
reach_next[stateid].tags[trans->tags[i]] = pos;
|
||||
|
||||
/* Set the parameters, depth, and costs. */
|
||||
reach_next[stateid].params = default_params;
|
||||
reach_next[stateid].depth = 0;
|
||||
for (i = 0; i < TRE_M_LAST; i++)
|
||||
reach_next[stateid].costs[0][i] = 0;
|
||||
if (trans->params)
|
||||
tre_set_params(&reach_next[stateid], trans->params,
|
||||
default_params);
|
||||
|
||||
/* If this is the final state, mark the exact match. */
|
||||
if (trans->state == tnfa->final)
|
||||
{
|
||||
match_eo = pos;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
match_tags[i] = reach_next[stateid].tags[i];
|
||||
for (i = 0; i < TRE_M_LAST; i++)
|
||||
match_costs[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
DPRINT(("\n"));
|
||||
}
|
||||
|
||||
|
||||
/* Handle inserts. This is done by pretending there's an epsilon
|
||||
transition from each state in `reach' back to the same state.
|
||||
We don't need to worry about the final state here; this will never
|
||||
give a better match than what we already have. */
|
||||
for (id = 0; id < tnfa->num_states; id++)
|
||||
{
|
||||
int depth;
|
||||
int cost, cost0;
|
||||
|
||||
if (reach[id].pos != prev_pos)
|
||||
{
|
||||
DPRINT((" insert: %d not reached\n", id));
|
||||
continue; /* Not reached. */
|
||||
}
|
||||
|
||||
depth = reach[id].depth;
|
||||
|
||||
/* Compute and check cost at current depth. */
|
||||
cost = reach[id].costs[depth][TRE_M_COST];
|
||||
if (reach[id].params.cost_ins != TRE_PARAM_UNSET)
|
||||
cost += reach[id].params.cost_ins;
|
||||
if (cost > reach[id].params.max_cost)
|
||||
continue; /* Cost too large. */
|
||||
|
||||
/* Check number of inserts at current depth. */
|
||||
if (reach[id].costs[depth][TRE_M_NUM_INS] + 1
|
||||
> reach[id].params.max_ins)
|
||||
continue; /* Too many inserts. */
|
||||
|
||||
/* Check total number of errors at current depth. */
|
||||
if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1
|
||||
> reach[id].params.max_err)
|
||||
continue; /* Too many errors. */
|
||||
|
||||
/* Compute overall cost. */
|
||||
cost0 = cost;
|
||||
if (depth > 0)
|
||||
{
|
||||
cost0 = reach[id].costs[0][TRE_M_COST];
|
||||
if (reach[id].params.cost_ins != TRE_PARAM_UNSET)
|
||||
cost0 += reach[id].params.cost_ins;
|
||||
else
|
||||
cost0 += default_params.cost_ins;
|
||||
}
|
||||
|
||||
DPRINT((" insert: from %d to %d, cost %d: ", id, id,
|
||||
reach[id].costs[depth][TRE_M_COST]));
|
||||
if (reach_next[id].pos == pos
|
||||
&& (cost0 >= reach_next[id].costs[0][TRE_M_COST]))
|
||||
{
|
||||
DPRINT(("lose\n"));
|
||||
continue;
|
||||
}
|
||||
DPRINT(("win\n"));
|
||||
|
||||
/* Copy state, position, tags, parameters, and depth. */
|
||||
reach_next[id].state = reach[id].state;
|
||||
reach_next[id].pos = pos;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
reach_next[id].tags[i] = reach[id].tags[i];
|
||||
reach_next[id].params = reach[id].params;
|
||||
reach_next[id].depth = reach[id].depth;
|
||||
|
||||
/* Set the costs after this transition. */
|
||||
memcpy(reach_next[id].costs, reach[id].costs,
|
||||
sizeof(reach_next[id].costs[0][0])
|
||||
* TRE_M_LAST * (depth + 1));
|
||||
reach_next[id].costs[depth][TRE_M_COST] = cost;
|
||||
reach_next[id].costs[depth][TRE_M_NUM_INS]++;
|
||||
reach_next[id].costs[depth][TRE_M_NUM_ERR]++;
|
||||
if (depth > 0)
|
||||
{
|
||||
reach_next[id].costs[0][TRE_M_COST] = cost0;
|
||||
reach_next[id].costs[0][TRE_M_NUM_INS]++;
|
||||
reach_next[id].costs[0][TRE_M_NUM_ERR]++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Handle deletes. This is done by traversing through the whole TNFA
|
||||
pretending that all transitions are epsilon transitions, until
|
||||
no more states can be reached with better costs. */
|
||||
{
|
||||
/* XXX - dynamic ringbuffer size */
|
||||
tre_tnfa_approx_reach_t *ringbuffer[512];
|
||||
tre_tnfa_approx_reach_t **deque_start, **deque_end;
|
||||
|
||||
deque_start = deque_end = ringbuffer;
|
||||
|
||||
/* Add all states in `reach_next' to the deque. */
|
||||
for (id = 0; id < tnfa->num_states; id++)
|
||||
{
|
||||
if (reach_next[id].pos != pos)
|
||||
continue;
|
||||
*deque_end = &reach_next[id];
|
||||
deque_end++;
|
||||
assert(deque_end != deque_start);
|
||||
}
|
||||
|
||||
/* Repeat until the deque is empty. */
|
||||
while (deque_end != deque_start)
|
||||
{
|
||||
tre_tnfa_approx_reach_t *reach_p;
|
||||
int depth;
|
||||
int cost, cost0;
|
||||
tre_tnfa_transition_t *trans;
|
||||
|
||||
/* Pop the first item off the deque. */
|
||||
reach_p = *deque_start;
|
||||
id = reach_p - reach_next;
|
||||
depth = reach_p->depth;
|
||||
|
||||
/* Compute cost at current depth. */
|
||||
cost = reach_p->costs[depth][TRE_M_COST];
|
||||
if (reach_p->params.cost_del != TRE_PARAM_UNSET)
|
||||
cost += reach_p->params.cost_del;
|
||||
|
||||
/* Check cost, number of deletes, and total number of errors
|
||||
at current depth. */
|
||||
if (cost > reach_p->params.max_cost
|
||||
|| (reach_p->costs[depth][TRE_M_NUM_DEL] + 1
|
||||
> reach_p->params.max_del)
|
||||
|| (reach_p->costs[depth][TRE_M_NUM_ERR] + 1
|
||||
> reach_p->params.max_err))
|
||||
{
|
||||
/* Too many errors or cost too large. */
|
||||
DPRINT((" delete: from %03d: cost too large\n", id));
|
||||
deque_start++;
|
||||
if (deque_start >= (ringbuffer + 512))
|
||||
deque_start = ringbuffer;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute overall cost. */
|
||||
cost0 = cost;
|
||||
if (depth > 0)
|
||||
{
|
||||
cost0 = reach_p->costs[0][TRE_M_COST];
|
||||
if (reach_p->params.cost_del != TRE_PARAM_UNSET)
|
||||
cost0 += reach_p->params.cost_del;
|
||||
else
|
||||
cost0 += default_params.cost_del;
|
||||
}
|
||||
|
||||
for (trans = reach_p->state; trans->state; trans++)
|
||||
{
|
||||
int dest_id = trans->state_id;
|
||||
DPRINT((" delete: from %03d to %03d, cost %d (%d): ",
|
||||
id, dest_id, cost0, reach_p->params.max_cost));
|
||||
|
||||
if (trans->assertions && CHECK_ASSERTIONS(trans->assertions))
|
||||
{
|
||||
DPRINT(("assertion failed\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute tag values after this transition. */
|
||||
for (i = 0; i < num_tags; i++)
|
||||
tmp_tags[i] = reach_p->tags[i];
|
||||
if (trans->tags)
|
||||
for (i = 0; trans->tags[i] >= 0; i++)
|
||||
if (trans->tags[i] < num_tags)
|
||||
tmp_tags[trans->tags[i]] = pos;
|
||||
|
||||
/* If another path has also reached this state, choose the one
|
||||
with the smallest cost or best tags if costs are equal. */
|
||||
if (reach_next[dest_id].pos == pos
|
||||
&& (cost0 > reach_next[dest_id].costs[0][TRE_M_COST]
|
||||
|| (cost0 == reach_next[dest_id].costs[0][TRE_M_COST]
|
||||
&& (!match_tags
|
||||
|| !tre_tag_order(num_tags,
|
||||
tnfa->tag_directions,
|
||||
tmp_tags,
|
||||
reach_next[dest_id].tags)))))
|
||||
{
|
||||
DPRINT(("lose, cost0 %d, have %d\n",
|
||||
cost0, reach_next[dest_id].costs[0][TRE_M_COST]));
|
||||
continue;
|
||||
}
|
||||
DPRINT(("win\n"));
|
||||
|
||||
/* Set state, position, tags, parameters, depth, and costs. */
|
||||
reach_next[dest_id].state = trans->state;
|
||||
reach_next[dest_id].pos = pos;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
reach_next[dest_id].tags[i] = tmp_tags[i];
|
||||
|
||||
reach_next[dest_id].params = reach_p->params;
|
||||
if (trans->params)
|
||||
tre_set_params(&reach_next[dest_id], trans->params,
|
||||
default_params);
|
||||
|
||||
reach_next[dest_id].depth = reach_p->depth;
|
||||
memcpy(&reach_next[dest_id].costs,
|
||||
reach_p->costs,
|
||||
sizeof(reach_p->costs[0][0])
|
||||
* TRE_M_LAST * (depth + 1));
|
||||
reach_next[dest_id].costs[depth][TRE_M_COST] = cost;
|
||||
reach_next[dest_id].costs[depth][TRE_M_NUM_DEL]++;
|
||||
reach_next[dest_id].costs[depth][TRE_M_NUM_ERR]++;
|
||||
if (depth > 0)
|
||||
{
|
||||
reach_next[dest_id].costs[0][TRE_M_COST] = cost0;
|
||||
reach_next[dest_id].costs[0][TRE_M_NUM_DEL]++;
|
||||
reach_next[dest_id].costs[0][TRE_M_NUM_ERR]++;
|
||||
}
|
||||
|
||||
if (trans->state == tnfa->final
|
||||
&& (match_eo < 0
|
||||
|| match_costs[TRE_M_COST] > cost0
|
||||
|| (match_costs[TRE_M_COST] == cost0
|
||||
&& (num_tags > 0
|
||||
&& tmp_tags[0] <= match_tags[0]))))
|
||||
{
|
||||
DPRINT((" setting new match at %zd, cost %d\n",
|
||||
pos, cost0));
|
||||
match_eo = pos;
|
||||
memcpy(match_costs, reach_next[dest_id].costs[0],
|
||||
sizeof(match_costs[0]) * TRE_M_LAST);
|
||||
for (i = 0; i < num_tags; i++)
|
||||
match_tags[i] = tmp_tags[i];
|
||||
}
|
||||
|
||||
/* Add to the end of the deque. */
|
||||
*deque_end = &reach_next[dest_id];
|
||||
deque_end++;
|
||||
if (deque_end >= (ringbuffer + 512))
|
||||
deque_end = ringbuffer;
|
||||
assert(deque_end != deque_start);
|
||||
}
|
||||
deque_start++;
|
||||
if (deque_start >= (ringbuffer + 512))
|
||||
deque_start = ringbuffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
tre_print_reach(tnfa, reach_next, pos, num_tags);
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
/* Check for end of string. */
|
||||
if (len < 0)
|
||||
{
|
||||
if (type == STR_USER)
|
||||
{
|
||||
if (str_user_end)
|
||||
break;
|
||||
}
|
||||
else if (next_c == L'\0' || pos >= TRE_MAX_STRING)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos >= len)
|
||||
break;
|
||||
}
|
||||
|
||||
prev_pos = pos;
|
||||
GET_NEXT_WCHAR();
|
||||
|
||||
/* Swap `reach' and `reach_next'. */
|
||||
{
|
||||
tre_tnfa_approx_reach_t *tmp;
|
||||
tmp = reach;
|
||||
reach = reach_next;
|
||||
reach_next = tmp;
|
||||
}
|
||||
|
||||
/* Handle exact matches and substitutions. */
|
||||
for (id = 0; id < tnfa->num_states; id++)
|
||||
{
|
||||
tre_tnfa_transition_t *trans;
|
||||
|
||||
if (reach[id].pos < prev_pos)
|
||||
continue; /* Not reached. */
|
||||
for (trans = reach[id].state; trans->state; trans++)
|
||||
{
|
||||
int dest_id;
|
||||
int depth;
|
||||
int cost, cost0, err;
|
||||
|
||||
if (trans->assertions
|
||||
&& (CHECK_ASSERTIONS(trans->assertions)
|
||||
|| CHECK_CHAR_CLASSES(trans, tnfa, eflags)))
|
||||
{
|
||||
DPRINT((" exact, from %d: assert failed\n", id));
|
||||
continue;
|
||||
}
|
||||
|
||||
depth = reach[id].depth;
|
||||
dest_id = trans->state_id;
|
||||
|
||||
cost = reach[id].costs[depth][TRE_M_COST];
|
||||
cost0 = reach[id].costs[0][TRE_M_COST];
|
||||
err = 0;
|
||||
|
||||
if (trans->code_min > (tre_cint_t)prev_c
|
||||
|| trans->code_max < (tre_cint_t)prev_c)
|
||||
{
|
||||
/* Handle substitutes. The required character was not in
|
||||
the string, so match it in place of whatever was supposed
|
||||
to be there and increase costs accordingly. */
|
||||
err = 1;
|
||||
|
||||
/* Compute and check cost at current depth. */
|
||||
cost = reach[id].costs[depth][TRE_M_COST];
|
||||
if (reach[id].params.cost_subst != TRE_PARAM_UNSET)
|
||||
cost += reach[id].params.cost_subst;
|
||||
if (cost > reach[id].params.max_cost)
|
||||
continue; /* Cost too large. */
|
||||
|
||||
/* Check number of substitutes at current depth. */
|
||||
if (reach[id].costs[depth][TRE_M_NUM_SUBST] + 1
|
||||
> reach[id].params.max_subst)
|
||||
continue; /* Too many substitutes. */
|
||||
|
||||
/* Check total number of errors at current depth. */
|
||||
if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1
|
||||
> reach[id].params.max_err)
|
||||
continue; /* Too many errors. */
|
||||
|
||||
/* Compute overall cost. */
|
||||
cost0 = cost;
|
||||
if (depth > 0)
|
||||
{
|
||||
cost0 = reach[id].costs[0][TRE_M_COST];
|
||||
if (reach[id].params.cost_subst != TRE_PARAM_UNSET)
|
||||
cost0 += reach[id].params.cost_subst;
|
||||
else
|
||||
cost0 += default_params.cost_subst;
|
||||
}
|
||||
DPRINT((" subst, from %03d to %03d, cost %d: ",
|
||||
id, dest_id, cost0));
|
||||
}
|
||||
else
|
||||
DPRINT((" exact, from %03d to %03d, cost %d: ",
|
||||
id, dest_id, cost0));
|
||||
|
||||
/* Compute tag values after this transition. */
|
||||
for (i = 0; i < num_tags; i++)
|
||||
tmp_tags[i] = reach[id].tags[i];
|
||||
if (trans->tags)
|
||||
for (i = 0; trans->tags[i] >= 0; i++)
|
||||
if (trans->tags[i] < num_tags)
|
||||
tmp_tags[trans->tags[i]] = pos;
|
||||
|
||||
/* If another path has also reached this state, choose the
|
||||
one with the smallest cost or best tags if costs are equal. */
|
||||
if (reach_next[dest_id].pos == pos
|
||||
&& (cost0 > reach_next[dest_id].costs[0][TRE_M_COST]
|
||||
|| (cost0 == reach_next[dest_id].costs[0][TRE_M_COST]
|
||||
&& !tre_tag_order(num_tags, tnfa->tag_directions,
|
||||
tmp_tags,
|
||||
reach_next[dest_id].tags))))
|
||||
{
|
||||
DPRINT(("lose\n"));
|
||||
continue;
|
||||
}
|
||||
DPRINT(("win %d %d\n",
|
||||
reach_next[dest_id].pos,
|
||||
reach_next[dest_id].costs[0][TRE_M_COST]));
|
||||
|
||||
/* Set state, position, tags, and depth. */
|
||||
reach_next[dest_id].state = trans->state;
|
||||
reach_next[dest_id].pos = pos;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
reach_next[dest_id].tags[i] = tmp_tags[i];
|
||||
reach_next[dest_id].depth = reach[id].depth;
|
||||
|
||||
/* Set parameters. */
|
||||
reach_next[dest_id].params = reach[id].params;
|
||||
if (trans->params)
|
||||
tre_set_params(&reach_next[dest_id], trans->params,
|
||||
default_params);
|
||||
|
||||
/* Set the costs after this transition. */
|
||||
memcpy(&reach_next[dest_id].costs,
|
||||
reach[id].costs,
|
||||
sizeof(reach[id].costs[0][0])
|
||||
* TRE_M_LAST * (depth + 1));
|
||||
reach_next[dest_id].costs[depth][TRE_M_COST] = cost;
|
||||
reach_next[dest_id].costs[depth][TRE_M_NUM_SUBST] += err;
|
||||
reach_next[dest_id].costs[depth][TRE_M_NUM_ERR] += err;
|
||||
if (depth > 0)
|
||||
{
|
||||
reach_next[dest_id].costs[0][TRE_M_COST] = cost0;
|
||||
reach_next[dest_id].costs[0][TRE_M_NUM_SUBST] += err;
|
||||
reach_next[dest_id].costs[0][TRE_M_NUM_ERR] += err;
|
||||
}
|
||||
|
||||
if (trans->state == tnfa->final
|
||||
&& (match_eo < 0
|
||||
|| cost0 < match_costs[TRE_M_COST]
|
||||
|| (cost0 == match_costs[TRE_M_COST]
|
||||
&& num_tags > 0 && tmp_tags[0] <= match_tags[0])))
|
||||
{
|
||||
DPRINT((" setting new match at %zd, cost %d\n",
|
||||
pos, cost0));
|
||||
match_eo = pos;
|
||||
for (i = 0; i < TRE_M_LAST; i++)
|
||||
match_costs[i] = reach_next[dest_id].costs[0][i];
|
||||
for (i = 0; i < num_tags; i++)
|
||||
match_tags[i] = tmp_tags[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DPRINT(("match end offset = %d, match cost = %d\n", match_eo,
|
||||
match_costs[TRE_M_COST]));
|
||||
|
||||
match->cost = match_costs[TRE_M_COST];
|
||||
match->num_ins = match_costs[TRE_M_NUM_INS];
|
||||
match->num_del = match_costs[TRE_M_NUM_DEL];
|
||||
match->num_subst = match_costs[TRE_M_NUM_SUBST];
|
||||
*match_end_ofs = match_eo;
|
||||
|
||||
ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
|
||||
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (buf)
|
||||
xfree(buf);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
tre-match-backtrack.c - TRE backtracking regex matching engine
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
This matcher is for regexps that use back referencing. Regexp matching
|
||||
with back referencing is an NP-complete problem on the number of back
|
||||
references. The easiest way to match them is to use a backtracking
|
||||
routine which basically goes through all possible paths in the TNFA
|
||||
and chooses the one which results in the best (leftmost and longest)
|
||||
match. This can be spectacularly expensive and may run out of stack
|
||||
space, but there really is no better known generic algorithm. Quoting
|
||||
Henry Spencer from comp.compilers:
|
||||
<URL: http://compilers.iecc.com/comparch/article/93-03-102>
|
||||
|
||||
POSIX.2 REs require longest match, which is really exciting to
|
||||
implement since the obsolete ("basic") variant also includes
|
||||
\<digit>. I haven't found a better way of tackling this than doing
|
||||
a preliminary match using a DFA (or simulation) on a modified RE
|
||||
that just replicates subREs for \<digit>, and then doing a
|
||||
backtracking match to determine whether the subRE matches were
|
||||
right. This can be rather slow, but I console myself with the
|
||||
thought that people who use \<digit> deserve very slow execution.
|
||||
(Pun unintentional but very appropriate.)
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# if HAVE_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
char *alloca ();
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#endif /* TRE_USE_ALLOCA */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
#ifdef HAVE_WCTYPE_H
|
||||
#include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H */
|
||||
#ifndef TRE_WCHAR
|
||||
#include <ctype.h>
|
||||
#endif /* !TRE_WCHAR */
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif /* HAVE_MALLOC_H */
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "tre-mem.h"
|
||||
#include "tre-match-utils.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
typedef struct {
|
||||
int pos;
|
||||
const char *str_byte;
|
||||
#ifdef TRE_WCHAR
|
||||
const wchar_t *str_wide;
|
||||
#endif /* TRE_WCHAR */
|
||||
tre_tnfa_transition_t *state;
|
||||
int state_id;
|
||||
int next_c;
|
||||
int *tags;
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate_t mbstate;
|
||||
#endif /* TRE_MBSTATE */
|
||||
} tre_backtrack_item_t;
|
||||
|
||||
typedef struct tre_backtrack_struct {
|
||||
tre_backtrack_item_t item;
|
||||
struct tre_backtrack_struct *prev;
|
||||
struct tre_backtrack_struct *next;
|
||||
} *tre_backtrack_t;
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
#define BT_STACK_WIDE_IN(_str_wide) stack->item.str_wide = (_str_wide)
|
||||
#define BT_STACK_WIDE_OUT (str_wide) = stack->item.str_wide
|
||||
#else /* !TRE_WCHAR */
|
||||
#define BT_STACK_WIDE_IN(_str_wide)
|
||||
#define BT_STACK_WIDE_OUT
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
#ifdef TRE_MBSTATE
|
||||
#define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate)
|
||||
#define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
|
||||
#else /* !TRE_MBSTATE */
|
||||
#define BT_STACK_MBSTATE_IN
|
||||
#define BT_STACK_MBSTATE_OUT
|
||||
#endif /* !TRE_MBSTATE */
|
||||
|
||||
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
#define tre_bt_mem_new tre_mem_newa
|
||||
#define tre_bt_mem_alloc tre_mem_alloca
|
||||
#define tre_bt_mem_destroy(obj) do { } while (0)
|
||||
#define xafree(obj) do { } while (0) /* do nothing, obj was obtained with alloca() */
|
||||
#else /* !TRE_USE_ALLOCA */
|
||||
#define tre_bt_mem_new tre_mem_new
|
||||
#define tre_bt_mem_alloc tre_mem_alloc
|
||||
#define tre_bt_mem_destroy tre_mem_destroy
|
||||
#define xafree(obj) xfree(obj)
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
|
||||
|
||||
#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
|
||||
do \
|
||||
{ \
|
||||
int i; \
|
||||
if (!stack->next) \
|
||||
{ \
|
||||
tre_backtrack_t s; \
|
||||
s = tre_bt_mem_alloc(mem, sizeof(*s)); \
|
||||
if (!s) \
|
||||
{ \
|
||||
tre_bt_mem_destroy(mem); \
|
||||
if (tags) \
|
||||
xafree(tags); \
|
||||
if (pmatch) \
|
||||
xafree(pmatch); \
|
||||
if (states_seen) \
|
||||
xafree(states_seen); \
|
||||
return REG_ESPACE; \
|
||||
} \
|
||||
s->prev = stack; \
|
||||
s->next = NULL; \
|
||||
s->item.tags = tre_bt_mem_alloc(mem, \
|
||||
sizeof(*tags) * tnfa->num_tags); \
|
||||
if (!s->item.tags) \
|
||||
{ \
|
||||
tre_bt_mem_destroy(mem); \
|
||||
if (tags) \
|
||||
xafree(tags); \
|
||||
if (pmatch) \
|
||||
xafree(pmatch); \
|
||||
if (states_seen) \
|
||||
xafree(states_seen); \
|
||||
return REG_ESPACE; \
|
||||
} \
|
||||
stack->next = s; \
|
||||
stack = s; \
|
||||
} \
|
||||
else \
|
||||
stack = stack->next; \
|
||||
stack->item.pos = (_pos); \
|
||||
stack->item.str_byte = (_str_byte); \
|
||||
BT_STACK_WIDE_IN(_str_wide); \
|
||||
stack->item.state = (_state); \
|
||||
stack->item.state_id = (_state_id); \
|
||||
stack->item.next_c = (_next_c); \
|
||||
for (i = 0; i < tnfa->num_tags; i++) \
|
||||
stack->item.tags[i] = (_tags)[i]; \
|
||||
BT_STACK_MBSTATE_IN; \
|
||||
} \
|
||||
while (/*CONSTCOND*/(void)0,0)
|
||||
|
||||
#define BT_STACK_POP() \
|
||||
do \
|
||||
{ \
|
||||
int i; \
|
||||
assert(stack->prev); \
|
||||
pos = stack->item.pos; \
|
||||
if (type == STR_USER) \
|
||||
str_source->rewind(pos + pos_add_next, str_source->context); \
|
||||
str_byte = stack->item.str_byte; \
|
||||
BT_STACK_WIDE_OUT; \
|
||||
state = stack->item.state; \
|
||||
next_c = (tre_char_t) stack->item.next_c; \
|
||||
for (i = 0; i < tnfa->num_tags; i++) \
|
||||
tags[i] = stack->item.tags[i]; \
|
||||
BT_STACK_MBSTATE_OUT; \
|
||||
stack = stack->prev; \
|
||||
} \
|
||||
while (/*CONSTCOND*/(void)0,0)
|
||||
|
||||
#undef MIN
|
||||
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
||||
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
|
||||
ssize_t len, tre_str_type_t type, int *match_tags,
|
||||
int eflags, int *match_end_ofs)
|
||||
{
|
||||
/* State variables required by GET_NEXT_WCHAR. */
|
||||
tre_char_t prev_c = 0, next_c = 0;
|
||||
const char *str_byte = string;
|
||||
ssize_t pos = 0;
|
||||
unsigned int pos_add_next = 1;
|
||||
#ifdef TRE_WCHAR
|
||||
const wchar_t *str_wide = string;
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate_t mbstate;
|
||||
#endif /* TRE_MBSTATE */
|
||||
#endif /* TRE_WCHAR */
|
||||
int reg_notbol = eflags & REG_NOTBOL;
|
||||
int reg_noteol = eflags & REG_NOTEOL;
|
||||
int reg_newline = tnfa->cflags & REG_NEWLINE;
|
||||
int str_user_end = 0;
|
||||
|
||||
/* These are used to remember the necessary values of the above
|
||||
variables to return to the position where the current search
|
||||
started from. */
|
||||
int next_c_start;
|
||||
const char *str_byte_start;
|
||||
int pos_start = -1;
|
||||
#ifdef TRE_WCHAR
|
||||
const wchar_t *str_wide_start;
|
||||
#endif /* TRE_WCHAR */
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate_t mbstate_start;
|
||||
#endif /* TRE_MBSTATE */
|
||||
reg_errcode_t ret;
|
||||
|
||||
/* End offset of best match so far, or -1 if no match found yet. */
|
||||
int match_eo = -1;
|
||||
/* Tag arrays. */
|
||||
int *next_tags, *tags = NULL;
|
||||
/* Current TNFA state. */
|
||||
tre_tnfa_transition_t *state;
|
||||
int *states_seen = NULL;
|
||||
|
||||
/* Memory allocator to for allocating the backtracking stack. */
|
||||
tre_mem_t mem = tre_bt_mem_new();
|
||||
|
||||
/* The backtracking stack. */
|
||||
tre_backtrack_t stack;
|
||||
|
||||
tre_tnfa_transition_t *trans_i;
|
||||
regmatch_t *pmatch = NULL;
|
||||
|
||||
/*
|
||||
* TRE internals tend to use int instead of size_t for positions or
|
||||
* lengths and don't check for overflow. This will take time to fix
|
||||
* properly. In the meantime, simply limit the input to what we can
|
||||
* handle.
|
||||
*/
|
||||
if (len > TRE_MAX_STRING)
|
||||
len = TRE_MAX_STRING;
|
||||
|
||||
#ifdef TRE_MBSTATE
|
||||
memset(&mbstate, '\0', sizeof(mbstate));
|
||||
#endif /* TRE_MBSTATE */
|
||||
|
||||
if (!mem)
|
||||
return REG_ESPACE;
|
||||
stack = tre_bt_mem_alloc(mem, sizeof(*stack));
|
||||
if (!stack)
|
||||
{
|
||||
ret = REG_ESPACE;
|
||||
goto error_exit;
|
||||
}
|
||||
stack->prev = NULL;
|
||||
stack->next = NULL;
|
||||
|
||||
DPRINT(("tnfa_execute_backtrack, input type %d\n", type));
|
||||
DPRINT(("len = %zd\n", len));
|
||||
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
tags = alloca(sizeof(*tags) * tnfa->num_tags);
|
||||
pmatch = alloca(sizeof(*pmatch) * tnfa->num_submatches);
|
||||
states_seen = alloca(sizeof(*states_seen) * tnfa->num_states);
|
||||
#else /* !TRE_USE_ALLOCA */
|
||||
if (tnfa->num_tags)
|
||||
{
|
||||
tags = xmalloc(sizeof(*tags) * tnfa->num_tags);
|
||||
if (!tags)
|
||||
{
|
||||
ret = REG_ESPACE;
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
if (tnfa->num_submatches)
|
||||
{
|
||||
pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches);
|
||||
if (!pmatch)
|
||||
{
|
||||
ret = REG_ESPACE;
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
if (tnfa->num_states)
|
||||
{
|
||||
states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states);
|
||||
if (!states_seen)
|
||||
{
|
||||
ret = REG_ESPACE;
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
|
||||
retry:
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < tnfa->num_tags; i++)
|
||||
{
|
||||
tags[i] = -1;
|
||||
if (match_tags)
|
||||
match_tags[i] = -1;
|
||||
}
|
||||
for (i = 0; i < tnfa->num_states; i++)
|
||||
states_seen[i] = 0;
|
||||
}
|
||||
|
||||
state = NULL;
|
||||
pos = pos_start;
|
||||
if (type == STR_USER)
|
||||
str_source->rewind(pos + pos_add_next, str_source->context);
|
||||
GET_NEXT_WCHAR();
|
||||
pos_start = pos;
|
||||
next_c_start = next_c;
|
||||
str_byte_start = str_byte;
|
||||
#ifdef TRE_WCHAR
|
||||
str_wide_start = str_wide;
|
||||
#endif /* TRE_WCHAR */
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate_start = mbstate;
|
||||
#endif /* TRE_MBSTATE */
|
||||
|
||||
/* Handle initial states. */
|
||||
next_tags = NULL;
|
||||
for (trans_i = tnfa->initial; trans_i->state; trans_i++)
|
||||
{
|
||||
DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c));
|
||||
if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
|
||||
{
|
||||
DPRINT(("assert failed\n"));
|
||||
continue;
|
||||
}
|
||||
if (state == NULL)
|
||||
{
|
||||
/* Start from this state. */
|
||||
state = trans_i->state;
|
||||
next_tags = trans_i->tags;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Backtrack to this state. */
|
||||
DPRINT(("saving state %d for backtracking\n", trans_i->state_id));
|
||||
BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state,
|
||||
trans_i->state_id, next_c, tags, mbstate);
|
||||
{
|
||||
int *tmp = trans_i->tags;
|
||||
if (tmp)
|
||||
while (*tmp >= 0)
|
||||
stack->item.tags[*tmp++] = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next_tags)
|
||||
for (; *next_tags >= 0; next_tags++)
|
||||
tags[*next_tags] = pos;
|
||||
|
||||
|
||||
DPRINT(("entering match loop, pos %zd, str_byte %p\n", pos, str_byte));
|
||||
DPRINT(("pos:chr/code | state and tags\n"));
|
||||
DPRINT(("-------------+------------------------------------------------\n"));
|
||||
|
||||
if (state == NULL)
|
||||
goto backtrack;
|
||||
|
||||
while (/*CONSTCOND*/(void)1,1)
|
||||
{
|
||||
tre_tnfa_transition_t *next_state;
|
||||
int empty_br_match;
|
||||
|
||||
DPRINT(("start loop\n"));
|
||||
if (state == tnfa->final)
|
||||
{
|
||||
DPRINT((" match found, %d %zd\n", match_eo, pos));
|
||||
if (match_eo < pos
|
||||
|| (match_eo == pos
|
||||
&& match_tags
|
||||
&& tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
|
||||
tags, match_tags)))
|
||||
{
|
||||
int i;
|
||||
/* This match wins the previous match. */
|
||||
DPRINT((" win previous\n"));
|
||||
match_eo = pos;
|
||||
if (match_tags)
|
||||
for (i = 0; i < tnfa->num_tags; i++)
|
||||
match_tags[i] = tags[i];
|
||||
}
|
||||
/* Our TNFAs never have transitions leaving from the final state,
|
||||
so we jump right to backtracking. */
|
||||
goto backtrack;
|
||||
}
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
DPRINT(("%3zd:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c,
|
||||
state));
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < tnfa->num_tags; i++)
|
||||
DPRINT(("%d%s", tags[i], i < tnfa->num_tags - 1 ? ", " : ""));
|
||||
DPRINT(("\n"));
|
||||
}
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
/* Go to the next character in the input string. */
|
||||
empty_br_match = 0;
|
||||
trans_i = state;
|
||||
if (trans_i->state && trans_i->assertions & ASSERT_BACKREF)
|
||||
{
|
||||
/* This is a back reference state. All transitions leaving from
|
||||
this state have the same back reference "assertion". Instead
|
||||
of reading the next character, we match the back reference. */
|
||||
int so, eo, bt = trans_i->u.backref;
|
||||
int bt_len;
|
||||
int result;
|
||||
|
||||
DPRINT((" should match back reference %d\n", bt));
|
||||
/* Get the substring we need to match against. Remember to
|
||||
turn off REG_NOSUB temporarily. */
|
||||
tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB,
|
||||
tnfa, tags, pos);
|
||||
so = pmatch[bt].rm_so;
|
||||
eo = pmatch[bt].rm_eo;
|
||||
bt_len = eo - so;
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
{
|
||||
int slen;
|
||||
if (len < 0)
|
||||
slen = bt_len;
|
||||
else
|
||||
slen = MIN(bt_len, len - pos);
|
||||
|
||||
if (type == STR_BYTE)
|
||||
{
|
||||
DPRINT((" substring (len %d) is [%d, %d[: '%.*s'\n",
|
||||
bt_len, so, eo, bt_len, (char*)string + so));
|
||||
DPRINT((" current string is '%.*s'\n", slen, str_byte - 1));
|
||||
}
|
||||
#ifdef TRE_WCHAR
|
||||
else if (type == STR_WIDE)
|
||||
{
|
||||
DPRINT((" substring (len %d) is [%d, %d[: '%.*" STRF "'\n",
|
||||
bt_len, so, eo, bt_len, (wchar_t*)string + so));
|
||||
DPRINT((" current string is '%.*" STRF "'\n",
|
||||
slen, str_wide - 1));
|
||||
}
|
||||
#endif /* TRE_WCHAR */
|
||||
}
|
||||
#endif
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
if (type == STR_USER)
|
||||
result = str_source->compare((unsigned)so, (unsigned)pos,
|
||||
(unsigned)bt_len,
|
||||
str_source->context);
|
||||
#ifdef TRE_WCHAR
|
||||
else if (type == STR_WIDE)
|
||||
result = wcsncmp((const wchar_t*)string + so, str_wide - 1,
|
||||
(size_t)bt_len);
|
||||
#endif /* TRE_WCHAR */
|
||||
else
|
||||
result = strncmp((const char*)string + so, str_byte - 1,
|
||||
(size_t)bt_len);
|
||||
}
|
||||
else if (len - pos < bt_len)
|
||||
result = 1;
|
||||
#ifdef TRE_WCHAR
|
||||
else if (type == STR_WIDE)
|
||||
result = wmemcmp((const wchar_t*)string + so, str_wide - 1,
|
||||
(size_t)bt_len);
|
||||
#endif /* TRE_WCHAR */
|
||||
else
|
||||
result = memcmp((const char*)string + so, str_byte - 1,
|
||||
(size_t)bt_len);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
/* Back reference matched. Check for infinite loop. */
|
||||
if (bt_len == 0)
|
||||
empty_br_match = 1;
|
||||
if (empty_br_match && states_seen[trans_i->state_id])
|
||||
{
|
||||
DPRINT((" avoid loop\n"));
|
||||
goto backtrack;
|
||||
}
|
||||
|
||||
states_seen[trans_i->state_id] = empty_br_match;
|
||||
|
||||
/* Advance in input string and resync `prev_c', `next_c'
|
||||
and pos. */
|
||||
DPRINT((" back reference matched\n"));
|
||||
str_byte += bt_len - 1;
|
||||
#ifdef TRE_WCHAR
|
||||
str_wide += bt_len - 1;
|
||||
#endif /* TRE_WCHAR */
|
||||
pos += bt_len - 1;
|
||||
GET_NEXT_WCHAR();
|
||||
DPRINT((" pos now %zd\n", pos));
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT((" back reference did not match\n"));
|
||||
goto backtrack;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check for end of string. */
|
||||
if (len < 0)
|
||||
{
|
||||
if (type == STR_USER)
|
||||
{
|
||||
if (str_user_end)
|
||||
goto backtrack;
|
||||
}
|
||||
else if (next_c == L'\0' || pos >= TRE_MAX_STRING)
|
||||
goto backtrack;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos >= len)
|
||||
goto backtrack;
|
||||
}
|
||||
|
||||
/* Read the next character. */
|
||||
GET_NEXT_WCHAR();
|
||||
}
|
||||
|
||||
next_state = NULL;
|
||||
for (trans_i = state; trans_i->state; trans_i++)
|
||||
{
|
||||
DPRINT((" transition %d-%d (%c-%c) %d to %d\n",
|
||||
trans_i->code_min, trans_i->code_max,
|
||||
trans_i->code_min, trans_i->code_max,
|
||||
trans_i->assertions, trans_i->state_id));
|
||||
if (trans_i->code_min <= (tre_cint_t)prev_c
|
||||
&& trans_i->code_max >= (tre_cint_t)prev_c)
|
||||
{
|
||||
if (trans_i->assertions
|
||||
&& (CHECK_ASSERTIONS(trans_i->assertions)
|
||||
|| CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
|
||||
{
|
||||
DPRINT((" assertion failed\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (next_state == NULL)
|
||||
{
|
||||
/* First matching transition. */
|
||||
DPRINT((" Next state is %d\n", trans_i->state_id));
|
||||
next_state = trans_i->state;
|
||||
next_tags = trans_i->tags;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Second matching transition. We may need to backtrack here
|
||||
to take this transition instead of the first one, so we
|
||||
push this transition in the backtracking stack so we can
|
||||
jump back here if needed. */
|
||||
DPRINT((" saving state %d for backtracking\n",
|
||||
trans_i->state_id));
|
||||
BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state,
|
||||
trans_i->state_id, next_c, tags, mbstate);
|
||||
{
|
||||
int *tmp;
|
||||
for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
|
||||
stack->item.tags[*tmp] = pos;
|
||||
}
|
||||
#if 0 /* XXX - it's important not to look at all transitions here to keep
|
||||
the stack small! */
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next_state != NULL)
|
||||
{
|
||||
/* Matching transitions were found. Take the first one. */
|
||||
state = next_state;
|
||||
|
||||
/* Update the tag values. */
|
||||
if (next_tags)
|
||||
while (*next_tags >= 0)
|
||||
tags[*next_tags++] = pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
backtrack:
|
||||
/* A matching transition was not found. Try to backtrack. */
|
||||
if (stack->prev)
|
||||
{
|
||||
DPRINT((" backtracking\n"));
|
||||
if (stack->item.state->assertions & ASSERT_BACKREF)
|
||||
{
|
||||
DPRINT((" states_seen[%d] = 0\n",
|
||||
stack->item.state_id));
|
||||
states_seen[stack->item.state_id] = 0;
|
||||
}
|
||||
|
||||
BT_STACK_POP();
|
||||
}
|
||||
else if (match_eo < 0)
|
||||
{
|
||||
/* Try starting from a later position in the input string. */
|
||||
/* Check for end of string. */
|
||||
if (len < 0)
|
||||
{
|
||||
if (next_c_start == L'\0' || pos_start >= TRE_MAX_STRING)
|
||||
{
|
||||
DPRINT(("end of string.\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos_start >= len)
|
||||
{
|
||||
DPRINT(("end of string.\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DPRINT(("restarting from next start position\n"));
|
||||
next_c = (tre_char_t) next_c_start;
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate = mbstate_start;
|
||||
#endif /* TRE_MBSTATE */
|
||||
str_byte = str_byte_start;
|
||||
#ifdef TRE_WCHAR
|
||||
str_wide = str_wide_start;
|
||||
#endif /* TRE_WCHAR */
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT(("finished\n"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
|
||||
*match_end_ofs = match_eo;
|
||||
|
||||
error_exit:
|
||||
tre_bt_mem_destroy(mem);
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (tags)
|
||||
xafree(tags);
|
||||
if (pmatch)
|
||||
xafree(pmatch);
|
||||
if (states_seen)
|
||||
xafree(states_seen);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
tre-match-parallel.c - TRE parallel regex matching engine
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
This algorithm searches for matches basically by reading characters
|
||||
in the searched string one by one, starting at the beginning. All
|
||||
matching paths in the TNFA are traversed in parallel. When two or
|
||||
more paths reach the same state, exactly one is chosen according to
|
||||
tag ordering rules; if returning submatches is not required it does
|
||||
not matter which path is chosen.
|
||||
|
||||
The worst case time required for finding the leftmost and longest
|
||||
match, or determining that there is no match, is always linearly
|
||||
dependent on the length of the text being searched.
|
||||
|
||||
This algorithm cannot handle TNFAs with back referencing nodes.
|
||||
See `tre-match-backtrack.c'.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# if HAVE_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
char *alloca ();
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#endif /* TRE_USE_ALLOCA */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
#ifdef HAVE_WCTYPE_H
|
||||
#include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H */
|
||||
#ifndef TRE_WCHAR
|
||||
#include <ctype.h>
|
||||
#endif /* !TRE_WCHAR */
|
||||
#ifdef HAVE_MALLOC_H
|
||||
#include <malloc.h>
|
||||
#endif /* HAVE_MALLOC_H */
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "tre-match-utils.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
tre_tnfa_transition_t *state;
|
||||
int *tags;
|
||||
} tre_tnfa_reach_t;
|
||||
|
||||
typedef struct {
|
||||
int pos;
|
||||
int **tags;
|
||||
} tre_reach_pos_t;
|
||||
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
static void
|
||||
tre_print_reach(const tre_tnfa_reach_t *reach, int num_tags)
|
||||
{
|
||||
int i;
|
||||
|
||||
while (reach->state != NULL)
|
||||
{
|
||||
DPRINT((" %p", (void *)reach->state));
|
||||
if (num_tags > 0)
|
||||
{
|
||||
DPRINT(("/"));
|
||||
for (i = 0; i < num_tags; i++)
|
||||
{
|
||||
DPRINT(("%d:%d", i, reach->tags[i]));
|
||||
if (i < (num_tags-1))
|
||||
DPRINT((","));
|
||||
}
|
||||
}
|
||||
reach++;
|
||||
}
|
||||
DPRINT(("\n"));
|
||||
|
||||
}
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
reg_errcode_t
|
||||
tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, ssize_t len,
|
||||
tre_str_type_t type, int *match_tags, int eflags,
|
||||
int *match_end_ofs)
|
||||
{
|
||||
/* State variables required by GET_NEXT_WCHAR. */
|
||||
tre_char_t prev_c = 0, next_c = 0;
|
||||
const char *str_byte = string;
|
||||
ssize_t pos = -1;
|
||||
unsigned int pos_add_next = 1;
|
||||
#ifdef TRE_WCHAR
|
||||
const wchar_t *str_wide = string;
|
||||
#ifdef TRE_MBSTATE
|
||||
mbstate_t mbstate;
|
||||
#endif /* TRE_MBSTATE */
|
||||
#endif /* TRE_WCHAR */
|
||||
reg_errcode_t ret;
|
||||
int reg_notbol = eflags & REG_NOTBOL;
|
||||
int reg_noteol = eflags & REG_NOTEOL;
|
||||
int reg_newline = tnfa->cflags & REG_NEWLINE;
|
||||
int str_user_end = 0;
|
||||
|
||||
char *buf;
|
||||
tre_tnfa_transition_t *trans_i;
|
||||
tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i;
|
||||
tre_reach_pos_t *reach_pos;
|
||||
int *tag_i;
|
||||
int num_tags, i;
|
||||
|
||||
int match_eo = -1; /* end offset of match (-1 if no match found yet) */
|
||||
int new_match = 0;
|
||||
int *tmp_tags = NULL;
|
||||
int *tmp_iptr;
|
||||
|
||||
/*
|
||||
* TRE internals tend to use int instead of size_t for positions or
|
||||
* lengths and don't check for overflow. This will take time to fix
|
||||
* properly. In the meantime, simply limit the input to what we can
|
||||
* handle.
|
||||
*/
|
||||
if (len > TRE_MAX_STRING)
|
||||
len = TRE_MAX_STRING;
|
||||
|
||||
#ifdef TRE_MBSTATE
|
||||
memset(&mbstate, '\0', sizeof(mbstate));
|
||||
#endif /* TRE_MBSTATE */
|
||||
|
||||
DPRINT(("tre_tnfa_run_parallel, input type %d\n", type));
|
||||
|
||||
if (!match_tags)
|
||||
num_tags = 0;
|
||||
else
|
||||
num_tags = tnfa->num_tags;
|
||||
|
||||
/* Allocate memory for temporary data required for matching. This needs to
|
||||
be done for every matching operation to be thread safe. This allocates
|
||||
everything in a single large block from the stack frame using alloca()
|
||||
or with malloc() if alloca is unavailable. */
|
||||
{
|
||||
size_t tbytes, rbytes, pbytes, xbytes, total_bytes;
|
||||
char *tmp_buf;
|
||||
/* Compute the length of the block we need. */
|
||||
tbytes = sizeof(*tmp_tags) * num_tags;
|
||||
rbytes = sizeof(*reach_next) * (tnfa->num_states + 1);
|
||||
pbytes = sizeof(*reach_pos) * tnfa->num_states;
|
||||
xbytes = sizeof(int) * num_tags;
|
||||
total_bytes =
|
||||
(sizeof(long) - 1) * 4 /* for alignment paddings */
|
||||
+ (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes;
|
||||
|
||||
/* Allocate the memory. */
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
buf = alloca(total_bytes);
|
||||
#else /* !TRE_USE_ALLOCA */
|
||||
buf = xmalloc(total_bytes);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
if (buf == NULL)
|
||||
return REG_ESPACE;
|
||||
memset(buf, 0, total_bytes);
|
||||
|
||||
/* Get the various pointers within tmp_buf (properly aligned). */
|
||||
tmp_tags = (void *)buf;
|
||||
tmp_buf = buf + tbytes;
|
||||
tmp_buf += ALIGN(tmp_buf, long);
|
||||
reach_next = (void *)tmp_buf;
|
||||
tmp_buf += rbytes;
|
||||
tmp_buf += ALIGN(tmp_buf, long);
|
||||
reach = (void *)tmp_buf;
|
||||
tmp_buf += rbytes;
|
||||
tmp_buf += ALIGN(tmp_buf, long);
|
||||
reach_pos = (void *)tmp_buf;
|
||||
tmp_buf += pbytes;
|
||||
tmp_buf += ALIGN(tmp_buf, long);
|
||||
for (i = 0; i < tnfa->num_states; i++)
|
||||
{
|
||||
reach[i].tags = (void *)tmp_buf;
|
||||
tmp_buf += xbytes;
|
||||
reach_next[i].tags = (void *)tmp_buf;
|
||||
tmp_buf += xbytes;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tnfa->num_states; i++)
|
||||
reach_pos[i].pos = -1;
|
||||
|
||||
/* If only one character can start a match, find it first. */
|
||||
if (tnfa->first_char >= 0 && type == STR_BYTE && str_byte)
|
||||
{
|
||||
const char *orig_str = str_byte;
|
||||
int first = tnfa->first_char;
|
||||
|
||||
if (len >= 0)
|
||||
str_byte = memchr(orig_str, first, (size_t)len);
|
||||
else
|
||||
str_byte = strchr(orig_str, first);
|
||||
if (str_byte == NULL)
|
||||
{
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (buf)
|
||||
xfree(buf);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
return REG_NOMATCH;
|
||||
}
|
||||
DPRINT(("skipped %lu chars\n", (unsigned long)(str_byte - orig_str)));
|
||||
if (str_byte >= orig_str + 1)
|
||||
prev_c = (unsigned char)*(str_byte - 1);
|
||||
next_c = (unsigned char)*str_byte;
|
||||
pos = str_byte - orig_str;
|
||||
if (len < 0 || pos < len)
|
||||
str_byte++;
|
||||
}
|
||||
else
|
||||
{
|
||||
GET_NEXT_WCHAR();
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Skip over characters that cannot possibly be the first character
|
||||
of a match. */
|
||||
if (tnfa->firstpos_chars != NULL)
|
||||
{
|
||||
char *chars = tnfa->firstpos_chars;
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
const char *orig_str = str_byte;
|
||||
/* XXX - use strpbrk() and wcspbrk() because they might be
|
||||
optimized for the target architecture. Try also strcspn()
|
||||
and wcscspn() and compare the speeds. */
|
||||
while (next_c != L'\0' && !chars[next_c])
|
||||
{
|
||||
next_c = *str_byte++;
|
||||
}
|
||||
prev_c = *(str_byte - 2);
|
||||
pos += str_byte - orig_str;
|
||||
DPRINT(("skipped %d chars\n", str_byte - orig_str));
|
||||
}
|
||||
else
|
||||
{
|
||||
while (pos <= len && !chars[next_c])
|
||||
{
|
||||
prev_c = next_c;
|
||||
next_c = (unsigned char)(*str_byte++);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DPRINT(("length: %zd\n", len));
|
||||
DPRINT(("pos:chr/code | states and tags\n"));
|
||||
DPRINT(("-------------+------------------------------------------------\n"));
|
||||
|
||||
reach_next_i = reach_next;
|
||||
while (/*CONSTCOND*/(void)1,1)
|
||||
{
|
||||
/* If no match found yet, add the initial states to `reach_next'. */
|
||||
if (match_eo < 0)
|
||||
{
|
||||
DPRINT((" init >"));
|
||||
trans_i = tnfa->initial;
|
||||
while (trans_i->state != NULL)
|
||||
{
|
||||
if (reach_pos[trans_i->state_id].pos < pos)
|
||||
{
|
||||
if (trans_i->assertions
|
||||
&& CHECK_ASSERTIONS(trans_i->assertions))
|
||||
{
|
||||
DPRINT(("assertion failed\n"));
|
||||
trans_i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
DPRINT((" %p", (void *)trans_i->state));
|
||||
reach_next_i->state = trans_i->state;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
reach_next_i->tags[i] = -1;
|
||||
tag_i = trans_i->tags;
|
||||
if (tag_i)
|
||||
while (*tag_i >= 0)
|
||||
{
|
||||
if (*tag_i < num_tags)
|
||||
reach_next_i->tags[*tag_i] = pos;
|
||||
tag_i++;
|
||||
}
|
||||
if (reach_next_i->state == tnfa->final)
|
||||
{
|
||||
DPRINT((" found empty match\n"));
|
||||
match_eo = pos;
|
||||
new_match = 1;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
match_tags[i] = reach_next_i->tags[i];
|
||||
}
|
||||
reach_pos[trans_i->state_id].pos = pos;
|
||||
reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
|
||||
reach_next_i++;
|
||||
}
|
||||
trans_i++;
|
||||
}
|
||||
DPRINT(("\n"));
|
||||
reach_next_i->state = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num_tags == 0 || reach_next_i == reach_next)
|
||||
/* We have found a match. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for end of string. */
|
||||
if (len < 0)
|
||||
{
|
||||
if (type == STR_USER)
|
||||
{
|
||||
if (str_user_end)
|
||||
break;
|
||||
}
|
||||
else if (next_c == L'\0' || pos >= TRE_MAX_STRING)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos >= len)
|
||||
break;
|
||||
}
|
||||
|
||||
GET_NEXT_WCHAR();
|
||||
|
||||
#ifdef TRE_DEBUG
|
||||
DPRINT(("%3zd:%2lc/%05d |", pos - 1, (tre_cint_t)prev_c, (int)prev_c));
|
||||
tre_print_reach(reach_next, num_tags);
|
||||
DPRINT(("%3zd:%2lc/%05d |", pos, (tre_cint_t)next_c, (int)next_c));
|
||||
tre_print_reach(reach_next, num_tags);
|
||||
#endif /* TRE_DEBUG */
|
||||
|
||||
/* Swap `reach' and `reach_next'. */
|
||||
reach_i = reach;
|
||||
reach = reach_next;
|
||||
reach_next = reach_i;
|
||||
|
||||
/* For each state in `reach', weed out states that don't fulfill the
|
||||
minimal matching conditions. */
|
||||
if (tnfa->num_minimals && new_match)
|
||||
{
|
||||
new_match = 0;
|
||||
reach_next_i = reach_next;
|
||||
for (reach_i = reach; reach_i->state; reach_i++)
|
||||
{
|
||||
int skip = 0;
|
||||
for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
|
||||
{
|
||||
int end = tnfa->minimal_tags[i];
|
||||
int start = tnfa->minimal_tags[i + 1];
|
||||
DPRINT((" Minimal start %d, end %d\n", start, end));
|
||||
if (end >= num_tags)
|
||||
{
|
||||
DPRINT((" Throwing %p out.\n", reach_i->state));
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
else if (reach_i->tags[start] == match_tags[start]
|
||||
&& reach_i->tags[end] < match_tags[end])
|
||||
{
|
||||
DPRINT((" Throwing %p out because t%d < %d\n",
|
||||
reach_i->state, end, match_tags[end]));
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip)
|
||||
{
|
||||
reach_next_i->state = reach_i->state;
|
||||
tmp_iptr = reach_next_i->tags;
|
||||
reach_next_i->tags = reach_i->tags;
|
||||
reach_i->tags = tmp_iptr;
|
||||
reach_next_i++;
|
||||
}
|
||||
}
|
||||
reach_next_i->state = NULL;
|
||||
|
||||
/* Swap `reach' and `reach_next'. */
|
||||
reach_i = reach;
|
||||
reach = reach_next;
|
||||
reach_next = reach_i;
|
||||
}
|
||||
|
||||
/* For each state in `reach' see if there is a transition leaving with
|
||||
the current input symbol to a state not yet in `reach_next', and
|
||||
add the destination states to `reach_next'. */
|
||||
reach_next_i = reach_next;
|
||||
for (reach_i = reach; reach_i->state; reach_i++)
|
||||
{
|
||||
for (trans_i = reach_i->state; trans_i->state; trans_i++)
|
||||
{
|
||||
/* Does this transition match the input symbol? */
|
||||
if (trans_i->code_min <= (tre_cint_t)prev_c &&
|
||||
trans_i->code_max >= (tre_cint_t)prev_c)
|
||||
{
|
||||
if (trans_i->assertions
|
||||
&& (CHECK_ASSERTIONS(trans_i->assertions)
|
||||
|| CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
|
||||
{
|
||||
DPRINT(("assertion failed\n"));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute the tags after this transition. */
|
||||
for (i = 0; i < num_tags; i++)
|
||||
tmp_tags[i] = reach_i->tags[i];
|
||||
tag_i = trans_i->tags;
|
||||
if (tag_i != NULL)
|
||||
while (*tag_i >= 0)
|
||||
{
|
||||
if (*tag_i < num_tags)
|
||||
tmp_tags[*tag_i] = pos;
|
||||
tag_i++;
|
||||
}
|
||||
|
||||
if (reach_pos[trans_i->state_id].pos < pos)
|
||||
{
|
||||
/* Found an unvisited node. */
|
||||
reach_next_i->state = trans_i->state;
|
||||
tmp_iptr = reach_next_i->tags;
|
||||
reach_next_i->tags = tmp_tags;
|
||||
tmp_tags = tmp_iptr;
|
||||
reach_pos[trans_i->state_id].pos = pos;
|
||||
reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
|
||||
|
||||
if (reach_next_i->state == tnfa->final
|
||||
&& (match_eo == -1
|
||||
|| (num_tags > 0
|
||||
&& reach_next_i->tags[0] <= match_tags[0])))
|
||||
{
|
||||
DPRINT((" found match %p\n", trans_i->state));
|
||||
match_eo = pos;
|
||||
new_match = 1;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
match_tags[i] = reach_next_i->tags[i];
|
||||
}
|
||||
reach_next_i++;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(reach_pos[trans_i->state_id].pos == pos);
|
||||
/* Another path has also reached this state. We choose
|
||||
the winner by examining the tag values for both
|
||||
paths. */
|
||||
if (tre_tag_order(num_tags, tnfa->tag_directions,
|
||||
tmp_tags,
|
||||
*reach_pos[trans_i->state_id].tags))
|
||||
{
|
||||
/* The new path wins. */
|
||||
tmp_iptr = *reach_pos[trans_i->state_id].tags;
|
||||
*reach_pos[trans_i->state_id].tags = tmp_tags;
|
||||
if (trans_i->state == tnfa->final)
|
||||
{
|
||||
DPRINT((" found better match\n"));
|
||||
match_eo = pos;
|
||||
new_match = 1;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
match_tags[i] = tmp_tags[i];
|
||||
}
|
||||
tmp_tags = tmp_iptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reach_next_i->state = NULL;
|
||||
}
|
||||
|
||||
DPRINT(("match end offset = %d\n", match_eo));
|
||||
|
||||
*match_end_ofs = match_eo;
|
||||
ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
|
||||
|
||||
#ifndef TRE_USE_ALLOCA
|
||||
if (buf)
|
||||
xfree(buf);
|
||||
#endif /* !TRE_USE_ALLOCA */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
tre-match-utils.h - TRE matcher helper definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
#ifndef TRE_MATCH_UTILS_H
|
||||
#define TRE_MATCH_UTILS_H
|
||||
|
||||
#define str_source ((const tre_str_source*)string)
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
|
||||
#ifdef TRE_MULTIBYTE
|
||||
|
||||
/* Wide character and multibyte support. */
|
||||
|
||||
#define GET_NEXT_WCHAR() \
|
||||
do { \
|
||||
prev_c = next_c; \
|
||||
if (type == STR_BYTE) \
|
||||
{ \
|
||||
pos++; \
|
||||
if (len >= 0 && pos >= len) \
|
||||
next_c = '\0'; \
|
||||
else \
|
||||
next_c = (unsigned char)(*str_byte++); \
|
||||
} \
|
||||
else if (type == STR_WIDE) \
|
||||
{ \
|
||||
pos++; \
|
||||
if (len >= 0 && pos >= len) \
|
||||
next_c = L'\0'; \
|
||||
else \
|
||||
next_c = *str_wide++; \
|
||||
} \
|
||||
else if (type == STR_MBS) \
|
||||
{ \
|
||||
pos += pos_add_next; \
|
||||
if (str_byte == NULL) \
|
||||
next_c = L'\0'; \
|
||||
else \
|
||||
{ \
|
||||
size_t w; \
|
||||
size_t max; \
|
||||
if (len >= 0) \
|
||||
max = len - pos; \
|
||||
else \
|
||||
max = 32; \
|
||||
if (max <= 0) \
|
||||
{ \
|
||||
next_c = L'\0'; \
|
||||
pos_add_next = 1; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
w = tre_mbrtowc(&next_c, str_byte, (size_t)max, &mbstate); \
|
||||
if (w == (size_t)-1 || w == (size_t)-2) \
|
||||
return REG_NOMATCH; \
|
||||
if (w == 0 && len >= 0) \
|
||||
{ \
|
||||
pos_add_next = 1; \
|
||||
next_c = 0; \
|
||||
str_byte++; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
pos_add_next = w; \
|
||||
str_byte += w; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
else if (type == STR_USER) \
|
||||
{ \
|
||||
pos += pos_add_next; \
|
||||
str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \
|
||||
str_source->context); \
|
||||
} \
|
||||
} while(/*CONSTCOND*/(void)0,0)
|
||||
|
||||
#else /* !TRE_MULTIBYTE */
|
||||
|
||||
/* Wide character support, no multibyte support. */
|
||||
|
||||
#define GET_NEXT_WCHAR() \
|
||||
do { \
|
||||
prev_c = next_c; \
|
||||
if (type == STR_BYTE) \
|
||||
{ \
|
||||
pos++; \
|
||||
if (len >= 0 && pos >= len) \
|
||||
next_c = '\0'; \
|
||||
else \
|
||||
next_c = (unsigned char)(*str_byte++); \
|
||||
} \
|
||||
else if (type == STR_WIDE) \
|
||||
{ \
|
||||
pos++; \
|
||||
if (len >= 0 && pos >= len) \
|
||||
next_c = L'\0'; \
|
||||
else \
|
||||
next_c = *str_wide++; \
|
||||
} \
|
||||
else if (type == STR_USER) \
|
||||
{ \
|
||||
pos += pos_add_next; \
|
||||
str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \
|
||||
str_source->context); \
|
||||
} \
|
||||
} while(/*CONSTCOND*/(void)0,0)
|
||||
|
||||
#endif /* !TRE_MULTIBYTE */
|
||||
|
||||
#else /* !TRE_WCHAR */
|
||||
|
||||
/* No wide character or multibyte support. */
|
||||
|
||||
#define GET_NEXT_WCHAR() \
|
||||
do { \
|
||||
prev_c = next_c; \
|
||||
if (type == STR_BYTE) \
|
||||
{ \
|
||||
pos++; \
|
||||
if (len >= 0 && pos >= len) \
|
||||
next_c = '\0'; \
|
||||
else \
|
||||
next_c = (unsigned char)(*str_byte++); \
|
||||
} \
|
||||
else if (type == STR_USER) \
|
||||
{ \
|
||||
pos += pos_add_next; \
|
||||
str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \
|
||||
str_source->context); \
|
||||
} \
|
||||
} while(/*CONSTCOND*/(void)0,0)
|
||||
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
|
||||
|
||||
#define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum(c))
|
||||
|
||||
#define CHECK_ASSERTIONS(assertions) \
|
||||
(((assertions & ASSERT_AT_BOL) \
|
||||
&& (pos > 0 || reg_notbol) \
|
||||
&& (prev_c != L'\n' || !reg_newline)) \
|
||||
|| ((assertions & ASSERT_AT_EOL) \
|
||||
&& (next_c != L'\0' || reg_noteol) \
|
||||
&& (next_c != L'\n' || !reg_newline)) \
|
||||
|| ((assertions & ASSERT_AT_BOW) \
|
||||
&& (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) \
|
||||
|| ((assertions & ASSERT_AT_EOW) \
|
||||
&& (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) \
|
||||
|| ((assertions & ASSERT_AT_WB) \
|
||||
&& (pos != 0 && next_c != L'\0' \
|
||||
&& IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) \
|
||||
|| ((assertions & ASSERT_AT_WB_NEG) \
|
||||
&& (pos == 0 || next_c == L'\0' \
|
||||
|| IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c))))
|
||||
|
||||
#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \
|
||||
(((trans_i->assertions & ASSERT_CHAR_CLASS) \
|
||||
&& !(tnfa->cflags & REG_ICASE) \
|
||||
&& !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) \
|
||||
|| ((trans_i->assertions & ASSERT_CHAR_CLASS) \
|
||||
&& (tnfa->cflags & REG_ICASE) \
|
||||
&& !tre_isctype(tre_tolower((tre_cint_t)prev_c),trans_i->u.class) \
|
||||
&& !tre_isctype(tre_toupper((tre_cint_t)prev_c),trans_i->u.class)) \
|
||||
|| ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) \
|
||||
&& tre_neg_char_classes_match(trans_i->neg_classes,(tre_cint_t)prev_c,\
|
||||
tnfa->cflags & REG_ICASE)))
|
||||
|
||||
|
||||
|
||||
|
||||
/* Returns 1 if `t1' wins `t2', 0 otherwise. */
|
||||
inline static int
|
||||
tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
|
||||
int *t1, int *t2)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_tags; i++)
|
||||
{
|
||||
if (tag_directions[i] == TRE_TAG_MINIMIZE)
|
||||
{
|
||||
if (t1[i] < t2[i])
|
||||
return 1;
|
||||
if (t1[i] > t2[i])
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t1[i] > t2[i])
|
||||
return 1;
|
||||
if (t1[i] < t2[i])
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* assert(0);*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline static int
|
||||
tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase)
|
||||
{
|
||||
DPRINT(("neg_char_classes_test: %p, %d, %d\n", classes, wc, icase));
|
||||
while (*classes != (tre_ctype_t)0)
|
||||
if ((!icase && tre_isctype(wc, *classes))
|
||||
|| (icase && (tre_isctype(tre_toupper(wc), *classes)
|
||||
|| tre_isctype(tre_tolower(wc), *classes))))
|
||||
return 1; /* Match. */
|
||||
else
|
||||
classes++;
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
#endif /* TRE_MATCH_UTILS_H */
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
tre-mem.c - TRE memory allocator
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
This memory allocator is for allocating small memory blocks efficiently
|
||||
in terms of memory overhead and execution speed. The allocated blocks
|
||||
cannot be freed individually, only all at once. There can be multiple
|
||||
allocators, though.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "tre-mem.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
||||
/* Returns a new memory allocator or NULL if out of memory. */
|
||||
tre_mem_t
|
||||
tre_mem_new_impl(int provided, void *provided_block)
|
||||
{
|
||||
tre_mem_t mem;
|
||||
if (provided)
|
||||
{
|
||||
mem = provided_block;
|
||||
memset(mem, 0, sizeof(*mem));
|
||||
}
|
||||
else
|
||||
mem = xcalloc(1, sizeof(*mem));
|
||||
if (mem == NULL)
|
||||
return NULL;
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
/* Frees the memory allocator and all memory allocated with it. */
|
||||
void
|
||||
tre_mem_destroy(tre_mem_t mem)
|
||||
{
|
||||
tre_list_t *tmp, *l = mem->blocks;
|
||||
|
||||
while (l != NULL)
|
||||
{
|
||||
xfree(l->data);
|
||||
tmp = l->next;
|
||||
xfree(l);
|
||||
l = tmp;
|
||||
}
|
||||
xfree(mem);
|
||||
}
|
||||
|
||||
|
||||
/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
|
||||
allocated block or NULL if an underlying malloc() failed. */
|
||||
void *
|
||||
tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
|
||||
int zero, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
if (mem->failed)
|
||||
{
|
||||
DPRINT(("tre_mem_alloc: oops, called after failure?!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef MALLOC_DEBUGGING
|
||||
if (!provided)
|
||||
{
|
||||
ptr = xmalloc(1);
|
||||
if (ptr == NULL)
|
||||
{
|
||||
DPRINT(("tre_mem_alloc: xmalloc forced failure\n"));
|
||||
mem->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
xfree(ptr);
|
||||
}
|
||||
#endif /* MALLOC_DEBUGGING */
|
||||
|
||||
if (mem->n < size)
|
||||
{
|
||||
/* We need more memory than is available in the current block.
|
||||
Allocate a new block. */
|
||||
tre_list_t *l;
|
||||
if (provided)
|
||||
{
|
||||
DPRINT(("tre_mem_alloc: using provided block\n"));
|
||||
if (provided_block == NULL)
|
||||
{
|
||||
DPRINT(("tre_mem_alloc: provided block was NULL\n"));
|
||||
mem->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
mem->ptr = provided_block;
|
||||
mem->n = TRE_MEM_BLOCK_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t block_size;
|
||||
if (size * 8 > TRE_MEM_BLOCK_SIZE)
|
||||
block_size = size * 8;
|
||||
else
|
||||
block_size = TRE_MEM_BLOCK_SIZE;
|
||||
DPRINT(("tre_mem_alloc: allocating new %zu byte block\n",
|
||||
block_size));
|
||||
l = xmalloc(sizeof(*l));
|
||||
if (l == NULL)
|
||||
{
|
||||
mem->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
l->data = xmalloc(block_size);
|
||||
if (l->data == NULL)
|
||||
{
|
||||
xfree(l);
|
||||
mem->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
l->next = NULL;
|
||||
if (mem->current != NULL)
|
||||
mem->current->next = l;
|
||||
if (mem->blocks == NULL)
|
||||
mem->blocks = l;
|
||||
mem->current = l;
|
||||
mem->ptr = l->data;
|
||||
mem->n = block_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the next pointer will be aligned. */
|
||||
size += ALIGN(mem->ptr + size, long);
|
||||
|
||||
/* Allocate from current block. */
|
||||
ptr = mem->ptr;
|
||||
mem->ptr += size;
|
||||
mem->n -= size;
|
||||
|
||||
/* Set to zero if needed. */
|
||||
if (zero)
|
||||
memset(ptr, 0, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
tre-mem.h - TRE memory allocator interface
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TRE_MEM_H
|
||||
#define TRE_MEM_H 1
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TRE_MEM_BLOCK_SIZE 1024
|
||||
|
||||
typedef struct tre_list {
|
||||
void *data;
|
||||
struct tre_list *next;
|
||||
} tre_list_t;
|
||||
|
||||
typedef struct tre_mem_struct {
|
||||
tre_list_t *blocks;
|
||||
tre_list_t *current;
|
||||
char *ptr;
|
||||
size_t n;
|
||||
int failed;
|
||||
void **provided;
|
||||
} *tre_mem_t;
|
||||
|
||||
|
||||
tre_mem_t tre_mem_new_impl(int provided, void *provided_block);
|
||||
void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block,
|
||||
int zero, size_t size);
|
||||
|
||||
/* Returns a new memory allocator or NULL if out of memory. */
|
||||
#define tre_mem_new() tre_mem_new_impl(0, NULL)
|
||||
|
||||
/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
|
||||
allocated block or NULL if an underlying malloc() failed. */
|
||||
#define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size)
|
||||
|
||||
/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the
|
||||
allocated block or NULL if an underlying malloc() failed. The memory
|
||||
is set to zero. */
|
||||
#define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size)
|
||||
|
||||
#ifdef TRE_USE_ALLOCA
|
||||
/* alloca() versions. Like above, but memory is allocated with alloca()
|
||||
instead of malloc(). */
|
||||
|
||||
#define tre_mem_newa() \
|
||||
tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct)))
|
||||
|
||||
#define tre_mem_alloca(mem, size) \
|
||||
((mem)->n >= (size) \
|
||||
? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \
|
||||
: tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size)))
|
||||
#endif /* TRE_USE_ALLOCA */
|
||||
|
||||
|
||||
/* Frees the memory allocator and all memory allocated with it. */
|
||||
void tre_mem_destroy(tre_mem_t mem);
|
||||
|
||||
#endif /* TRE_MEM_H */
|
||||
|
||||
/* EOF */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
tre-parse.c - Regexp parser definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TRE_PARSE_H
|
||||
#define TRE_PARSE_H 1
|
||||
|
||||
/* Parse context. */
|
||||
typedef struct {
|
||||
/* Memory allocator. The AST is allocated using this. */
|
||||
tre_mem_t mem;
|
||||
/* Stack used for keeping track of regexp syntax. */
|
||||
tre_stack_t *stack;
|
||||
/* The parse result. */
|
||||
tre_ast_node_t *result;
|
||||
/* The regexp to parse and its length. */
|
||||
const tre_char_t *re;
|
||||
/* The first character of the entire regexp. */
|
||||
const tre_char_t *re_start;
|
||||
/* The first character after the end of the regexp. */
|
||||
const tre_char_t *re_end;
|
||||
size_t len;
|
||||
/* Current submatch ID. */
|
||||
int submatch_id;
|
||||
/* The highest back reference or -1 if none seen so far. */
|
||||
int max_backref;
|
||||
/* This flag is set if the regexp uses approximate matching. */
|
||||
int have_approx;
|
||||
/* Compilation flags. */
|
||||
int cflags;
|
||||
/* If this flag is set the top-level submatch is not captured. */
|
||||
int nofirstsub;
|
||||
/* The currently set approximate matching parameters. */
|
||||
int params[TRE_PARAM_LAST];
|
||||
/* the MB_CUR_MAX in use */
|
||||
int mb_cur_max;
|
||||
} tre_parse_ctx_t;
|
||||
|
||||
/* Parses a wide character regexp pattern into a syntax tree. This parser
|
||||
handles both syntaxes (BRE and ERE), including the TRE extensions. */
|
||||
reg_errcode_t
|
||||
tre_parse(tre_parse_ctx_t *ctx);
|
||||
|
||||
#endif /* TRE_PARSE_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
tre-stack.c - Simple stack implementation
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tre-internal.h"
|
||||
#include "tre-stack.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
union tre_stack_item {
|
||||
void *voidptr_value;
|
||||
int int_value;
|
||||
};
|
||||
|
||||
struct tre_stack_rec {
|
||||
size_t size;
|
||||
size_t max_size;
|
||||
size_t ptr;
|
||||
union tre_stack_item *stack;
|
||||
};
|
||||
|
||||
|
||||
tre_stack_t *
|
||||
tre_stack_new(size_t size, size_t max_size)
|
||||
{
|
||||
tre_stack_t *s;
|
||||
|
||||
s = xmalloc(sizeof(*s));
|
||||
if (s != NULL)
|
||||
{
|
||||
s->stack = xmalloc(sizeof(*s->stack) * size);
|
||||
if (s->stack == NULL)
|
||||
{
|
||||
xfree(s);
|
||||
return NULL;
|
||||
}
|
||||
s->size = size;
|
||||
s->max_size = max_size;
|
||||
s->ptr = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
tre_stack_destroy(tre_stack_t *s)
|
||||
{
|
||||
xfree(s->stack);
|
||||
xfree(s);
|
||||
}
|
||||
|
||||
size_t
|
||||
tre_stack_num_items(tre_stack_t *s)
|
||||
{
|
||||
return s->ptr;
|
||||
}
|
||||
|
||||
static reg_errcode_t
|
||||
tre_stack_push(tre_stack_t *s, union tre_stack_item value)
|
||||
{
|
||||
if (s->ptr < s->size)
|
||||
{
|
||||
s->stack[s->ptr] = value;
|
||||
s->ptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s->size >= s->max_size)
|
||||
{
|
||||
DPRINT(("tre_stack_push: stack full\n"));
|
||||
return REG_ESPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
union tre_stack_item *new_buffer;
|
||||
size_t new_size;
|
||||
DPRINT(("tre_stack_push: trying to realloc more space\n"));
|
||||
new_size = s->size + s->size;
|
||||
if (new_size > s->max_size)
|
||||
new_size = s->max_size;
|
||||
new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size);
|
||||
if (new_buffer == NULL)
|
||||
{
|
||||
DPRINT(("tre_stack_push: realloc failed.\n"));
|
||||
return REG_ESPACE;
|
||||
}
|
||||
DPRINT(("tre_stack_push: realloc succeeded.\n"));
|
||||
assert(new_size > s->size);
|
||||
s->size = new_size;
|
||||
s->stack = new_buffer;
|
||||
tre_stack_push(s, value);
|
||||
}
|
||||
}
|
||||
return REG_OK;
|
||||
}
|
||||
|
||||
#define define_pushf(typetag, type) \
|
||||
declare_pushf(typetag, type) { \
|
||||
union tre_stack_item item; \
|
||||
item.typetag ## _value = value; \
|
||||
return tre_stack_push(s, item); \
|
||||
}
|
||||
|
||||
define_pushf(int, int)
|
||||
define_pushf(voidptr, void *)
|
||||
|
||||
#define define_popf(typetag, type) \
|
||||
declare_popf(typetag, type) { \
|
||||
return s->stack[--s->ptr].typetag ## _value; \
|
||||
}
|
||||
|
||||
define_popf(int, int)
|
||||
define_popf(voidptr, void *)
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
tre-stack.h: Stack definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TRE_STACK_H
|
||||
#define TRE_STACK_H 1
|
||||
|
||||
#include "tre.h"
|
||||
|
||||
typedef struct tre_stack_rec tre_stack_t;
|
||||
|
||||
/* Creates a new stack object with initial size `size' and maximum size
|
||||
`max_size'. Pushing an additional item onto a full stack will resize
|
||||
the stack to double its capacity until the maximum is reached. Returns
|
||||
the stack object or NULL if out of memory. */
|
||||
tre_stack_t *
|
||||
tre_stack_new(size_t size, size_t max_size);
|
||||
|
||||
/* Frees the stack object. */
|
||||
void
|
||||
tre_stack_destroy(tre_stack_t *s);
|
||||
|
||||
/* Returns the current number of items on the stack. */
|
||||
size_t
|
||||
tre_stack_num_items(tre_stack_t *s);
|
||||
|
||||
/* Each tre_stack_push_*(tre_stack_t *s, <type> value) function pushes
|
||||
`value' on top of stack `s'. Returns REG_ESPACE if out of memory.
|
||||
This tries to realloc() more space before failing if maximum size
|
||||
has not yet been reached. Returns REG_OK if successful. */
|
||||
#define declare_pushf(typetag, type) \
|
||||
reg_errcode_t tre_stack_push_ ## typetag(tre_stack_t *s, type value)
|
||||
|
||||
declare_pushf(voidptr, void *);
|
||||
declare_pushf(int, int);
|
||||
|
||||
/* Each tre_stack_pop_*(tre_stack_t *s) function pops the topmost
|
||||
element off of stack `s' and returns it. The stack must not be
|
||||
empty. */
|
||||
#define declare_popf(typetag, type) \
|
||||
type tre_stack_pop_ ## typetag(tre_stack_t *s)
|
||||
|
||||
declare_popf(voidptr, void *);
|
||||
declare_popf(int, int);
|
||||
|
||||
/* Just to save some typing. */
|
||||
#define STACK_PUSH(s, typetag, value) \
|
||||
do \
|
||||
{ \
|
||||
status = tre_stack_push_ ## typetag(s, value); \
|
||||
} \
|
||||
while (/*CONSTCOND*/(void)0,0)
|
||||
|
||||
#define STACK_PUSHX(s, typetag, value) \
|
||||
{ \
|
||||
status = tre_stack_push_ ## typetag(s, value); \
|
||||
if (status != REG_OK) \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define STACK_PUSHR(s, typetag, value) \
|
||||
{ \
|
||||
reg_errcode_t _status; \
|
||||
_status = tre_stack_push_ ## typetag(s, value); \
|
||||
if (_status != REG_OK) \
|
||||
return _status; \
|
||||
}
|
||||
|
||||
#endif /* TRE_STACK_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
tre.h - TRE public API definitions
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TRE_H
|
||||
#define TRE_H 1
|
||||
|
||||
#ifdef USE_LOCAL_TRE_H
|
||||
/* Make certain to use the header(s) from the TRE package that this
|
||||
file is part of by giving the full path to the header from this directory. */
|
||||
#include "tre-config.h"
|
||||
#else
|
||||
/* Use the header in the same directory as this file if there is one. */
|
||||
#include "tre-config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif /* HAVE_SYS_TYPES_H */
|
||||
|
||||
#ifdef HAVE_LIBUTF8_H
|
||||
#include <libutf8.h>
|
||||
#endif /* HAVE_LIBUTF8_H */
|
||||
|
||||
#ifdef TRE_USE_SYSTEM_REGEX_H
|
||||
/* Include the system regex.h to make TRE ABI compatible with the
|
||||
system regex. */
|
||||
#include TRE_SYSTEM_REGEX_H_PATH
|
||||
#define tre_regcomp regcomp
|
||||
#define tre_regexec regexec
|
||||
#define tre_regerror regerror
|
||||
#define tre_regfree regfree
|
||||
/* The GNU C regex has a number of refinements to the POSIX standard for the
|
||||
formal parameter list of the regexec() function, and some of those fail to
|
||||
compile when using LLVM. The refinements seem to be opt-out rather than
|
||||
opt-in when using a recent gcc, and they produce a warning when TRE tries
|
||||
to mimic the API without the refinements. The TRE code still works but
|
||||
the warnings are distracting, so try to #define a flag to indicate when to
|
||||
add the refinements to TRE's parameter list too. */
|
||||
#ifdef __GNUC__
|
||||
/* Try to test something that looks pretty REGEX specific and hope we don't
|
||||
need a zillion different platform+compiler specific tests to deal with this. */
|
||||
#ifdef _REGEX_NELTS
|
||||
/* Define a TRE specific flag here so that:
|
||||
1) there is only one place where code has to be changed if the test above is not adequate, and
|
||||
2) the flag can be used in any other parts of the TRE source that might be affected by the
|
||||
GNUC refinements.
|
||||
Note that this flag is only defined when all of TRE_USE_SYSTEM_REGEX_H, __GNUC__, and _REGEX_NELTS are defined. */
|
||||
#define TRE_USE_GNUC_REGEXEC_FPL 1
|
||||
#endif
|
||||
#endif
|
||||
#endif /* TRE_USE_SYSTEM_REGEX_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef TRE_USE_SYSTEM_REGEX_H
|
||||
|
||||
#ifndef REG_OK
|
||||
#define REG_OK 0
|
||||
#endif /* !REG_OK */
|
||||
|
||||
#ifndef HAVE_REG_ERRCODE_T
|
||||
typedef int reg_errcode_t;
|
||||
#endif /* !HAVE_REG_ERRCODE_T */
|
||||
|
||||
#if !defined(REG_NOSPEC) && !defined(REG_LITERAL)
|
||||
#define REG_LITERAL 0x1000
|
||||
#endif
|
||||
|
||||
/* Extra tre_regcomp() return error codes. */
|
||||
#define REG_BADMAX REG_BADBR
|
||||
|
||||
/* Extra tre_regcomp() flags. */
|
||||
#ifndef REG_BASIC
|
||||
#define REG_BASIC 0
|
||||
#endif /* !REG_BASIC */
|
||||
#define REG_RIGHT_ASSOC (REG_LITERAL << 1)
|
||||
#ifdef REG_UNGREEDY
|
||||
/* We're going to use TRE code, so we need the TRE define (dodge problem in MacOS). */
|
||||
#undef REG_UNGREEDY
|
||||
#endif
|
||||
#define REG_UNGREEDY (REG_RIGHT_ASSOC << 1)
|
||||
|
||||
#define REG_USEBYTES (REG_UNGREEDY << 1)
|
||||
|
||||
/* Extra tre_regexec() flags. */
|
||||
#define REG_APPROX_MATCHER 0x1000
|
||||
#ifdef REG_BACKTRACKING_MATCHER
|
||||
/* We're going to use TRE code, so we need the TRE define (dodge problem in MacOS). */
|
||||
#undef REG_BACKTRACKING_MATCHER
|
||||
#endif
|
||||
#define REG_BACKTRACKING_MATCHER (REG_APPROX_MATCHER << 1)
|
||||
|
||||
#else /* !TRE_USE_SYSTEM_REGEX_H */
|
||||
|
||||
/* If the we're not using system regex.h, we need to define the
|
||||
structs and enums ourselves. */
|
||||
|
||||
typedef int regoff_t;
|
||||
typedef struct {
|
||||
size_t re_nsub; /* Number of parenthesized subexpressions. */
|
||||
void *value; /* For internal use only. */
|
||||
} regex_t;
|
||||
|
||||
typedef struct {
|
||||
regoff_t rm_so;
|
||||
regoff_t rm_eo;
|
||||
} regmatch_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
REG_OK = 0, /* No error. */
|
||||
/* POSIX tre_regcomp() return error codes. (In the order listed in the
|
||||
standard.) */
|
||||
REG_NOMATCH, /* No match. */
|
||||
REG_BADPAT, /* Invalid regexp. */
|
||||
REG_ECOLLATE, /* Unknown collating element. */
|
||||
REG_ECTYPE, /* Unknown character class name. */
|
||||
REG_EESCAPE, /* Trailing backslash. */
|
||||
REG_ESUBREG, /* Invalid back reference. */
|
||||
REG_EBRACK, /* "[]" imbalance */
|
||||
REG_EPAREN, /* "\(\)" or "()" imbalance */
|
||||
REG_EBRACE, /* "\{\}" or "{}" imbalance */
|
||||
REG_BADBR, /* Invalid content of {} */
|
||||
REG_ERANGE, /* Invalid use of range operator */
|
||||
REG_ESPACE, /* Out of memory. */
|
||||
REG_BADRPT, /* Invalid use of repetition operators. */
|
||||
REG_BADMAX, /* Maximum repetition in {} too large */
|
||||
} reg_errcode_t;
|
||||
|
||||
/* POSIX tre_regcomp() flags. */
|
||||
#define REG_EXTENDED 1
|
||||
#define REG_ICASE (REG_EXTENDED << 1)
|
||||
#define REG_NEWLINE (REG_ICASE << 1)
|
||||
#define REG_NOSUB (REG_NEWLINE << 1)
|
||||
|
||||
/* Extra tre_regcomp() flags. */
|
||||
#define REG_BASIC 0
|
||||
#define REG_LITERAL (REG_NOSUB << 1)
|
||||
#define REG_RIGHT_ASSOC (REG_LITERAL << 1)
|
||||
#define REG_UNGREEDY (REG_RIGHT_ASSOC << 1)
|
||||
#define REG_USEBYTES (REG_UNGREEDY << 1)
|
||||
|
||||
/* POSIX tre_regexec() flags. */
|
||||
#define REG_NOTBOL 1
|
||||
#define REG_NOTEOL (REG_NOTBOL << 1)
|
||||
|
||||
/* Extra tre_regexec() flags. */
|
||||
#define REG_APPROX_MATCHER (REG_NOTEOL << 1)
|
||||
#define REG_BACKTRACKING_MATCHER (REG_APPROX_MATCHER << 1)
|
||||
|
||||
#endif /* !TRE_USE_SYSTEM_REGEX_H */
|
||||
|
||||
/* REG_NOSPEC and REG_LITERAL mean the same thing. */
|
||||
#if defined(REG_LITERAL) && !defined(REG_NOSPEC)
|
||||
#define REG_NOSPEC REG_LITERAL
|
||||
#elif defined(REG_NOSPEC) && !defined(REG_LITERAL)
|
||||
#define REG_LITERAL REG_NOSPEC
|
||||
#endif /* defined(REG_NOSPEC) */
|
||||
|
||||
/* The maximum number of iterations in a bound expression. */
|
||||
#undef RE_DUP_MAX
|
||||
#define RE_DUP_MAX 255
|
||||
|
||||
/* The POSIX.2 regexp functions */
|
||||
extern int
|
||||
tre_regcomp(regex_t *preg, const char *regex, int cflags);
|
||||
|
||||
#ifdef TRE_USE_GNUC_REGEXEC_FPL
|
||||
extern int
|
||||
tre_regexec(const regex_t *preg, const char *string,
|
||||
size_t nmatch, regmatch_t pmatch[_Restrict_arr_ _REGEX_NELTS (nmatch)],
|
||||
int eflags);
|
||||
#else
|
||||
extern int
|
||||
tre_regexec(const regex_t *preg, const char *string, size_t nmatch,
|
||||
regmatch_t pmatch[], int eflags);
|
||||
#endif
|
||||
|
||||
extern int
|
||||
tre_regcompb(regex_t *preg, const char *regex, int cflags);
|
||||
|
||||
extern int
|
||||
tre_regexecb(const regex_t *preg, const char *string, size_t nmatch,
|
||||
regmatch_t pmatch[], int eflags);
|
||||
|
||||
extern size_t
|
||||
tre_regerror(int errcode, const regex_t *preg, char *errbuf,
|
||||
size_t errbuf_size);
|
||||
|
||||
extern void
|
||||
tre_regfree(regex_t *preg);
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
#ifdef HAVE_WCHAR_H
|
||||
#include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H */
|
||||
|
||||
/* Wide character versions (not in POSIX.2). */
|
||||
extern int
|
||||
tre_regwcomp(regex_t *preg, const wchar_t *regex, int cflags);
|
||||
|
||||
extern int
|
||||
tre_regwexec(const regex_t *preg, const wchar_t *string,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags);
|
||||
#endif /* TRE_WCHAR */
|
||||
|
||||
/* Versions with a maximum length argument and therefore the capability to
|
||||
handle null characters in the middle of the strings (not in POSIX.2). */
|
||||
extern int
|
||||
tre_regncomp(regex_t *preg, const char *regex, size_t len, int cflags);
|
||||
|
||||
extern int
|
||||
tre_regnexec(const regex_t *preg, const char *string, size_t len,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags);
|
||||
|
||||
/* regn*b versions take byte literally as 8-bit values */
|
||||
extern int
|
||||
tre_regncompb(regex_t *preg, const char *regex, size_t n, int cflags);
|
||||
|
||||
extern int
|
||||
tre_regnexecb(const regex_t *preg, const char *str, size_t len,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags);
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
extern int
|
||||
tre_regwncomp(regex_t *preg, const wchar_t *regex, size_t len, int cflags);
|
||||
|
||||
extern int
|
||||
tre_regwnexec(const regex_t *preg, const wchar_t *string, size_t len,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags);
|
||||
#endif /* TRE_WCHAR */
|
||||
|
||||
#ifdef TRE_APPROX
|
||||
|
||||
/* Approximate matching parameter struct. */
|
||||
typedef struct {
|
||||
int cost_ins; /* Default cost of an inserted character. */
|
||||
int cost_del; /* Default cost of a deleted character. */
|
||||
int cost_subst; /* Default cost of a substituted character. */
|
||||
int max_cost; /* Maximum allowed cost of a match. */
|
||||
|
||||
int max_ins; /* Maximum allowed number of inserts. */
|
||||
int max_del; /* Maximum allowed number of deletes. */
|
||||
int max_subst; /* Maximum allowed number of substitutes. */
|
||||
int max_err; /* Maximum allowed number of errors total. */
|
||||
} regaparams_t;
|
||||
|
||||
/* Approximate matching result struct. */
|
||||
typedef struct {
|
||||
size_t nmatch; /* Length of pmatch[] array. */
|
||||
regmatch_t *pmatch; /* Submatch data. */
|
||||
int cost; /* Cost of the match. */
|
||||
int num_ins; /* Number of inserts in the match. */
|
||||
int num_del; /* Number of deletes in the match. */
|
||||
int num_subst; /* Number of substitutes in the match. */
|
||||
} regamatch_t;
|
||||
|
||||
|
||||
/* Approximate matching functions. */
|
||||
extern int
|
||||
tre_regaexec(const regex_t *preg, const char *string,
|
||||
regamatch_t *match, regaparams_t params, int eflags);
|
||||
|
||||
extern int
|
||||
tre_reganexec(const regex_t *preg, const char *string, size_t len,
|
||||
regamatch_t *match, regaparams_t params, int eflags);
|
||||
|
||||
extern int
|
||||
tre_regaexecb(const regex_t *preg, const char *string,
|
||||
regamatch_t *match, regaparams_t params, int eflags);
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
/* Wide character approximate matching. */
|
||||
extern int
|
||||
tre_regawexec(const regex_t *preg, const wchar_t *string,
|
||||
regamatch_t *match, regaparams_t params, int eflags);
|
||||
|
||||
extern int
|
||||
tre_regawnexec(const regex_t *preg, const wchar_t *string, size_t len,
|
||||
regamatch_t *match, regaparams_t params, int eflags);
|
||||
#endif /* TRE_WCHAR */
|
||||
|
||||
/* Sets the parameters to default values. */
|
||||
extern void
|
||||
tre_regaparams_default(regaparams_t *params);
|
||||
#endif /* TRE_APPROX */
|
||||
|
||||
#ifdef TRE_WCHAR
|
||||
typedef wchar_t tre_char_t;
|
||||
#else /* !TRE_WCHAR */
|
||||
typedef unsigned char tre_char_t;
|
||||
#endif /* !TRE_WCHAR */
|
||||
|
||||
typedef struct {
|
||||
int (*get_next_char)(tre_char_t *c, unsigned int *pos_add, void *context);
|
||||
void (*rewind)(size_t pos, void *context);
|
||||
int (*compare)(size_t pos1, size_t pos2, size_t len, void *context);
|
||||
void *context;
|
||||
} tre_str_source;
|
||||
|
||||
extern int
|
||||
tre_reguexec(const regex_t *preg, const tre_str_source *string,
|
||||
size_t nmatch, regmatch_t pmatch[], int eflags);
|
||||
|
||||
/* Returns the version string. The returned string is static. */
|
||||
extern char *
|
||||
tre_version(void);
|
||||
|
||||
/* Returns the value for a config parameter. The type to which `result'
|
||||
must point to depends of the value of `query', see documentation for
|
||||
more details. */
|
||||
extern int
|
||||
tre_config(int query, void *result);
|
||||
|
||||
enum {
|
||||
TRE_CONFIG_APPROX,
|
||||
TRE_CONFIG_WCHAR,
|
||||
TRE_CONFIG_MULTIBYTE,
|
||||
TRE_CONFIG_SYSTEM_ABI,
|
||||
TRE_CONFIG_VERSION
|
||||
};
|
||||
|
||||
/* Returns 1 if the compiled pattern has back references, 0 if not. */
|
||||
extern int
|
||||
tre_have_backrefs(const regex_t *preg);
|
||||
|
||||
/* Returns 1 if the compiled pattern uses approximate matching features,
|
||||
0 if not. */
|
||||
extern int
|
||||
tre_have_approx(const regex_t *preg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* TRE_H */
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* tre_all.c — Single compilation unit for vendored TRE regex library.
|
||||
* Only compiled on Windows (POSIX systems use system <regex.h>).
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "tre-config.h"
|
||||
|
||||
/* Core library sources */
|
||||
#include "tre-ast.c"
|
||||
#include "tre-compile.c"
|
||||
#include "tre-mem.c"
|
||||
#include "tre-parse.c"
|
||||
#include "tre-stack.c"
|
||||
|
||||
/* Matchers (tre-match-utils.h has include guard to handle being
|
||||
* included by both matcher .c files in same translation unit) */
|
||||
#include "tre-match-parallel.c"
|
||||
#include "tre-match-backtrack.c"
|
||||
|
||||
/* Approximate matching + filter */
|
||||
#include "tre-match-approx.c"
|
||||
#include "tre-filter.c"
|
||||
|
||||
/* POSIX API */
|
||||
#include "regcomp.c"
|
||||
#include "regexec.c"
|
||||
#include "regerror.c"
|
||||
|
||||
#endif /* _WIN32 */
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
xmalloc.c - Simple malloc debugging library implementation
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- red zones
|
||||
- group dumps by source location
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#define XMALLOC_INTERNAL 1
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
||||
/*
|
||||
Internal stuff.
|
||||
*/
|
||||
|
||||
typedef struct hashTableItemRec {
|
||||
void *ptr;
|
||||
size_t bytes;
|
||||
const char *file;
|
||||
int line;
|
||||
const char *func;
|
||||
struct hashTableItemRec *next;
|
||||
} hashTableItem;
|
||||
|
||||
typedef struct {
|
||||
hashTableItem **table;
|
||||
} hashTable;
|
||||
|
||||
static int xmalloc_peak;
|
||||
int xmalloc_current;
|
||||
static int xmalloc_peak_blocks;
|
||||
int xmalloc_current_blocks;
|
||||
static int xmalloc_fail_after;
|
||||
|
||||
#define TABLE_BITS 8
|
||||
#define TABLE_MASK ((1 << TABLE_BITS) - 1)
|
||||
#define TABLE_SIZE (1 << TABLE_BITS)
|
||||
|
||||
static hashTable *
|
||||
hash_table_new(void)
|
||||
{
|
||||
hashTable *tbl;
|
||||
|
||||
tbl = malloc(sizeof(*tbl));
|
||||
|
||||
if (tbl != NULL)
|
||||
{
|
||||
tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table));
|
||||
|
||||
if (tbl->table == NULL)
|
||||
{
|
||||
free(tbl);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
hash_void_ptr(void *ptr)
|
||||
{
|
||||
unsigned int hash;
|
||||
unsigned int i;
|
||||
|
||||
/* I took this hash function just off the top of my head, I have
|
||||
no idea whether it is bad or very bad. */
|
||||
hash = 0;
|
||||
for (i = 0; i < sizeof(ptr) * 8 / TABLE_BITS; i++)
|
||||
{
|
||||
hash ^= (uintptr_t)ptr >> i * 8;
|
||||
hash += i * 17;
|
||||
hash &= TABLE_MASK;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void
|
||||
hash_table_add(hashTable *tbl, void *ptr, size_t bytes,
|
||||
const char *file, int line, const char *func)
|
||||
{
|
||||
unsigned int i;
|
||||
hashTableItem *item, *new;
|
||||
|
||||
i = hash_void_ptr(ptr);
|
||||
|
||||
item = tbl->table[i];
|
||||
if (item != NULL)
|
||||
while (item->next != NULL)
|
||||
item = item->next;
|
||||
|
||||
new = malloc(sizeof(*new));
|
||||
assert(new != NULL);
|
||||
new->ptr = ptr;
|
||||
new->bytes = bytes;
|
||||
new->file = file;
|
||||
new->line = line;
|
||||
new->func = func;
|
||||
new->next = NULL;
|
||||
if (item != NULL)
|
||||
item->next = new;
|
||||
else
|
||||
tbl->table[i] = new;
|
||||
|
||||
xmalloc_current += bytes;
|
||||
if (xmalloc_current > xmalloc_peak)
|
||||
xmalloc_peak = xmalloc_current;
|
||||
xmalloc_current_blocks++;
|
||||
if (xmalloc_current_blocks > xmalloc_peak_blocks)
|
||||
xmalloc_peak_blocks = xmalloc_current_blocks;
|
||||
}
|
||||
|
||||
static void
|
||||
#if defined(__GNUC__) && __GNUC__ >= 10
|
||||
__attribute__((access(none, 2)))
|
||||
#endif
|
||||
hash_table_del(hashTable *tbl, void *ptr)
|
||||
{
|
||||
int i;
|
||||
hashTableItem *item, *prev;
|
||||
|
||||
i = hash_void_ptr(ptr);
|
||||
|
||||
item = tbl->table[i];
|
||||
if (item == NULL)
|
||||
{
|
||||
printf("xfree: invalid ptr %p\n", ptr);
|
||||
abort();
|
||||
}
|
||||
prev = NULL;
|
||||
while (item->ptr != ptr)
|
||||
{
|
||||
prev = item;
|
||||
item = item->next;
|
||||
}
|
||||
if (item->ptr != ptr)
|
||||
{
|
||||
printf("xfree: invalid ptr %p\n", ptr);
|
||||
abort();
|
||||
}
|
||||
|
||||
xmalloc_current -= item->bytes;
|
||||
xmalloc_current_blocks--;
|
||||
|
||||
if (prev != NULL)
|
||||
{
|
||||
prev->next = item->next;
|
||||
free(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
tbl->table[i] = item->next;
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
|
||||
static hashTable *xmalloc_table = NULL;
|
||||
|
||||
static void
|
||||
xmalloc_init(void)
|
||||
{
|
||||
if (xmalloc_table == NULL)
|
||||
{
|
||||
xmalloc_table = hash_table_new();
|
||||
xmalloc_peak = 0;
|
||||
xmalloc_peak_blocks = 0;
|
||||
xmalloc_current = 0;
|
||||
xmalloc_current_blocks = 0;
|
||||
xmalloc_fail_after = -1;
|
||||
}
|
||||
assert(xmalloc_table != NULL);
|
||||
assert(xmalloc_table->table != NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public API.
|
||||
*/
|
||||
|
||||
void
|
||||
xmalloc_configure(int fail_after)
|
||||
{
|
||||
xmalloc_init();
|
||||
xmalloc_fail_after = fail_after;
|
||||
}
|
||||
|
||||
int
|
||||
xmalloc_dump_leaks(void)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int num_leaks = 0;
|
||||
size_t leaked_bytes = 0;
|
||||
hashTableItem *item;
|
||||
|
||||
xmalloc_init();
|
||||
|
||||
for (i = 0; i < TABLE_SIZE; i++)
|
||||
{
|
||||
item = xmalloc_table->table[i];
|
||||
while (item != NULL)
|
||||
{
|
||||
printf("%s:%d: %s: %zu bytes at %p not freed\n",
|
||||
item->file, item->line, item->func, item->bytes, item->ptr);
|
||||
num_leaks++;
|
||||
leaked_bytes += item->bytes;
|
||||
item = item->next;
|
||||
}
|
||||
}
|
||||
if (num_leaks == 0)
|
||||
printf("No memory leaks.\n");
|
||||
else
|
||||
printf("%u unfreed memory chuncks, total %zu unfreed bytes.\n",
|
||||
num_leaks, leaked_bytes);
|
||||
printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ",
|
||||
xmalloc_peak, (double)xmalloc_peak / 1024,
|
||||
(double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks);
|
||||
printf("(average ");
|
||||
if (xmalloc_peak_blocks)
|
||||
printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2)
|
||||
/ xmalloc_peak_blocks));
|
||||
else
|
||||
printf("N/A");
|
||||
printf(" bytes per block).\n");
|
||||
|
||||
return num_leaks;
|
||||
}
|
||||
|
||||
void *
|
||||
xmalloc_impl(size_t size, const char *file, int line, const char *func)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
xmalloc_init();
|
||||
assert(size > 0);
|
||||
|
||||
if (xmalloc_fail_after == 0)
|
||||
{
|
||||
xmalloc_fail_after = -2;
|
||||
#if 0
|
||||
printf("xmalloc: forced failure %s:%d: %s\n", file, line, func);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
else if (xmalloc_fail_after == -2)
|
||||
{
|
||||
printf("xmalloc: called after failure from %s:%d: %s\n",
|
||||
file, line, func);
|
||||
assert(0);
|
||||
}
|
||||
else if (xmalloc_fail_after > 0)
|
||||
xmalloc_fail_after--;
|
||||
|
||||
ptr = malloc(size);
|
||||
if (ptr != NULL)
|
||||
hash_table_add(xmalloc_table, ptr, (int)size, file, line, func);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *
|
||||
xcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
|
||||
const char *func)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
xmalloc_init();
|
||||
assert(size > 0);
|
||||
|
||||
if (xmalloc_fail_after == 0)
|
||||
{
|
||||
xmalloc_fail_after = -2;
|
||||
#if 0
|
||||
printf("xcalloc: forced failure %s:%d: %s\n", file, line, func);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
else if (xmalloc_fail_after == -2)
|
||||
{
|
||||
printf("xcalloc: called after failure from %s:%d: %s\n",
|
||||
file, line, func);
|
||||
assert(0);
|
||||
}
|
||||
else if (xmalloc_fail_after > 0)
|
||||
xmalloc_fail_after--;
|
||||
|
||||
ptr = calloc(nmemb, size);
|
||||
if (ptr != NULL)
|
||||
hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
xfree_impl(void *ptr, const char *file, int line, const char *func)
|
||||
{
|
||||
/*LINTED*/(void)&file;
|
||||
/*LINTED*/(void)&line;
|
||||
/*LINTED*/(void)&func;
|
||||
xmalloc_init();
|
||||
|
||||
if (ptr != NULL)
|
||||
hash_table_del(xmalloc_table, ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void *
|
||||
xrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
|
||||
const char *func)
|
||||
{
|
||||
void *new_ptr;
|
||||
|
||||
xmalloc_init();
|
||||
assert(ptr != NULL);
|
||||
assert(new_size > 0);
|
||||
|
||||
if (xmalloc_fail_after == 0)
|
||||
{
|
||||
xmalloc_fail_after = -2;
|
||||
return NULL;
|
||||
}
|
||||
else if (xmalloc_fail_after == -2)
|
||||
{
|
||||
printf("xrealloc: called after failure from %s:%d: %s\n",
|
||||
file, line, func);
|
||||
assert(0);
|
||||
}
|
||||
else if (xmalloc_fail_after > 0)
|
||||
xmalloc_fail_after--;
|
||||
|
||||
new_ptr = realloc(ptr, new_size);
|
||||
if (new_ptr != NULL && new_ptr != ptr)
|
||||
{
|
||||
hash_table_del(xmalloc_table, ptr);
|
||||
hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* EOF */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
xmalloc.h - Simple malloc debugging library API
|
||||
|
||||
This software is released under a BSD-style license.
|
||||
See the file LICENSE for details and copyright.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _XMALLOC_H
|
||||
#define _XMALLOC_H 1
|
||||
|
||||
void *xmalloc_impl(size_t size, const char *file, int line, const char *func);
|
||||
void *xcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
|
||||
const char *func);
|
||||
void xfree_impl(void *ptr, const char *file, int line, const char *func);
|
||||
void *xrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
|
||||
const char *func);
|
||||
int xmalloc_dump_leaks(void);
|
||||
void xmalloc_configure(int fail_after);
|
||||
|
||||
|
||||
#ifndef XMALLOC_INTERNAL
|
||||
#ifdef MALLOC_DEBUGGING
|
||||
|
||||
/* Version 2.4 and later of GCC define a magical variable `__PRETTY_FUNCTION__'
|
||||
which contains the name of the function currently being defined.
|
||||
# define __XMALLOC_FUNCTION __PRETTY_FUNCTION__
|
||||
This is broken in G++ before version 2.6.
|
||||
C9x has a similar variable called __func__, but prefer the GCC one since
|
||||
it demangles C++ function names. */
|
||||
# ifdef __GNUC__
|
||||
# if __GNUC__ > 2 || (__GNUC__ == 2 \
|
||||
&& __GNUC_MINOR__ >= (defined __cplusplus ? 6 : 4))
|
||||
# define __XMALLOC_FUNCTION __PRETTY_FUNCTION__
|
||||
# else
|
||||
# define __XMALLOC_FUNCTION ((const char *) 0)
|
||||
# endif
|
||||
# else
|
||||
# if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
|
||||
# define __XMALLOC_FUNCTION __func__
|
||||
# else
|
||||
# define __XMALLOC_FUNCTION ((const char *) 0)
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#define xmalloc(size) xmalloc_impl(size, __FILE__, __LINE__, \
|
||||
__XMALLOC_FUNCTION)
|
||||
#define xcalloc(nmemb, size) xcalloc_impl(nmemb, size, __FILE__, __LINE__, \
|
||||
__XMALLOC_FUNCTION)
|
||||
#define xfree(ptr) xfree_impl(ptr, __FILE__, __LINE__, __XMALLOC_FUNCTION)
|
||||
#define xrealloc(ptr, new_size) xrealloc_impl(ptr, new_size, __FILE__, \
|
||||
__LINE__, __XMALLOC_FUNCTION)
|
||||
#undef malloc
|
||||
#undef calloc
|
||||
#undef free
|
||||
#undef realloc
|
||||
|
||||
#define malloc USE_XMALLOC_INSTEAD_OF_MALLOC
|
||||
#define calloc USE_XCALLOC_INSTEAD_OF_CALLOC
|
||||
#define free USE_XFREE_INSTEAD_OF_FREE
|
||||
#define realloc USE_XREALLOC_INSTEAD_OF_REALLOC
|
||||
|
||||
#else /* !MALLOC_DEBUGGING */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define xmalloc(size) malloc(size)
|
||||
#define xcalloc(nmemb, size) calloc(nmemb, size)
|
||||
#define xfree(ptr) free(ptr)
|
||||
#define xrealloc(ptr, new_size) realloc(ptr, new_size)
|
||||
|
||||
#endif /* !MALLOC_DEBUGGING */
|
||||
#endif /* !XMALLOC_INTERNAL */
|
||||
|
||||
#endif /* _XMALLOC_H */
|
||||
|
||||
/* EOF */
|
||||
Reference in New Issue
Block a user