chore: import upstream snapshot with attribution
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:28:05 +08:00
commit 41cb1c0170
1830 changed files with 38276124 additions and 0 deletions
+29
View File
@@ -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.
+188
View File
@@ -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 */
+86
View File
@@ -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 */
+48
View File
@@ -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 */
+387
View File
@@ -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(&params);
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 */
+226
View File
@@ -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 */
+128
View File
@@ -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
+27
View File
@@ -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 */
+18
View File
@@ -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
+73
View File
@@ -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;
}
+19
View File
@@ -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);
+306
View File
@@ -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 */
+819
View File
@@ -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;
}
+676
View File
@@ -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;
}
+511
View File
@@ -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 */
+219
View File
@@ -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 */
+155
View File
@@ -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 */
+66
View File
@@ -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
+50
View File
@@ -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 */
+123
View File
@@ -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 */
+76
View File
@@ -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 */
+344
View File
@@ -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 */
+30
View File
@@ -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 */
+355
View File
@@ -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 */
+77
View File
@@ -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 */